From 95c0c937b1bfb4502ea90fcd471b9bf6a37257d9 Mon Sep 17 00:00:00 2001 From: Julie Turner Date: Tue, 1 Oct 2024 13:55:22 +0000 Subject: [PATCH 1/3] Added deprecation styling to older docs --- docs/v1/documentation/css/extra.css | 41 ++++++++++++++++++++++++----- docs/v2/css/extra.css | 29 ++++++++++++++++++++ docs/v3/css/extra.css | 29 ++++++++++++++++++++ 3 files changed, 93 insertions(+), 6 deletions(-) diff --git a/docs/v1/documentation/css/extra.css b/docs/v1/documentation/css/extra.css index 6391b1ff7..4d693ca20 100644 --- a/docs/v1/documentation/css/extra.css +++ b/docs/v1/documentation/css/extra.css @@ -4,15 +4,15 @@ padding: 0 0.25 0.5 !important; } -.md-header{ +.md-header { height: 75px; } -.md-container{ +.md-container { padding-top: 70px; } -.md-sidebar[data-md-state="lock"]{ +.md-sidebar[data-md-state="lock"] { padding-top: 75px; } @@ -27,7 +27,36 @@ } @media only screen and (max-width: 76.1875em) { - .md-nav--primary .md-nav__title--site .md-nav__button { - width: 150px; - } + .md-nav--primary .md-nav__title--site .md-nav__button { + width: 150px; + } +} + +body { + min-height: 100vh; + min-width: 100vw; + margin: 0; + background-color: light-dark(whitesmoke, black); + + &:before { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + content: "V1 \A Deprecated"; + white-space: pre-wrap; + display: block; + box-sizing: border-box; + padding: 20vh; + font-size: clamp(4rem, 20vh, 10rem); + height: 100vh; + width: 100vw; + text-align: center; + z-index: -1; + color: silver; + font-weight: 900; + font-family: sans-serif; + text-transform: uppercase; + } } \ No newline at end of file diff --git a/docs/v2/css/extra.css b/docs/v2/css/extra.css index 6391b1ff7..81c8ef41b 100644 --- a/docs/v2/css/extra.css +++ b/docs/v2/css/extra.css @@ -30,4 +30,33 @@ .md-nav--primary .md-nav__title--site .md-nav__button { width: 150px; } +} + +body { + min-height: 100vh; + min-width: 100vw; + margin: 0; + background-color: light-dark(whitesmoke, black); + + &:before { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + content: "V2 \A Deprecated"; + white-space: pre-wrap; + display: block; + box-sizing: border-box; + padding: 20vh; + font-size: clamp(4rem, 20vh, 10rem); + height: 100vh; + width: 100vw; + text-align: center; + z-index: -1; + color: silver; + font-weight: 900; + font-family: sans-serif; + text-transform: uppercase; + } } \ No newline at end of file diff --git a/docs/v3/css/extra.css b/docs/v3/css/extra.css index 6391b1ff7..0193615dd 100644 --- a/docs/v3/css/extra.css +++ b/docs/v3/css/extra.css @@ -30,4 +30,33 @@ .md-nav--primary .md-nav__title--site .md-nav__button { width: 150px; } +} + +body { + min-height: 100vh; + min-width: 100vw; + margin: 0; + background-color: light-dark(whitesmoke, black); + + &:before { + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + content: "V3 \A Deprecated"; + white-space: pre-wrap; + display: block; + box-sizing: border-box; + padding: 20vh; + font-size: clamp(4rem, 20vh, 10rem); + height: 100vh; + width: 100vw; + text-align: center; + z-index: -1; + color: silver; + font-weight: 900; + font-family: sans-serif; + text-transform: uppercase; + } } \ No newline at end of file From ae8fc470c5fbfcbcf44001bf50e4eb5f426c06a0 Mon Sep 17 00:00:00 2001 From: Julie Turner Date: Tue, 1 Oct 2024 14:02:29 +0000 Subject: [PATCH 2/3] Remove copy of legacy docs in v3 docs subfolder --- docs/v3/v1/404.html | 1611 ------- docs/v3/v1/assets/fonts/font-awesome.css | 4 - docs/v3/v1/assets/fonts/material-icons.css | 13 - .../v1/assets/fonts/specimen/FontAwesome.ttf | Bin 165548 -> 0 bytes .../v1/assets/fonts/specimen/FontAwesome.woff | Bin 98024 -> 0 bytes .../assets/fonts/specimen/FontAwesome.woff2 | Bin 77160 -> 0 bytes .../fonts/specimen/MaterialIcons-Regular.ttf | Bin 128180 -> 0 bytes .../fonts/specimen/MaterialIcons-Regular.woff | Bin 57620 -> 0 bytes .../specimen/MaterialIcons-Regular.woff2 | Bin 44300 -> 0 bytes docs/v3/v1/assets/images/favicon.png | Bin 521 -> 0 bytes .../images/icons/bitbucket.1b09e088.svg | 20 - .../assets/images/icons/github.f0b8504a.svg | 18 - .../assets/images/icons/gitlab.6dd19c00.svg | 38 - .../javascripts/application.583bbe55.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.da.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.de.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.du.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.es.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.fi.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.fr.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.hu.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.it.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.jp.js | 1 - .../v1/assets/javascripts/lunr/lunr.multi.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.no.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.pt.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.ro.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.ru.js | 1 - .../javascripts/lunr/lunr.stemmer.support.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.sv.js | 1 - docs/v3/v1/assets/javascripts/lunr/lunr.tr.js | 1 - docs/v3/v1/assets/javascripts/lunr/tinyseg.js | 1 - .../assets/javascripts/modernizr.1aa3b519.js | 1 - .../application-palette.22915126.css | 1176 ----- .../stylesheets/application.451f80e5.css | 2552 ---------- docs/v3/v1/common/docs/adalclient/index.html | 1994 -------- docs/v3/v1/common/docs/collections/index.html | 1786 ------- .../docs/custom-httpclientimpl/index.html | 1798 ------- docs/v3/v1/common/docs/index.html | 1787 ------- docs/v3/v1/common/docs/libconfig/index.html | 1930 -------- docs/v3/v1/common/docs/netutil/index.html | 1860 -------- docs/v3/v1/common/docs/storage/index.html | 1850 -------- docs/v3/v1/common/docs/util/index.html | 2058 -------- .../docs/configuration/index.html | 1730 ------- docs/v3/v1/config-store/docs/index.html | 1761 ------- .../v1/config-store/docs/providers/index.html | 1782 ------- .../SPFx-On-Premesis-2016/index.html | 1720 ------- .../v1/documentation/beta-versions/index.html | 1742 ------- docs/v3/v1/documentation/css/extra.css | 33 - docs/v3/v1/documentation/debugging/index.html | 2098 --------- .../v3/v1/documentation/deployment/index.html | 1972 -------- .../v1/documentation/documentation/index.html | 1760 ------- .../getting-started-dev/index.html | 1799 ------- .../documentation/getting-started/index.html | 2280 --------- .../v1/documentation/gulp-commands/index.html | 2100 --------- docs/v3/v1/documentation/img/Logo.png | Bin 3151 -> 0 bytes .../v1/documentation/img/PnPJS_FluentAPI.gif | Bin 707717 -> 0 bytes .../img/SPFx-On-Premesis-2016-1.png | Bin 46965 -> 0 bytes .../img/office365-header-icon.png | Bin 312 -> 0 bytes .../v1/documentation/img/pnpjs-common-uml.svg | 220 - .../img/pnpjs-config-store-uml.svg | 56 - .../v1/documentation/img/pnpjs-graph-uml.svg | 602 --- .../documentation/img/pnpjs-logging-uml.svg | 59 - .../v1/documentation/img/pnpjs-nodejs-uml.svg | 85 - .../v1/documentation/img/pnpjs-odata-uml.svg | 253 - .../img/pnpjs-sp-addinhelpers-uml.svg | 48 - .../img/pnpjs-sp-clientsvc-uml.svg | 167 - .../img/pnpjs-sp-taxonomy-uml.svg | 458 -- docs/v3/v1/documentation/img/pnpjs-sp-uml.svg | 2833 ----------- .../package-structure/index.html | 1875 -------- docs/v3/v1/documentation/packages/index.html | 1741 ------- docs/v3/v1/documentation/polyfill/index.html | 1877 -------- docs/v3/v1/documentation/theme/main.html | 5 - .../documentation/transition-guide/index.html | 1977 -------- docs/v3/v1/graph/docs/contacts/index.html | 2080 -------- .../v1/graph/docs/directoryobjects/index.html | 1865 -------- docs/v3/v1/graph/docs/index.html | 1858 -------- docs/v3/v1/graph/docs/insights/index.html | 1698 ------- docs/v3/v1/graph/docs/invitations/index.html | 1742 ------- docs/v3/v1/graph/docs/onedrive/index.html | 2086 -------- docs/v3/v1/graph/docs/people/index.html | 1664 ------- docs/v3/v1/graph/docs/planner/index.html | 2097 --------- docs/v3/v1/graph/docs/security/index.html | 1692 ------- docs/v3/v1/graph/docs/sites/index.html | 2043 -------- .../v3/v1/graph/docs/subscriptions/index.html | 1835 -------- docs/v3/v1/graph/docs/teams/index.html | 2097 --------- docs/v3/v1/index.html | 1889 -------- docs/v3/v1/logging/docs/index.html | 2045 -------- .../adal-certificate-fetch-client/index.html | 1676 ------- .../nodejs/docs/adal-fetch-client/index.html | 1713 ------- .../docs/bearer-token-fetch-client/index.html | 1712 ------- docs/v3/v1/nodejs/docs/index.html | 1764 ------- .../docs/provider-hosted-app/index.html | 1725 ------- docs/v3/v1/nodejs/docs/proxy/index.html | 1699 ------- .../v1/nodejs/docs/sp-fetch-client/index.html | 1895 -------- docs/v3/v1/odata/docs/caching/index.html | 1988 -------- docs/v3/v1/odata/docs/core/index.html | 1829 ------- docs/v3/v1/odata/docs/index.html | 1781 ------- docs/v3/v1/odata/docs/odata-batch/index.html | 1755 ------- docs/v3/v1/odata/docs/parsers/index.html | 1862 -------- docs/v3/v1/odata/docs/pipeline/index.html | 1811 ------- docs/v3/v1/odata/docs/queryable/index.html | 1967 -------- docs/v3/v1/pnpjs/docs/index.html | 1814 ------- docs/v3/v1/search/search_index.json | 1 - docs/v3/v1/sitemap.xml | 428 -- docs/v3/v1/sitemap.xml.gz | Bin 878 -> 0 bytes docs/v3/v1/sp-addinhelpers/docs/index.html | 1771 ------- .../sp-request-executor-client/index.html | 1760 ------- .../docs/sp-rest-addin/index.html | 1711 ------- docs/v3/v1/sp-clientsvc/docs/index.html | 1736 ------- docs/v3/v1/sp-taxonomy/docs/index.html | 1887 -------- docs/v3/v1/sp-taxonomy/docs/labels/index.html | 1827 ------- .../sp-taxonomy/docs/term-groups/index.html | 1875 -------- .../v1/sp-taxonomy/docs/term-sets/index.html | 1946 -------- .../sp-taxonomy/docs/term-stores/index.html | 2111 --------- docs/v3/v1/sp-taxonomy/docs/terms/index.html | 2036 -------- .../v1/sp-taxonomy/docs/utilities/index.html | 1783 ------- .../v3/v1/sp/docs/alias-parameters/index.html | 1830 ------- docs/v3/v1/sp/docs/alm/index.html | 1920 -------- docs/v3/v1/sp/docs/attachments/index.html | 1998 -------- .../v1/sp/docs/client-side-pages/index.html | 2031 -------- docs/v3/v1/sp/docs/comments-likes/index.html | 1958 -------- docs/v3/v1/sp/docs/content-types/index.html | 1758 ------- docs/v3/v1/sp/docs/entity-merging/index.html | 1838 -------- docs/v3/v1/sp/docs/features/index.html | 1824 ------- docs/v3/v1/sp/docs/fields/index.html | 2031 -------- docs/v3/v1/sp/docs/files/index.html | 2178 --------- docs/v3/v1/sp/docs/index.html | 1894 -------- docs/v3/v1/sp/docs/items/index.html | 2362 ---------- .../v1/sp/docs/navigation-service/index.html | 1791 ------- docs/v3/v1/sp/docs/permissions/index.html | 1860 -------- docs/v3/v1/sp/docs/profiles/index.html | 1794 ------- docs/v3/v1/sp/docs/related-items/index.html | 1886 -------- docs/v3/v1/sp/docs/search/index.html | 1830 ------- docs/v3/v1/sp/docs/sharing/index.html | 2058 -------- docs/v3/v1/sp/docs/sitedesigns/index.html | 1851 -------- docs/v3/v1/sp/docs/sites/index.html | 2186 --------- docs/v3/v1/sp/docs/social/index.html | 2011 -------- .../sp/docs/sp-utilities-utility/index.html | 2061 -------- .../v1/sp/docs/tenant-properties/index.html | 1795 ------- docs/v3/v1/sp/docs/views/index.html | 1869 -------- docs/v3/v1/sp/docs/webs/index.html | 1972 -------- docs/v3/v2/404.html | 2108 --------- docs/v3/v2/SPFx-on-premises/index.html | 2278 --------- docs/v3/v2/_theme/main.html | 5 - docs/v3/v2/adaljsclient/adalclient/index.html | 15 - docs/v3/v2/adaljsclient/index.html | 15 - docs/v3/v2/assets/images/favicon.png | Bin 1870 -> 0 bytes .../assets/javascripts/bundle.9554a270.min.js | 2 - .../javascripts/bundle.9554a270.min.js.map | 1 - .../javascripts/lunr/min/lunr.ar.min.js | 1 - .../javascripts/lunr/min/lunr.da.min.js | 18 - .../javascripts/lunr/min/lunr.de.min.js | 18 - .../javascripts/lunr/min/lunr.du.min.js | 18 - .../javascripts/lunr/min/lunr.es.min.js | 18 - .../javascripts/lunr/min/lunr.fi.min.js | 18 - .../javascripts/lunr/min/lunr.fr.min.js | 18 - .../javascripts/lunr/min/lunr.hu.min.js | 18 - .../javascripts/lunr/min/lunr.it.min.js | 18 - .../javascripts/lunr/min/lunr.ja.min.js | 1 - .../javascripts/lunr/min/lunr.jp.min.js | 1 - .../javascripts/lunr/min/lunr.multi.min.js | 1 - .../javascripts/lunr/min/lunr.nl.min.js | 18 - .../javascripts/lunr/min/lunr.no.min.js | 18 - .../javascripts/lunr/min/lunr.pt.min.js | 18 - .../javascripts/lunr/min/lunr.ro.min.js | 18 - .../javascripts/lunr/min/lunr.ru.min.js | 18 - .../lunr/min/lunr.stemmer.support.min.js | 1 - .../javascripts/lunr/min/lunr.sv.min.js | 18 - .../javascripts/lunr/min/lunr.tr.min.js | 18 - .../javascripts/lunr/min/lunr.vi.min.js | 1 - .../v2/assets/javascripts/lunr/tinyseg.min.js | 1 - .../assets/javascripts/vendor.77e55a48.min.js | 30 - .../javascripts/vendor.77e55a48.min.js.map | 1 - .../javascripts/worker/search.4ac00218.min.js | 59 - .../worker/search.4ac00218.min.js.map | 1 - .../assets/stylesheets/main.38780c08.min.css | 3 - .../stylesheets/main.38780c08.min.css.map | 1 - .../stylesheets/palette.3f72e892.min.css | 3 - .../stylesheets/palette.3f72e892.min.css.map | 1 - .../v2/authentication/adaljsclient/index.html | 2510 ---------- .../bearertokenclient/index.html | 2251 --------- .../v2/authentication/client-spa/index.html | 2206 --------- .../v2/authentication/client-spfx/index.html | 2397 ---------- docs/v3/v2/authentication/index.html | 2307 --------- .../v2/authentication/lambdaclient/index.html | 2304 --------- .../v2/authentication/msaljsclient/index.html | 2413 ---------- .../authentication/server-nodejs/index.html | 2404 ---------- .../sp-app-registration/index.html | 2273 --------- docs/v3/v2/common/collections/index.html | 2277 --------- .../common/custom-httpclientimpl/index.html | 2300 --------- docs/v3/v2/common/index.html | 2262 --------- docs/v3/v2/common/libconfig/index.html | 2413 ---------- docs/v3/v2/common/netutil/index.html | 2350 --------- docs/v3/v2/common/storage/index.html | 2339 --------- docs/v3/v2/common/util/index.html | 2638 ----------- docs/v3/v2/concepts/configuration/index.html | 2685 ----------- docs/v3/v2/concepts/custom-bundle/index.html | 2279 --------- docs/v3/v2/concepts/error-handling/index.html | 2539 ---------- docs/v3/v2/concepts/ie11-mode/index.html | 2278 --------- docs/v3/v2/concepts/invokable/index.html | 2254 --------- docs/v3/v2/concepts/polyfill/index.html | 2401 ---------- .../v2/concepts/selective-imports/index.html | 2360 ---------- docs/v3/v2/concepts/settings/index.html | 2551 ---------- .../v2/config-store/configuration/index.html | 2226 --------- docs/v3/v2/config-store/index.html | 2239 --------- docs/v3/v2/config-store/providers/index.html | 2275 --------- .../v3/v2/contributing/debug-tests/index.html | 2323 --------- docs/v3/v2/contributing/debugging/index.html | 2514 ---------- .../v2/contributing/documentation/index.html | 2291 --------- .../extending-the-library/index.html | 2521 ---------- docs/v3/v2/contributing/index.html | 2270 --------- .../local-debug-configuration/index.html | 2284 --------- .../v2/contributing/pull-requests/index.html | 2261 --------- .../contributing/setup-dev-machine/index.html | 2284 --------- docs/v3/v2/css/extra.css | 33 - docs/v3/v2/debug-tests/index.html | 15 - docs/v3/v2/debugging/index.html | 15 - docs/v3/v2/documentation/index.html | 15 - docs/v3/v2/getting-started-dev/index.html | 15 - docs/v3/v2/getting-started/index.html | 2789 ----------- docs/v3/v2/graph/calendars/index.html | 2569 ---------- docs/v3/v2/graph/contacts/index.html | 2673 ----------- docs/v3/v2/graph/directoryobjects/index.html | 2402 ---------- docs/v3/v2/graph/groups/index.html | 2488 ---------- docs/v3/v2/graph/index.html | 2333 --------- docs/v3/v2/graph/insights/index.html | 2478 ---------- docs/v3/v2/graph/invitations/index.html | 2273 --------- docs/v3/v2/graph/onedrive/index.html | 2617 ---------- docs/v3/v2/graph/outlook/index.html | 2363 ---------- docs/v3/v2/graph/photos/index.html | 2351 --------- docs/v3/v2/graph/planner/index.html | 2628 ----------- docs/v3/v2/graph/search/index.html | 2264 --------- docs/v3/v2/graph/subscriptions/index.html | 2333 --------- docs/v3/v2/graph/teams/index.html | 2619 ----------- docs/v3/v2/graph/users/index.html | 2461 ---------- docs/v3/v2/img/ConsoleListenerColors.png | Bin 4349 -> 0 bytes docs/v3/v2/img/Logo.png | Bin 3151 -> 0 bytes docs/v3/v2/img/PnPJS_FluentAPI.gif | Bin 707717 -> 0 bytes docs/v3/v2/img/SPFx-On-Premesis-2016-1.png | Bin 46965 -> 0 bytes docs/v3/v2/img/office365-header-icon.png | Bin 312 -> 0 bytes docs/v3/v2/img/usage-2020-eoy.png | Bin 41944 -> 0 bytes docs/v3/v2/index.html | 2410 ---------- docs/v3/v2/logging/index.html | 2622 ----------- .../v3/v2/news/2020-year-in-review/index.html | 2525 ---------- docs/v3/v2/nodejs-support/index.html | 2398 ---------- .../v3/v2/nodejs/adal-fetch-client/index.html | 2204 --------- .../bearer-token-fetch-client/index.html | 2203 --------- docs/v3/v2/nodejs/index.html | 2261 --------- .../v2/nodejs/provider-hosted-app/index.html | 2220 --------- docs/v3/v2/nodejs/proxy/index.html | 2238 --------- docs/v3/v2/nodejs/sp-extensions/index.html | 2362 ---------- docs/v3/v2/nodejs/sp-fetch-client/index.html | 2294 --------- docs/v3/v2/npm-scripts/index.html | 2521 ---------- docs/v3/v2/odata/caching/index.html | 2465 ---------- docs/v3/v2/odata/core/index.html | 2322 --------- docs/v3/v2/odata/debug/index.html | 2358 ---------- docs/v3/v2/odata/extensions/index.html | 2581 ---------- docs/v3/v2/odata/index.html | 2259 --------- docs/v3/v2/odata/odata-batch/index.html | 2250 --------- docs/v3/v2/odata/parsers/index.html | 2355 --------- docs/v3/v2/odata/pipeline/index.html | 2297 --------- docs/v3/v2/odata/queryable/index.html | 2455 ---------- docs/v3/v2/packages/index.html | 2251 --------- docs/v3/v2/pnpjs/index.html | 2281 --------- docs/v3/v2/search/search_index.json | 1 - docs/v3/v2/sitemap.xml | 483 -- docs/v3/v2/sitemap.xml.gz | Bin 1134 -> 0 bytes docs/v3/v2/sp-addinhelpers/index.html | 2274 --------- .../sp-request-executor-client/index.html | 2253 --------- .../sp-addinhelpers/sp-rest-addin/index.html | 2206 --------- docs/v3/v2/sp/alias-parameters/index.html | 2330 --------- docs/v3/v2/sp/alm/index.html | 2433 ---------- docs/v3/v2/sp/attachments/index.html | 2504 ---------- docs/v3/v2/sp/clientside-pages/index.html | 3477 -------------- docs/v3/v2/sp/column-defaults/index.html | 2540 ---------- docs/v3/v2/sp/comments-likes/index.html | 2646 ----------- docs/v3/v2/sp/content-types/index.html | 2462 ---------- .../v3/v2/sp/custom-irequestclient/index.html | 2339 --------- docs/v3/v2/sp/entity-merging/index.html | 2325 --------- docs/v3/v2/sp/features/index.html | 2440 ---------- docs/v3/v2/sp/fields/index.html | 3103 ------------ docs/v3/v2/sp/files/index.html | 2949 ------------ docs/v3/v2/sp/folders/index.html | 2968 ------------ docs/v3/v2/sp/forms/index.html | 2289 --------- docs/v3/v2/sp/hubsites/index.html | 2485 ---------- docs/v3/v2/sp/index.html | 2328 --------- docs/v3/v2/sp/items/index.html | 2974 ------------ docs/v3/v2/sp/lists/index.html | 3387 ------------- docs/v3/v2/sp/navigation/index.html | 2535 ---------- docs/v3/v2/sp/permissions/index.html | 2338 --------- docs/v3/v2/sp/profiles/index.html | 2806 ----------- docs/v3/v2/sp/regional-settings/index.html | 2394 ---------- docs/v3/v2/sp/related-items/index.html | 2417 ---------- docs/v3/v2/sp/search/index.html | 2474 ---------- docs/v3/v2/sp/security/index.html | 2432 ---------- docs/v3/v2/sp/sharing/index.html | 2589 ---------- docs/v3/v2/sp/site-designs/index.html | 2414 ---------- docs/v3/v2/sp/site-groups/index.html | 2504 ---------- docs/v3/v2/sp/site-scripts/index.html | 2433 ---------- docs/v3/v2/sp/site-users/index.html | 2641 ----------- docs/v3/v2/sp/sites/index.html | 2727 ----------- docs/v3/v2/sp/social/index.html | 2517 ---------- docs/v3/v2/sp/sp-utilities-utility/index.html | 2460 ---------- docs/v3/v2/sp/subscriptions/index.html | 2395 ---------- docs/v3/v2/sp/taxonomy/index.html | 2565 ---------- docs/v3/v2/sp/tenant-properties/index.html | 2286 --------- docs/v3/v2/sp/user-custom-actions/index.html | 2424 ---------- docs/v3/v2/sp/views/index.html | 2652 ----------- docs/v3/v2/sp/webs/index.html | 4190 ----------------- docs/v3/v2/transition-guide/index.html | 2435 ---------- 311 files changed, 469657 deletions(-) delete mode 100644 docs/v3/v1/404.html delete mode 100644 docs/v3/v1/assets/fonts/font-awesome.css delete mode 100644 docs/v3/v1/assets/fonts/material-icons.css delete mode 100644 docs/v3/v1/assets/fonts/specimen/FontAwesome.ttf delete mode 100644 docs/v3/v1/assets/fonts/specimen/FontAwesome.woff delete mode 100644 docs/v3/v1/assets/fonts/specimen/FontAwesome.woff2 delete mode 100644 docs/v3/v1/assets/fonts/specimen/MaterialIcons-Regular.ttf delete mode 100644 docs/v3/v1/assets/fonts/specimen/MaterialIcons-Regular.woff delete mode 100644 docs/v3/v1/assets/fonts/specimen/MaterialIcons-Regular.woff2 delete mode 100644 docs/v3/v1/assets/images/favicon.png delete mode 100644 docs/v3/v1/assets/images/icons/bitbucket.1b09e088.svg delete mode 100644 docs/v3/v1/assets/images/icons/github.f0b8504a.svg delete mode 100644 docs/v3/v1/assets/images/icons/gitlab.6dd19c00.svg delete mode 100644 docs/v3/v1/assets/javascripts/application.583bbe55.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.da.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.de.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.du.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.es.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.fi.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.fr.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.hu.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.it.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.jp.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.multi.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.no.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.pt.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.ro.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.ru.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.stemmer.support.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.sv.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/lunr.tr.js delete mode 100644 docs/v3/v1/assets/javascripts/lunr/tinyseg.js delete mode 100644 docs/v3/v1/assets/javascripts/modernizr.1aa3b519.js delete mode 100644 docs/v3/v1/assets/stylesheets/application-palette.22915126.css delete mode 100644 docs/v3/v1/assets/stylesheets/application.451f80e5.css delete mode 100644 docs/v3/v1/common/docs/adalclient/index.html delete mode 100644 docs/v3/v1/common/docs/collections/index.html delete mode 100644 docs/v3/v1/common/docs/custom-httpclientimpl/index.html delete mode 100644 docs/v3/v1/common/docs/index.html delete mode 100644 docs/v3/v1/common/docs/libconfig/index.html delete mode 100644 docs/v3/v1/common/docs/netutil/index.html delete mode 100644 docs/v3/v1/common/docs/storage/index.html delete mode 100644 docs/v3/v1/common/docs/util/index.html delete mode 100644 docs/v3/v1/config-store/docs/configuration/index.html delete mode 100644 docs/v3/v1/config-store/docs/index.html delete mode 100644 docs/v3/v1/config-store/docs/providers/index.html delete mode 100644 docs/v3/v1/documentation/SPFx-On-Premesis-2016/index.html delete mode 100644 docs/v3/v1/documentation/beta-versions/index.html delete mode 100644 docs/v3/v1/documentation/css/extra.css delete mode 100644 docs/v3/v1/documentation/debugging/index.html delete mode 100644 docs/v3/v1/documentation/deployment/index.html delete mode 100644 docs/v3/v1/documentation/documentation/index.html delete mode 100644 docs/v3/v1/documentation/getting-started-dev/index.html delete mode 100644 docs/v3/v1/documentation/getting-started/index.html delete mode 100644 docs/v3/v1/documentation/gulp-commands/index.html delete mode 100644 docs/v3/v1/documentation/img/Logo.png delete mode 100644 docs/v3/v1/documentation/img/PnPJS_FluentAPI.gif delete mode 100644 docs/v3/v1/documentation/img/SPFx-On-Premesis-2016-1.png delete mode 100644 docs/v3/v1/documentation/img/office365-header-icon.png delete mode 100644 docs/v3/v1/documentation/img/pnpjs-common-uml.svg delete mode 100644 docs/v3/v1/documentation/img/pnpjs-config-store-uml.svg delete mode 100644 docs/v3/v1/documentation/img/pnpjs-graph-uml.svg delete mode 100644 docs/v3/v1/documentation/img/pnpjs-logging-uml.svg delete mode 100644 docs/v3/v1/documentation/img/pnpjs-nodejs-uml.svg delete mode 100644 docs/v3/v1/documentation/img/pnpjs-odata-uml.svg delete mode 100644 docs/v3/v1/documentation/img/pnpjs-sp-addinhelpers-uml.svg delete mode 100644 docs/v3/v1/documentation/img/pnpjs-sp-clientsvc-uml.svg delete mode 100644 docs/v3/v1/documentation/img/pnpjs-sp-taxonomy-uml.svg delete mode 100644 docs/v3/v1/documentation/img/pnpjs-sp-uml.svg delete mode 100644 docs/v3/v1/documentation/package-structure/index.html delete mode 100644 docs/v3/v1/documentation/packages/index.html delete mode 100644 docs/v3/v1/documentation/polyfill/index.html delete mode 100644 docs/v3/v1/documentation/theme/main.html delete mode 100644 docs/v3/v1/documentation/transition-guide/index.html delete mode 100644 docs/v3/v1/graph/docs/contacts/index.html delete mode 100644 docs/v3/v1/graph/docs/directoryobjects/index.html delete mode 100644 docs/v3/v1/graph/docs/index.html delete mode 100644 docs/v3/v1/graph/docs/insights/index.html delete mode 100644 docs/v3/v1/graph/docs/invitations/index.html delete mode 100644 docs/v3/v1/graph/docs/onedrive/index.html delete mode 100644 docs/v3/v1/graph/docs/people/index.html delete mode 100644 docs/v3/v1/graph/docs/planner/index.html delete mode 100644 docs/v3/v1/graph/docs/security/index.html delete mode 100644 docs/v3/v1/graph/docs/sites/index.html delete mode 100644 docs/v3/v1/graph/docs/subscriptions/index.html delete mode 100644 docs/v3/v1/graph/docs/teams/index.html delete mode 100644 docs/v3/v1/index.html delete mode 100644 docs/v3/v1/logging/docs/index.html delete mode 100644 docs/v3/v1/nodejs/docs/adal-certificate-fetch-client/index.html delete mode 100644 docs/v3/v1/nodejs/docs/adal-fetch-client/index.html delete mode 100644 docs/v3/v1/nodejs/docs/bearer-token-fetch-client/index.html delete mode 100644 docs/v3/v1/nodejs/docs/index.html delete mode 100644 docs/v3/v1/nodejs/docs/provider-hosted-app/index.html delete mode 100644 docs/v3/v1/nodejs/docs/proxy/index.html delete mode 100644 docs/v3/v1/nodejs/docs/sp-fetch-client/index.html delete mode 100644 docs/v3/v1/odata/docs/caching/index.html delete mode 100644 docs/v3/v1/odata/docs/core/index.html delete mode 100644 docs/v3/v1/odata/docs/index.html delete mode 100644 docs/v3/v1/odata/docs/odata-batch/index.html delete mode 100644 docs/v3/v1/odata/docs/parsers/index.html delete mode 100644 docs/v3/v1/odata/docs/pipeline/index.html delete mode 100644 docs/v3/v1/odata/docs/queryable/index.html delete mode 100644 docs/v3/v1/pnpjs/docs/index.html delete mode 100644 docs/v3/v1/search/search_index.json delete mode 100644 docs/v3/v1/sitemap.xml delete mode 100644 docs/v3/v1/sitemap.xml.gz delete mode 100644 docs/v3/v1/sp-addinhelpers/docs/index.html delete mode 100644 docs/v3/v1/sp-addinhelpers/docs/sp-request-executor-client/index.html delete mode 100644 docs/v3/v1/sp-addinhelpers/docs/sp-rest-addin/index.html delete mode 100644 docs/v3/v1/sp-clientsvc/docs/index.html delete mode 100644 docs/v3/v1/sp-taxonomy/docs/index.html delete mode 100644 docs/v3/v1/sp-taxonomy/docs/labels/index.html delete mode 100644 docs/v3/v1/sp-taxonomy/docs/term-groups/index.html delete mode 100644 docs/v3/v1/sp-taxonomy/docs/term-sets/index.html delete mode 100644 docs/v3/v1/sp-taxonomy/docs/term-stores/index.html delete mode 100644 docs/v3/v1/sp-taxonomy/docs/terms/index.html delete mode 100644 docs/v3/v1/sp-taxonomy/docs/utilities/index.html delete mode 100644 docs/v3/v1/sp/docs/alias-parameters/index.html delete mode 100644 docs/v3/v1/sp/docs/alm/index.html delete mode 100644 docs/v3/v1/sp/docs/attachments/index.html delete mode 100644 docs/v3/v1/sp/docs/client-side-pages/index.html delete mode 100644 docs/v3/v1/sp/docs/comments-likes/index.html delete mode 100644 docs/v3/v1/sp/docs/content-types/index.html delete mode 100644 docs/v3/v1/sp/docs/entity-merging/index.html delete mode 100644 docs/v3/v1/sp/docs/features/index.html delete mode 100644 docs/v3/v1/sp/docs/fields/index.html delete mode 100644 docs/v3/v1/sp/docs/files/index.html delete mode 100644 docs/v3/v1/sp/docs/index.html delete mode 100644 docs/v3/v1/sp/docs/items/index.html delete mode 100644 docs/v3/v1/sp/docs/navigation-service/index.html delete mode 100644 docs/v3/v1/sp/docs/permissions/index.html delete mode 100644 docs/v3/v1/sp/docs/profiles/index.html delete mode 100644 docs/v3/v1/sp/docs/related-items/index.html delete mode 100644 docs/v3/v1/sp/docs/search/index.html delete mode 100644 docs/v3/v1/sp/docs/sharing/index.html delete mode 100644 docs/v3/v1/sp/docs/sitedesigns/index.html delete mode 100644 docs/v3/v1/sp/docs/sites/index.html delete mode 100644 docs/v3/v1/sp/docs/social/index.html delete mode 100644 docs/v3/v1/sp/docs/sp-utilities-utility/index.html delete mode 100644 docs/v3/v1/sp/docs/tenant-properties/index.html delete mode 100644 docs/v3/v1/sp/docs/views/index.html delete mode 100644 docs/v3/v1/sp/docs/webs/index.html delete mode 100644 docs/v3/v2/404.html delete mode 100644 docs/v3/v2/SPFx-on-premises/index.html delete mode 100644 docs/v3/v2/_theme/main.html delete mode 100644 docs/v3/v2/adaljsclient/adalclient/index.html delete mode 100644 docs/v3/v2/adaljsclient/index.html delete mode 100644 docs/v3/v2/assets/images/favicon.png delete mode 100644 docs/v3/v2/assets/javascripts/bundle.9554a270.min.js delete mode 100644 docs/v3/v2/assets/javascripts/bundle.9554a270.min.js.map delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.ar.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.da.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.de.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.du.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.es.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.fi.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.fr.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.hu.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.it.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.ja.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.jp.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.multi.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.nl.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.no.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.pt.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.ro.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.ru.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.stemmer.support.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.sv.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.tr.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/min/lunr.vi.min.js delete mode 100644 docs/v3/v2/assets/javascripts/lunr/tinyseg.min.js delete mode 100644 docs/v3/v2/assets/javascripts/vendor.77e55a48.min.js delete mode 100644 docs/v3/v2/assets/javascripts/vendor.77e55a48.min.js.map delete mode 100644 docs/v3/v2/assets/javascripts/worker/search.4ac00218.min.js delete mode 100644 docs/v3/v2/assets/javascripts/worker/search.4ac00218.min.js.map delete mode 100644 docs/v3/v2/assets/stylesheets/main.38780c08.min.css delete mode 100644 docs/v3/v2/assets/stylesheets/main.38780c08.min.css.map delete mode 100644 docs/v3/v2/assets/stylesheets/palette.3f72e892.min.css delete mode 100644 docs/v3/v2/assets/stylesheets/palette.3f72e892.min.css.map delete mode 100644 docs/v3/v2/authentication/adaljsclient/index.html delete mode 100644 docs/v3/v2/authentication/bearertokenclient/index.html delete mode 100644 docs/v3/v2/authentication/client-spa/index.html delete mode 100644 docs/v3/v2/authentication/client-spfx/index.html delete mode 100644 docs/v3/v2/authentication/index.html delete mode 100644 docs/v3/v2/authentication/lambdaclient/index.html delete mode 100644 docs/v3/v2/authentication/msaljsclient/index.html delete mode 100644 docs/v3/v2/authentication/server-nodejs/index.html delete mode 100644 docs/v3/v2/authentication/sp-app-registration/index.html delete mode 100644 docs/v3/v2/common/collections/index.html delete mode 100644 docs/v3/v2/common/custom-httpclientimpl/index.html delete mode 100644 docs/v3/v2/common/index.html delete mode 100644 docs/v3/v2/common/libconfig/index.html delete mode 100644 docs/v3/v2/common/netutil/index.html delete mode 100644 docs/v3/v2/common/storage/index.html delete mode 100644 docs/v3/v2/common/util/index.html delete mode 100644 docs/v3/v2/concepts/configuration/index.html delete mode 100644 docs/v3/v2/concepts/custom-bundle/index.html delete mode 100644 docs/v3/v2/concepts/error-handling/index.html delete mode 100644 docs/v3/v2/concepts/ie11-mode/index.html delete mode 100644 docs/v3/v2/concepts/invokable/index.html delete mode 100644 docs/v3/v2/concepts/polyfill/index.html delete mode 100644 docs/v3/v2/concepts/selective-imports/index.html delete mode 100644 docs/v3/v2/concepts/settings/index.html delete mode 100644 docs/v3/v2/config-store/configuration/index.html delete mode 100644 docs/v3/v2/config-store/index.html delete mode 100644 docs/v3/v2/config-store/providers/index.html delete mode 100644 docs/v3/v2/contributing/debug-tests/index.html delete mode 100644 docs/v3/v2/contributing/debugging/index.html delete mode 100644 docs/v3/v2/contributing/documentation/index.html delete mode 100644 docs/v3/v2/contributing/extending-the-library/index.html delete mode 100644 docs/v3/v2/contributing/index.html delete mode 100644 docs/v3/v2/contributing/local-debug-configuration/index.html delete mode 100644 docs/v3/v2/contributing/pull-requests/index.html delete mode 100644 docs/v3/v2/contributing/setup-dev-machine/index.html delete mode 100644 docs/v3/v2/css/extra.css delete mode 100644 docs/v3/v2/debug-tests/index.html delete mode 100644 docs/v3/v2/debugging/index.html delete mode 100644 docs/v3/v2/documentation/index.html delete mode 100644 docs/v3/v2/getting-started-dev/index.html delete mode 100644 docs/v3/v2/getting-started/index.html delete mode 100644 docs/v3/v2/graph/calendars/index.html delete mode 100644 docs/v3/v2/graph/contacts/index.html delete mode 100644 docs/v3/v2/graph/directoryobjects/index.html delete mode 100644 docs/v3/v2/graph/groups/index.html delete mode 100644 docs/v3/v2/graph/index.html delete mode 100644 docs/v3/v2/graph/insights/index.html delete mode 100644 docs/v3/v2/graph/invitations/index.html delete mode 100644 docs/v3/v2/graph/onedrive/index.html delete mode 100644 docs/v3/v2/graph/outlook/index.html delete mode 100644 docs/v3/v2/graph/photos/index.html delete mode 100644 docs/v3/v2/graph/planner/index.html delete mode 100644 docs/v3/v2/graph/search/index.html delete mode 100644 docs/v3/v2/graph/subscriptions/index.html delete mode 100644 docs/v3/v2/graph/teams/index.html delete mode 100644 docs/v3/v2/graph/users/index.html delete mode 100644 docs/v3/v2/img/ConsoleListenerColors.png delete mode 100644 docs/v3/v2/img/Logo.png delete mode 100644 docs/v3/v2/img/PnPJS_FluentAPI.gif delete mode 100644 docs/v3/v2/img/SPFx-On-Premesis-2016-1.png delete mode 100644 docs/v3/v2/img/office365-header-icon.png delete mode 100644 docs/v3/v2/img/usage-2020-eoy.png delete mode 100644 docs/v3/v2/index.html delete mode 100644 docs/v3/v2/logging/index.html delete mode 100644 docs/v3/v2/news/2020-year-in-review/index.html delete mode 100644 docs/v3/v2/nodejs-support/index.html delete mode 100644 docs/v3/v2/nodejs/adal-fetch-client/index.html delete mode 100644 docs/v3/v2/nodejs/bearer-token-fetch-client/index.html delete mode 100644 docs/v3/v2/nodejs/index.html delete mode 100644 docs/v3/v2/nodejs/provider-hosted-app/index.html delete mode 100644 docs/v3/v2/nodejs/proxy/index.html delete mode 100644 docs/v3/v2/nodejs/sp-extensions/index.html delete mode 100644 docs/v3/v2/nodejs/sp-fetch-client/index.html delete mode 100644 docs/v3/v2/npm-scripts/index.html delete mode 100644 docs/v3/v2/odata/caching/index.html delete mode 100644 docs/v3/v2/odata/core/index.html delete mode 100644 docs/v3/v2/odata/debug/index.html delete mode 100644 docs/v3/v2/odata/extensions/index.html delete mode 100644 docs/v3/v2/odata/index.html delete mode 100644 docs/v3/v2/odata/odata-batch/index.html delete mode 100644 docs/v3/v2/odata/parsers/index.html delete mode 100644 docs/v3/v2/odata/pipeline/index.html delete mode 100644 docs/v3/v2/odata/queryable/index.html delete mode 100644 docs/v3/v2/packages/index.html delete mode 100644 docs/v3/v2/pnpjs/index.html delete mode 100644 docs/v3/v2/search/search_index.json delete mode 100644 docs/v3/v2/sitemap.xml delete mode 100644 docs/v3/v2/sitemap.xml.gz delete mode 100644 docs/v3/v2/sp-addinhelpers/index.html delete mode 100644 docs/v3/v2/sp-addinhelpers/sp-request-executor-client/index.html delete mode 100644 docs/v3/v2/sp-addinhelpers/sp-rest-addin/index.html delete mode 100644 docs/v3/v2/sp/alias-parameters/index.html delete mode 100644 docs/v3/v2/sp/alm/index.html delete mode 100644 docs/v3/v2/sp/attachments/index.html delete mode 100644 docs/v3/v2/sp/clientside-pages/index.html delete mode 100644 docs/v3/v2/sp/column-defaults/index.html delete mode 100644 docs/v3/v2/sp/comments-likes/index.html delete mode 100644 docs/v3/v2/sp/content-types/index.html delete mode 100644 docs/v3/v2/sp/custom-irequestclient/index.html delete mode 100644 docs/v3/v2/sp/entity-merging/index.html delete mode 100644 docs/v3/v2/sp/features/index.html delete mode 100644 docs/v3/v2/sp/fields/index.html delete mode 100644 docs/v3/v2/sp/files/index.html delete mode 100644 docs/v3/v2/sp/folders/index.html delete mode 100644 docs/v3/v2/sp/forms/index.html delete mode 100644 docs/v3/v2/sp/hubsites/index.html delete mode 100644 docs/v3/v2/sp/index.html delete mode 100644 docs/v3/v2/sp/items/index.html delete mode 100644 docs/v3/v2/sp/lists/index.html delete mode 100644 docs/v3/v2/sp/navigation/index.html delete mode 100644 docs/v3/v2/sp/permissions/index.html delete mode 100644 docs/v3/v2/sp/profiles/index.html delete mode 100644 docs/v3/v2/sp/regional-settings/index.html delete mode 100644 docs/v3/v2/sp/related-items/index.html delete mode 100644 docs/v3/v2/sp/search/index.html delete mode 100644 docs/v3/v2/sp/security/index.html delete mode 100644 docs/v3/v2/sp/sharing/index.html delete mode 100644 docs/v3/v2/sp/site-designs/index.html delete mode 100644 docs/v3/v2/sp/site-groups/index.html delete mode 100644 docs/v3/v2/sp/site-scripts/index.html delete mode 100644 docs/v3/v2/sp/site-users/index.html delete mode 100644 docs/v3/v2/sp/sites/index.html delete mode 100644 docs/v3/v2/sp/social/index.html delete mode 100644 docs/v3/v2/sp/sp-utilities-utility/index.html delete mode 100644 docs/v3/v2/sp/subscriptions/index.html delete mode 100644 docs/v3/v2/sp/taxonomy/index.html delete mode 100644 docs/v3/v2/sp/tenant-properties/index.html delete mode 100644 docs/v3/v2/sp/user-custom-actions/index.html delete mode 100644 docs/v3/v2/sp/views/index.html delete mode 100644 docs/v3/v2/sp/webs/index.html delete mode 100644 docs/v3/v2/transition-guide/index.html diff --git a/docs/v3/v1/404.html b/docs/v3/v1/404.html deleted file mode 100644 index ffa3a384e..000000000 --- a/docs/v3/v1/404.html +++ /dev/null @@ -1,1611 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- -
- - - - - - - - -
-
- - -
-
-
- -
-
-
- - - -
-
- -

404 - Not found

- - - - - - -
-
-
-
- - - - -
- - - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v1/assets/fonts/font-awesome.css b/docs/v3/v1/assets/fonts/font-awesome.css deleted file mode 100644 index b476b53e3..000000000 --- a/docs/v3/v1/assets/fonts/font-awesome.css +++ /dev/null @@ -1,4 +0,0 @@ -/*! - * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome - * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url("specimen/FontAwesome.woff2") format("woff2"),url("specimen/FontAwesome.woff") format("woff"),url("specimen/FontAwesome.ttf") format("truetype")}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1,1);-ms-transform:scale(-1,1);transform:scale(-1,1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1,-1);-ms-transform:scale(1,-1);transform:scale(1,-1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} \ No newline at end of file diff --git a/docs/v3/v1/assets/fonts/material-icons.css b/docs/v3/v1/assets/fonts/material-icons.css deleted file mode 100644 index d23d365ed..000000000 --- a/docs/v3/v1/assets/fonts/material-icons.css +++ /dev/null @@ -1,13 +0,0 @@ -/*! - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy - * of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, SOFTWARE - * DISTRIBUTED UNDER THE LICENSE IS DISTRIBUTED ON AN "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - * SEE THE LICENSE FOR THE SPECIFIC LANGUAGE GOVERNING PERMISSIONS AND - * LIMITATIONS UNDER THE LICENSE. - */@font-face{font-family:"Material Icons";font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url("specimen/MaterialIcons-Regular.woff2") format("woff2"),url("specimen/MaterialIcons-Regular.woff") format("woff"),url("specimen/MaterialIcons-Regular.ttf") format("truetype")} \ No newline at end of file diff --git a/docs/v3/v1/assets/fonts/specimen/FontAwesome.ttf b/docs/v3/v1/assets/fonts/specimen/FontAwesome.ttf deleted file mode 100644 index 35acda2fa1196aad98c2adf4378a7611dd713aa3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165548 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 z zq1_(l4SX+ekaM;bY|YgEqv2RAEE}e-Im8<@oEZ?Z81Y?3(z-@nRbq?!xD9Hyn|7Gx z-NUw`yOor_DJLC1aqkf2(!i=2$ULNfg|s8bV^xB!_rY+bHA;KsWR@aB=!7n&LJq(} z!pqD3Wkvo-Goy zx1edGgnc}u5V8cw&nvWyWU+wXqwinB#x7(uc>H44lXZQkk*w_q#i2O!s_A?a*?`Rx zoZW6Qtj)L1T^4kDeD7;%G5dS816OPqAqPx~(_-jZ`bo-MR_kd&sJv{A^ zs@18qv!kD;U z5Evv$C*bD~m z+x@>Oo>;7%QCxfp-rOkNgx4j-(o*e5`6lW^X^{qpQo~SMWD`Gxyv6)+k)c@o6j`Yd z8c&XSiYbcmoCKe+82}>^CPM+?p@o&i(J*j0zsk}!P?!W%T5`ppk%)?&GxA`%4>0VX zKu?YB6Z)hFtj@u-icb&t5A1}BX!;~SqG5ARpVB>FEWPLW+C+QOf~G-Jj0r`0D6|0w zQUs5sE6PYc)!HWi))NeRvSZB3kWIW|R^A%RfamB2jCbVX(Fn>y%#b1W%}W%qc)XVrwuvM!>Qur!Ooy2`n@?qMe3$`F2vx z9<=L}wP7@diWhCYTD?x)LZ>F6F?z8naL18P%1T9&P_d4p;u=(XW1LO3-< z`{|5@&Y=}7sx3t1Zs zr9ZBmp}YpHLq7lwu?CXL8$Q65$Q29AlDCBJSxu5;p0({^4skD z+4se#9)xg8qnEh|WnPdgQ&+te7@`9WlzAwMit$Julp+d80n+VM1JxwqS5H6*MPKA` zlJ*Z77B;K~;4JkO5eq(@D}tezez*w6g3ZSn?J1d9Z~&MKbf=b6F9;8H22TxRl%y1r z<-6(lJiLAw>r^-=F-AIEd1y|Aq2MggNo&>7Ln)S~iAF1;-4`A*9KlL*vleLO3vhEd(@RsIWp~O@>N4p91SI zb~+*jP?8B~MwmI0W$>ksF8DC*2y8K0o#te?D$z8nrfK{|B1L^TR5hlugr|o=-;>Yn zmL6Yt=NZ2%cAsysPA)D^gkz2Vvh|Z9RJdoH$L$+6a^|>UO=3fBBH0UidA&_JQz9K~ zuo1Z_(cB7CiQ}4loOL3DsdC<+wYysw@&UMl21+LY-(z=6j8fu5%ZQg-z6Bor^M}LX z9hxH}aVC%rodtoGcTh)zEd=yDfCu5mE)qIjw~K+zwn&5c!L-N+E=kwxVEewN#vvx2WGCf^;C9^mmTlYc*kz$NUdQ=gDzLmf z!LXG7{N$Mi3n}?5L&f9TlCzzrgGR*6>MhWBR=lS)qP$&OMAQ2 z`$23{zM%a@9EPdjV|Y1zVVGf?mINO)i-q6;_Ev|n_JQ^Zy&BnUgV>NbY9xba1DlY@ zrg$_Kn?+^_+4V4^xS94tX2oLKAEiuU0<2S#v$WSDt0P^A+d-+M?XlR**u_Xdre&aY zNi~zJk9aLQUqaFZxCNRmu*wnxB_u*M6V0xVCtBhtpGUK)#Dob6DWm-n^~Vy)m~?Yg zO0^+v~`x6Vqtjl4I5;=^o2jyOb~m+ER;lNwO$iN ziH4vk>E`OTRx~v#B|ifef|ceH)%hgqOy|#f=Q|VlN6i{!0CRndN~x8wS6Ppqq7NSH zO5hX{k5T{4ib@&8t)u=V9nY+2RC^75jU%TRix}FDTB%>t;5jpNRv;(KB|%{AI7Jc= zd%t9-AjNUAs?8m40SLOhrjbC_yZoznU$(rnT2);Rr`2e6$k!zwlz!d|sZ3%x@$Nw? zVn?i%t!J+9SF@^ zO&TGun2&?VIygfH5ePk|!e&G3Zm-GUP(imiWzZu$9JU)Wot`}*RHV<-)vUhc6J6{w&PQIaSZ_N<(d>`C$yo#Ly&0Sr5gCkDY(4f@fY5!fLe57sH54#FF4 zg&hda`KjtJ8cTzz;DwFa#{$!}j~g$9zqFBC@To^}i#`b~xhU;p{x{^f1krbEFNqV^ zEq5c!C5XT0o_q{%p&0F@!I;9ejbs#P4q?R!i$?vl3~|GSyq4@q#3=wgsz+zkrIB<< z=HMWEBz?z??GvvT54YsDSnRLcEf!n>^0eKf4(CIT{qs4y$7_4e=JoIkq%~H9$z-r* zZ?`xgwL+DNAJE`VB;S+w#NvBT{3;}{CD&@Ig*Ka2Acx)2Qx zL)V#$n@%vf1Zzms4Th~fS|(DKDT`?BKfX3tkCBvKZLg^hUh|_Gz8?%#d(ANnY`5U1 zo;qjq=5tn!OQ*-JqA&iG-Tg#6Ka|O64eceRrSgggD%%QBX$t=6?hPEK2|lL1{?|>I^Toc>rQU7a_`RSM^EPVl{_&OG-P;|z0?v{3o#pkl zC6Y;&J7;#5N#+H2J-4RqiSK^rj<_Z6t%?`N$A_FUESt{TcayIew5oWi=jxT*aPIP6 z?MG`?k5p%-x>D73irru{R?lu7<54DCT9Q}%=4%@wZij4+M=fzzz`SJ3I%*#AikLUh zn>k=5%IKUP4TrvZ!A{&Oh;BR}6r3t3cpzS(&|cEe&e{MQby|1#X`?17e9?|=i`sPG zL|OOsh`j@PD4sc6&Y3rT`r?-EH0QPR*IobE@_fkB8*(886ZkjkcO{K8Sz$H`^D-8P zjKG9G9A`O!>|!ivAeteRVIcyIGa#O<6I$^O7}9&*8mHd@Gw!WDU*@;*L;SYvlV#p( zzFSsPw&^UdyxO}%i)W8$@f}|84*mz&i2q@SlzMOd%B!BHOJ<(FYUTR(Ui$DuX>?85 zcdzl5m3hzFr2S@c_20C2x&N)|$<=RhzxI!}NN+yS16X^(_mtqY)g*Q%Fux5}bP3q$ zxQD|TB{+4C1gL>zI>g~-ajKMb{2s_cFhN2(I(q^X!$H(GFxpc6oCV9#maj|OhFZaI z;umX6E*fQVTQ@lyZauuv>%E)5z-?zQZne18V5A}}JEQmCz>7^h0r)!zhinBG6 zMQghGt!Do5h%HmAQl~%m+!pr-&wlrcwW;qw)S$6*f}ZvXd;cHw=xm|y~mHbT3yX>?hoYKfy--h+6w9%@_4ukf0Et^zr-DbPwFdyj0VJHi}4bqRetSNR`DoWd( z(%n5>8MQl+>3SeL-DB@IaM{NDwd{{v_HMIO)PKO}v{{##c@ihB0w$aaPTSP4^>n3Z zC8Il%(3dCLLX$-|SwWx1u7KVztXpzNhrOZQ78c$jd{B9lqsNHLr*9h;N9$i+vsrM1 zKzLB_gVdMCfxceejpIZat!MbR)GNZ%^n|fEQo?Xtq#Qa_gEWKTFxSL4b{g}kJNd{QcoQ}HUP-A)Rq;U(***IA*V_0B5mr}Xp$q{YSYs-b2q~DHh z?+muRGn~std!VXuT>P9TL_8Km9G{doqRb-W0B&%d> z^3@hs6y5jaEq%P}dmr(8=f}x~^ z*{I{tkBgYk@Td|Z{csd23pziZlPYt2RJW7D_C#&)OONEWyN`I19_cM;`Aa=y_)ldH z^co(O-xWIN0{y|@?wx@Y!MeVg3Ln%4ORu5~Dl6$h>AGSXrK3!pH%cpM?D|6#*6+A# zlsj;J0_~^?DHIceRC~0iMq)SJ&?R&if{fsdIb>y;H@M4AE`z8~dvz)(e}BqUWK^U~ zFy`PX+z*Bmv9VxAN;%CvMk(#kGBEMP;a-GgGZf~r$(ei(%yGqHa2dS3hxdTT!r>La zUrW2dCTZ!SjD_D(?9$SK02e_#ZOxdAhO%hgVhq54U=2$Hm+1^O^nH<>wS|&<)2TtD zN_MN@O>?A@_&l;U)*GY*5F_a~cgQb_3p`#77ax1iRxIx!r0HkDnA2G*{l|*}g_yI% zZdHt2`Hx^MA#VH7@BEN68Y_;sAcCNgCY7S&dcQsp*$+uW7Dm@$Vl7!YA^51bi} z*Vy8uTj{neIhIL|PhditfC1Jeub(uy}w|wV5 zsQz)04y;BY2$7U4$~P{k)b`hZb>gv1RkD)L#g~$*N^1N1GfNMS)4r|pT*V<&KE1M9 zTh}rzSW#Kcci_#(^qf0gTW3&QN&zsW%VAQ+AZ%-3?E)kMdgL)kY~@mC>l?RH28u;Y zt-@_u^5(W>mDdtqoe){#t;3NA7c@{WoY9bYFNoq+sj&ru;Z`x>4ddY0y*`HRtHFEN% z@mFkp=x0C6zDGgA0s|mP^WNEwE4O}S?%DOtce3At%?ThxRp@`zCH6MyzM)dA9C7IP zI}t;YUV(Jcnw$4LoD4H(EM#!{L-Z|&fhNYnBlKcQ$UScR#HH>scYBTf2u|7Fd8q$R zy5Cbt=Pvf^e}m4?VVL@#Pi3z*q-Q0MG8pGTcbS|eeW%R5bRzKsHSH#G(#$9hj9}0O7lXsC zbZ7#UjJM^FcvdKK3MOEl+Pb-93Px}F$ID&jcvZdJ{d(D)x|*`=vi%1hdg(dd-1E>& zoB4U&a${9!xyxoT%$7gFp{M<_q z9oVnk*Dcp$k#jA#7-pZbXd=L8nDhe<*t_*%gj^Vx>(~KyEY~i&(?@R~L_e^txnUyh z64-dU=Lc;eQ}vPX;g{GitTVZben7||wttapene^dB|oSGB~tmAGqE^`1Jxt$4uXUL zz5?7GEqvmLa{#mgN6la^gYO#}`eXyUJ)lFyTO8*iL~P z$A`A_X^V#!SJyU8Dl%J*6&s9;Jl54CiyfA`ExxmjrZ1P8E%rJ7hFCFo6%{5mRa|LY zk^x76W8M0tQBa1Q(&L`|!e zrczv>+#&b2bt zuD1Bfoe>oW0&!ju$-LI)$URptI!inJ^Dz|<@S1hk+!(n2PWfi-AMb5*F03&_^29MB zgJP7yn#Fw4n&Rod*>LlF+qPx5ZT$80;+m*0X5ffa3d-;F72#5un;L$}RfmR5&xbOf(KNeD|gT1x6bw5t;~j}(oMHcSzkCgcpbd>5UN z7e8CV*di9kpyJAo1YyE9XtfV1Q8^?ViwrKgtK$H60 z%~xgAifVV#>j>4SN10>bP9OV9m`EA-H{bzMimEQ_3@VZH%@KZzjDu` zRCG*Ax6B^%%dyLs2Cw{bePFWM9750@SIoZoff4mJvyxIeIjeZ{tYpbmTk4_{wy!_uygk4J;wwSiK&OpZWguG$O082g z^a3rw)F1Q!*)rNy!Sqz9bk0u-kftk^q{FPl4N+eS@0p1= zhaBFdyShSMz97B%x3GE|Sst~8Le6+?q@g6HwE1hJ#X)o^?{1!x-m`LlQ+4%?^IPIo zHATgqrm-s`+6SW3LjHB>=Pp{i<6FE#j+sX(Vl-kJt6sug<4UG9SH_|( zOb(+Vn|4R4lc8pHa-japR|c0ZAN$KOvzss6bKW^uPM$I$8eTr{EMN2N%{Yrl{Z`Y^ zaQ`-S_6omm((Fih26~Bjf^W$wm1J`8N+(=0ET@KFDy;S%{mF@!2&1UMxk>jTk49;@ z*g#0?*iga;P7abx1bh^d3MoAy*XQp{Hl*t(buU@DamDmvcc;5}`ihM!mvm36|GqRu zn*3}UmnOSUai6mM*y&f#XmqyBo>b=dmra`8;%uC8_33-RpM6;x`Rrc0RM~y9>y~ry zVnGanZLDD_lC%6!F%Jzk##j%?nW>JEaJ#U89t`?mGJS_kO5+5U1Gh;Lb3`{w<-DW; z;USPAm%*aQJ)UeYnLVb2V3MJ2vrxAZ@&#?W$vW)7$+L7~7HSzuF&0V95FC4H6Dy<( z!#o7mJKLMHTNn5)Lyn5l4oh2$s~VI~tlIjn09jE~8C#Ooei=J?K;D+-<8Cb>8RPx8 z-~O0ST{mOeXg+qjG~?}E8@JAo-j?OJjgF3nb^K5v>$yq#-Ybd8lM^jdru2WE-*V6W z>sL(7?%-Qu?&?wZNmmqdn?$FXlE!>2BAa^bWfD69lP0?L3kopYkc4>{m#H6t2dLIEE47|jcI$tEuWzwjmRgqBPkzk zM+(?6)=);W6q<2z95fHMDFKxbhPD-r0IjdX_3EH*BFL|t3))c7d~8v;{wU5p8nHUz9I?>l zVfn$bENo_I3JOh1^^ z+un~MSwCyixbj%C?y{G@G7mSZg_cf~&@djVX_vn8;IF&q?ESd=*AJHOJ(!-hbKPlb zYi-r+me!ezr_eCiQ&SetY;BocRokkbwr=ONGzW2U@X=AUvS^E9eM^w~aztd4h$Q&kF;6EJ1O*M7tJfFi}R1 z6X@asDjL5w+#QEKQE5V48#ASm?H7u5j%nDqi)iO@a1@F z*^R+bGpEOs#pRx9CBZQ}#uQa|dCH5EW%a3Xv1;ye-}5|Yh4g~YH5gI1(b#B|6_ZI; zMkxwTjmkKoZIp~AqhXp+k&SSQ)9C=jCWTKCM?(&MUHex;c3Knl(A%3UgJT_BEixIE zQh!;Q(J<0)C`q0-^|UdaGYzFqr^{vZR~Tk?jyY}gf@H+0RHkZ{OID|x;6>6+g)|BK zs6zLY0U>bcbRd6kU;cgkomCZdBSC8$a1H`pcu;XqH=5 z+$oO3i&T_WpcYnVu*lchi>wxt#iE!!bG#kzjIFqb)`s?|OclRAnzUyW5*Py!P@srDXI}&s2lVYf2ZCG`F`H-9;60 zb<=6weckNk=DC&Q6QxU*uJ9FkaT>}qb##eRS8n%qG`G9WrS>Xm+w)!AXSASfd%5fg z#fqxk(5L9@fM};~Gk^Sgb;7|krF-an$kIROPt4HLqq6+EL+62d@~4Hsy9nIU?=Ue4 zJ69;q+5+73nU|TQu}$>#v(M&Vx1RD=6Lu`d?>zHN?P7J&XWwsvwJt|rr?CZu+l>m4 zTi^VLh6Uu2s392u(5DLaM%)Dr$%h3hRB>V7a9XG`B{ZsWgh4IyTO9R~TAR^h^~>ko z(k|Hy#@bP}7OyN92TKE%qNZfyWL32p-BJf1{jj0QU0V`yj=tRospvSewxGxoC=C|N zve$zAMuSaiyY)QTk9!VmwUK&<#b2fxMl_DX|5x$dKH3>6sdYCQ9@c)^A-Rn9vG?s)0)lCR76kgoR>S;B=kl(v zzM}o+G41dh)%9=ezv$7*a9Mrb+S@13nK-B6D!%vy(}5dzbg$`-UUZJKa`_Z{*$rCu zga2G}o3dTHW|>+P_>c8UOm4Vk-ojaTeAg0-+<4#u-{>pGTYz(%ojZ`0e*nHo=)XZS zpp=$zi4|RBMGJDX{Db?>>fq71rX3t$122E;cJ(9elj+kBXs>3?(tq=s*PeL^<(M$8 zUl;u9e6|EP5Us-A>Lzvr+ln|?*}wt;+gUmd>%?@Wl@m%Qm{>Q0JqTcxtB`ROhd6TB z$VY<7t$^N6IC(s*Z@x2?Gi%eB8%(hYaC zKfY5M-9MeR-@5h zZ?V`qr%%FlPQlW5v_Bp^Q?^)S*%Y#Z$|{!Lpju=$s702T z(P}foXu(uuHN!cJRK*W-8=F*QlYB*zT#WI-SmQ_VYEgKw+>wHhm`ECQS`r3VKw`wi zxlcnn26L*U;F-BC9u{Csy#e%+2uD$He5?mc55)ot>1w`?lr$J zsrI^qGB@!5dglADaHlvWto@|S>kF5>#i#hCNXbp*ZkO$*%P-Sjf3Vc+tuFaJ-^|Ou zW8=}1TOlafUitnrTA2D0<3}&zZz^%y5+t2`Tk`vBI93FqU`W!zY;M%AUoN1V1-I2I zPTVFqaw3Pr-`5HcEFWuD?!8Ybw)Y>g7c0tt=soTHiEBxlY;RlQ`iYY-qdd94zWjyD zFcskM^S{_!E?f3mEh9waR7tb6G&yl%GW%e&Sc5i;y@N)U5ZFLcAsma^K?Cg^%d{PO z=SHQq4a|l`AakzEY;A{n6Rn1u`7v~#ufV*6GZ$`Ef)d2%6apsU6^>QJl0@U& zq|wIBlBAgf0j!YaozAgmhAy0uy;AjRA2%(!`#&e>`V` zg`MfSf5gWvJY#?8%&|`Aj0<@aZ;-q#tCx=-zkGE|_C4)TqKjr-SE6po?cX?Z^B%62 zdA!75;$my<*q)n@eB<^dfFGwRaWB25UL#~PNEV>F^c+e2Be*Df(-rIVBJo2o*an$1*1 zD$bsUC-BvObdmkKlhW<59G9{d=@bAu8a05VWCO=@_~oP=G3SmO91AK_F`#5 zwXLRVay<~JYok|rdQM-~C?dcq?Yfz_*)fIte zkE_g4CeLj1oza=9zH!s!4k%H@-n{6aB&Z;Cs8MK?#Jxl`?wD>^{fTL&eQHAQFtJ_% zNEfs|gGYh+39S{-@#MrPA!XpgWD;NLlne0-Vey1n0?=ww18{L)7G|$1kjI(sjs z@|alUMcx*04*>=BWHv_W-t=rCAy0q6&*;kW&ImkwWTe$lzHJRZJ{-{ zl-mK6+j}V`wobm^^B&2Tl?1r=yWbz;v-F<#y!(CT?-4K(($wWtmD631MN9?trDG zMI7;9U7|UsC;urLP%eH1h%U`LJxT3oM4=gpi%X@lpVR9N6Q(uhJ00RWXeL-Z*V(O8 zsIyyVUvf=RXLBKX`!peifjIMvMs1YT0n$0*B;K^yZf&HN8$N%e=EgOejqihLPBT|< zs)z`nNU}BOdT7wYLy}R10eXUksn9o)jG)&=qteGc|XNI~h5R6UBfaPeIHbA32@*>orZsCB4`Q79}A=z@najfekt-_eTg7a}Mcas^D1ELlN6(y28c{ur|tmueFvIDOQxXs1)_lKrA`L2-^^VNC#miFvO%l6w5uK2bFyu?hyNLCjTCNRRVW^i+GX``giwc&TpV~OHu(yN&o)r2$K$1kjh@>iP z^&`?sCk#?xdFX+ilAb(;I7<$BQ#6j*jKsu%LEhQKe=>ki^ZICepr3#_2#pE`32i4Z zu%eXsgL)3x3Q-^OPPRhm<^!TEPoek6?O^j+qLQ*~#TBw4Aq~M2>U{>{jfojVPADAi zurKpW{7Ii5yqy6_1iXw3$aa!GLn|$~cnvQnv7{LMIFn!&d6K=3kH8+e90Zq5K%6YfdLv}ZdQmTk7SZ7}>rJ9TW)6>NY{uEZ zY^9PI1UqUFm|h0Vqe60Ny=wCFBtKb zXtqOa3M?2OEN=zDX7z}2$Y{2@WJjr?N`auMDVG9kSH~FjfJRNfsR@yJQp4cQ8zaFkT4>5XQqSVt5c}`-A#Z=3-_mGZ^)Hqayei zhJ}wgZ5UDln%)!;Wz@u=m(6C_P@r9*IMPe7Db`CSqad3ky-5-EcG=*v8J&{RtLJ(E zw2h-ghGYcDtqj4Z^nU7ChgEXO0kox=oGaY;0EPqeW89T6htbZg4z!uU1hi;omVj+3 z0B%$+k$`oH5*SeoG`Ay&BAA%nAUjQxsMlNdq8%;SbEAPVC#qm!r7j75W=A)&a6)3% zdQq$fCN;@RqI!KPfl9l=vmBFSFpD1cAxb@~K-$ZIlIL3W}?#3+|2p{|vZVq`YA zMbx|Xl57kJVwoetAo+opiewCkCIO=uBLEaG+!0U$MRdReNsx>+PIJWN6dW)pfeZ(u zQ8ei-Ht69)ZV`qv=vmorhOkF)Squ;)8AUfh<7A_xI8FGHMRW>~%o`1Wt3|8IMrM%& z8)|@=#ssro9=f9HtN0F#O085{Bf6PJnurfzS_yg?qqszmnQIYDP{N=xqPfvl;VNsK^qpoy2&App~Fe(MB7KCI)$p1!&YEB&%$9gTk zmvlt?t7!>_paNt_fYJvw^~LCqX{4opLy!n)md7}<_s?`gytfSAdoScQWTy&Tbr&~( zg9myGVv)l|4-umFBL0)Y(d}Rvt11)(O4ij#zeao~K$vh~JDn0_@3RjP2M0|79T&9+ z?>Vx&M30Sb15&<{RtpeYUf|n7n5GHyc+-FtA=7H$p6Mh=&M0O!so)tze7#WT>pp|x zfWae>0++DfscU2%>|@oiCQj+6O827)1}KsN^a>NSI*4?#ylfG-{q?3MMXX$dUH^S6Ni=Ve1d0(janpz@WqGJ?cG&sewpq294Qa zL{huwuoARdt5F4Dbh#?<2ruzSS{VeDAOtY+52t^xJW=!(0f3P&G3Cs^%~Q~~Wq{YA z!QrEk#>oXK{sc&Z7VB1_>fA1^#YyU1Ff<^9G(!V0!JW`n@EDdj$$2SVK6*7$!BvXP zmAC;h-W75(Nnzpro3CE9eV=~Lp7yS(vXnk@$g3{R`!(UG013==W*Hj{-*F!ujl+np%IX?E0*I&-K^u zY1z1I!`iOu+Ll`UtL|F6Vb?~vk=x9w6}eE^*<)O?pZQ#8YKE#b($x>w$3E*F0Kfk zfnyCo#zOpX1(P2yeHG@fP7}}~GB|&S27%6=@G^V=rmeTB$(w9rC6J@uQmcAMq zQ=Ce?Z0RkF_gu30<;5#jEW32il2?}$-6PZ?au16Y)?kUFy3L?ia1A@%S3G-M`{qn8 ze+|6jh0vqfkhdSb0MvIr!;;*AL}QX^gkc+q0RJ4i9IyOo+qAyHblI+$VuZ3UT7&iIG7640a)fe&>NOVU@xZ*YE`oy!JGMY%j}bGq!= z`R5xY(8TK&AH4b6WoKCo>lPh6vbfu1yYy02g^t9bDbexN!A`*$M5`u&}WqF?+*m?ZoW85&MFmXqQ1J{i;_Oz>3*#0?lWa zf?{tv`_JzP7D3x2gX&ICRn(aR$#>;ciH#pO?<*}!<}cYh_r{hb6*kkXSteV>l9n6i zwx63=u%!9MdE>@2X)3$YXh=DuRh~mN2bQFEH&_nHWfU{q+4=t07pt+Jfj90Or;6JX{BCQrE8bZe&wi3fwEXHRp zz8{VAmxsWU)3nT;;77X7@GCm7_fL1p_xKEG&6G~luO;Bc3ZIa?2b(*uH7qJ!es71c z{Buj4(;Jds$o78u<3df_2~DLq`e9*$SGmrR9p2OoVB5Q(KL3M{1>eq+;+lHK9N?xvyBPHni<#j$sZK{QrKEcdR9+eQD0V? zGPaq!#<-c#a>t4bt+R#Hu_|}dlIGeve@SR!d((u)Ga45+BuhHfA88G0cPrw>>(`ID zZ;aIyn|qmhuDXBthoW{J(WN+`Yud=y(wvd0rm&1*4>6?#8&)Fz z&@V=a0w4)F{^!&W_l6<5xg|-0F!~>aCALbeVsZTd*)M*^tr*!)O8w)mzKThWyQW@X zw%BFs5_@CIic5EPcTJu8=CmynV;``)3}gJ`Vl#VY_3Yib@P-KvBk_%!9OVu#8tG|Nc4I~A>8ch-~X%M@!>yk~ERI|QEcwzgI66IaaY>gx0~lm<@f z5-k^OY#SGC80Yr-tDRP(-FEJ{@_4LHsGJ=)PKZ@`eW75-r0ylN%0Q>&*M;@uZLdJ$ z)rw7Dt5ajr;P;~1P>jID!><(7R;w|Yf}qI&8klT?1dTfc@us5mKEe;qw;YKR(cp-D z6NmUMP8x7cM%~ytE@l*Mp^oN*mCF`gRNhw3gpO1PVi_^JzCJo>#mX(q+iJ(Ts$5=! z13b45gILEULS!=)SmZ{qsC1)$8-4eADGR?v z>~4k_SvdvPHAC}=4(!I^OLgQ@9EMDE7d$PvJbi+K%-HTh`P0#Ea|Jm6zj> z?R)(YWtZoIRx>AqzlG1UjT@6ba>yE z{Wf<5moh^-hu;ptAtPG}`h$4PWcOn>vy`#bH#Ss>OoAEE1gIbQwH#eG8+RHG0~TJ$ z>`C`c7KyM^gqsVNDXxT|1s;nTR&cCg6kd<-msrdE5Ofk=1BGDMlP2!93%0c@rg~4` zq)UFVW%s|`xb>;aR@L^*D>nkSLGNmM?cv)WzHZy3*>+*xAJSX;>))*XRT0r9<#zIpug(}{rSC9T$42@gb zy8eb6)~}wl<=or)2L}4T{vum>-g)QaKjtnp5fyd^;|BxHtx~2W^YbKq1HfB7@>Hw@U5)?b^H=uNOpli?w6O#~V`eG;`irLcC(&Uxz`L_Cl zS8r24e*U71o@dV6Soupo-}Ttu*Dk&EwY`h4KdY-k55DSqR&o7nufO)%>%s-Es^5Q_ z60#cReEy=$4|nW)bLh=|4bxW4j}A?qOle+wjn88oAeYb~!eA+EQ;8Ggp-UldAt$3M z7*E590amz>YB9L(z?Xx&?I37XYw?Os-t+05x6Z4vkzBE6-hrbB=GAB?p{DQXV4CKg zls@_wh*&XC<3R(CEZxg8*Y(6a>cIOq9Nss7{=UQ7Nv%O_WxSyBqnH{@(<>A&2on@z zn57W4Dh*E)o#rJ2#tyxV2;C5#rl8%%As$4qB=IbMt-z|jnWi>>7Ymq37;AW!6Y4nx z1Ogx#!WVdA92mEipgUxzy_?ddg|x)KOCyK)P5v@usc;0sN3{=0slt4CuwaxK@20eO zhdp~Z8iJ7GWrkq_-X`~(eBpthn9|`tZEUCIGiFpJjjxPVE9I)#z3Q$3tw`a69qxjuf+~ z*?v>d5~pcH-AQ~0)8PyIjumD^?SM8!Wb>KZoD7hOlc2nA0_(eG!in>}Ru}>6)>5 z@*}T`Hw{I^-?PS9>(#UFBQpW72* zsfj(2+_9@5x+57aN!`e`f(Mp_I(D>}p8)@&g^g+X1%d{ z%X5boE?hEoj0CiwTh9)#8^?~;|wgor_=Z1BI9_dI{ z&t*f95n?ZgZ5CnQa!v(p|JT?y0%KKgi`Smi9k5r!+!Mkz=&Z$%CFl;?AOzV`YBKrY z0#Y6~J6&dA=m>T@TYb8ukaV4z^Z?VX*MCKcp13-ye1*`gAj_Tm@r{fpm?K!U@Xg2AfndEo6jZN} z=XK0GRNXVLW2c?}B)rH^yR>u}b?|p(W$!TkQTAgu1AIG>MFfNchMQB_^-AQxRE$Th5-E_tBP@v(Cy|ojjP5LEU|JrM8 zVF5;$>Hl^jlHWDPChrTH(vh%bARyj5#TPb>omAs-)4zN z9?9(wybd0$Z5s+}Fiytv}-8U`IC<{6U2_NqEAkv;7lys5Qcq3EKt z0-!^Xy3idllgZ~qX^QTe=i*oGUCJNk>Y26?+9U(Ks|C81S{-v+6ebc`c(yibQbuB% zxM7mk>}dI-TfUi5Jqdu6b`4SqF)y5humuCaHhssdcR(jKf5ZGprx;Oe7VG#G6TA1+ z8oZLl<+ey(L+$Qsck^4fi{I|)p15MX73gHFUU!l${lN{)Ht_Wb%j#UE6cZ9}Wq^>+1wz z9TBA@%f~tby^0YWafmn&8Ppjn1Ng{d;S01WImtMzV<`!zU7;+8e-Xko>qM^OfOZ`Y zEZG#vcm>EGF??&G6+v(3l`X(xMn8ESv=@LdMfdcxFi%g1?0HDPG>blldR`OLlWN80 zz<$t+MM9%1K~JT@#aBZjOu9*G{W$u7cqTM|&a1)0wR8R^*r$<&AhuCq1Z{-aUhc5P zdyaaK{$P=Y6R{40FrWmLbDOCijqB(1PrKlnL)Tm|t=l}toVLAZOXJ*~-dx|_A&o65 zskcpT@bs+d@ia`f)t8ivl{(t%H?O?;=^s3O^GXqopx7E3kz06f^UQq<>gyNmo4Ij; zrOxuzn{WOqP75~PwPXC;3mZ#YW1xy&DEXsl~)u4`-v_{*B%R6xNH3* zJElz8@d#i4`#JV(ko%x;u{LMqLEEDmwD*(ccB9Wp;u*9I?=sC7g>%L{%$4m#zhbjm z)gK{LWQvE1>_yl|4T$nYKNVZ<)vza7FKU5*W~4)KNgN@;SA<9&ERxIfA&UZnB=r%N z5YD4fY$9Mkzy}!G+`KUy>3l(FSi1 zw)t)*w$E4#ZSxfm3cZLC(o3aQQ7uHk>_@fMTHoM0=quh%mfN6%{`O($pyzg0kPf=2 zjA%M7bRl4BhV5{{d4HbnTh`HM&YKw@N~47e7NFGr*9Yzi(7XQl-FJb4hPEKOC!K2x$nWy>8=PJYE)T$=Cqe(n*ChZE zklF{Ms}h0Jd|@o;Gz(~b;9d&c#0O^j{1?tF5dtMj9dG`|j0qZi^aF1r{<7KC5hZ`E zNX2nxJYEr@>u86|tPjTDet;fLn1R+IOm6&3b*}TOyNpIaid@W9c9!jIfiJOgK-aw=xb5Kpb)`E9x%CU82 zEQg_v`e+tWYClJHl=_EsSW?LZO3)o#ox(#2UW9|V7I8fYnz5fRtph`u)dywWL9}UV z*hdU9-BBK5G&}j~O6&dSdWDIpFX;&Or5wNbm^Y+A-x6(K$$Of6JTVl9n0gFY&=T5p zZX?pCxA&w{J)eDSfb?Zh*LT#AdiPlB;A%p|-`Aw6RP2mYTh zLmL~zM^VS0V@*4LkOEG~nQR)HyRB+;*KWli%QqKt&%16HWyMXRhtwdCgyoTm*5#itgp(Wap66 zyr-dgKgjl&t?JLMuw}!Boz)TOa2|37p^FAcPmxX0apWmfp$B1WF_@-dsK+?1F6~yY zEwi!-))Q_CbOP%?p%bx|=d^nLBig-_$e!nh19^Ps`s{SNq{nnW)V-qnz3y+Ipd7HS zsb}z%!+}y8izoy>Nyyj4m_br&8TGFcze#gP4?v*NEdl zzGBLM4qpvdu;5vCFi9^zXU;sW`>pPi|NFD# ze=$xI@7q9B4WPsw4CAO~UJ(S)s@u41E>#9D>!?=*N5m$%^0E` z<0RjkAj02TN9RLX3Js+GArg=Nu>E5z zPa!vMuMV06#7$1dLbwv+VGT(5V_&A~Uy3T^+|y~Q2>lA|=hZZ)ex%G`rhkN54C5gq z>w?qN=A+LgB0-@s{OJs7Da|z%dK)uDH4?m5Y=K(N5KWL)uqDxwBt>QmOk(h~1u6_s z>9x>G_+@bJhBQ;(Rr?20>Tjn}^Y`|rQvI3Ua5$aGq{HFf4BhwAFVk2oHNbk)hmAri zjQ_!g*-c^AKM>A@je&H)i1PsJ5929F<8bLXvONK4;-n6d;Zm7Q=G|k6Fp*AY!b1a`eoS*c zF413z6`x;!NZV1k5)sv;-Dqjt?t&|JLNGSA2yWhU-RYC^oiWI1+idw;6*>m1&Io`^iPgF6c$sN zw9j3KFYs@%*HNz1Jr?F^RiLV%@DyQ^Dnc1h&59pWKhD#AMQV~3k7}>c@gdw=dyRf5 zHGNU7bA_hHWUnI-9SXtjM~LT>U5!uS#{ zKSOhB>l^nUa&S8kEFoAUIDG}(Lr#|uJCGb%29Xr>1S4yk0d)9hoJ7#4xNbi?5Dt?N zBp45evje1L)A;&Smy9J8MJe@1#HwBFoYPv$=k%GOaq!kd58)tzBI~EkGG3Rqy>GOTce-p>jH0rb~c(K z1|9q=$3)Vdgcwyvy&>S3p(f~O;~?XK{)Kch&2!gs=%kNH#-Ee-i}S+a@DNWR(Xnv< zv7kIUUD(c?RS|JmPeXBC6cbxUl6qRxl;fFAiK%!>EzFa zJ$-mz?G%WqC+P-l!DLX&nfxzGAnLaFsOg^Vq~gaW2QQ<(qixj#J=;Y{m`?kHkfO)i zdxQ*`2Jr3iXdj4QE%|AlQ;|Wx~pKrr7xuNnTe=t-AO)iha6xDYpH}>yZ z+FD^H2VS0x4us;Wo_95^kElZ$>j2HW@wyeLi3i%Q28NXxQT7V1{iHY}Llc~!Dkv8* zM><6X$}-pv0N#?+N%W`5%}K0Is%8kCOC~LuR6+;gtHYPi9=dqUoin~Q^MhE;TSIe$6dEI=Xs(`oTlj_C-3c4KT+wJvpu4Kkn_RZVg5jE+RF`XNx?0xmaV~bW?v}wVTXn4{5 zO&2X+*pF%!%qu@3SLRk-npU5?`f_cV9;|pa#ktlD9VuvRx;TK+fWUv_$vC8-@TcO4 zN_-D6?7|-4!VWMEgQ}TUe(c3w4{eyxe8C5t7pS0MFe;X@U&B?sVDIGR;u>?mPyb2F zV5WLiQ2mX&1v=E#B`oe9yk4Y2^CFRk8*rV6k1!uW{m47&7E!m%(ANz&+ixrB^ng(;#RLHnX%tfsjJWM- zyBo5Of=eNl8*;gm`ozE0weGdP7~Iz5$$pI`$C5 z`U46T|8cnpt;J+VO?%~H_`Ph??bcn%Jzu`2`z~tc^PoA?r znJlfFuxIeRC?a>J?C!EC2Bn;dnhn3XeZ}sbjb-10*a7A?aS00$P{m0wm zO_v_`nJOwO*k6S$tHR@xmt`N`;fR%l>^^ZvbfRm}PUBtryK5pTwRdIZgj<#_irORP zr7I?yj7m&+KkD(;PKtLXmF-s9=>`j_AFjI$YN7_w1g7hD(md1~ysZj9;u_Y4i3Ssz zgRH~g_UH9AHR4A!67Z@2zch=Odh*4WzWc2=ekK0-ueW&=xy{z7Gz9CSbv}Pk+4ST# z#ZxnW&!Z1tS0A}`@LT_*wh{sv=f-Dy+2cPoUi{nzYTGjx)eit9s#G5^D0+(|iNBlJ zV$vUX35MrZ8K19VAN|i75_}Z#DO`R~MZQy~2$6gqOvN0Js%d70SzJm|ER&Jy5k>-I z!fh9^fC*zr22w0EG6&Uqo`eqC7_L8gi(#?!A>;y86ak0F7|oHQIhmW!15hHkZ(*|o zF+vd5r!A(imA-b0}qc4-&FS58}j>!?PW$SEg*;W8H~a^e%b?2`O8 z*`i%!x17FmIo=X;^83K2Y3Hja(b_rMns6%ts^>=(bA-9V<9O1I>564?R3a}v1yYtH z*l6T7AY0T66-95WtZgaP8(}|MBGlfNdh@=~Y1m!IA7($BPUtE`qT@h@;M3Hd z;_dtQw^?1x7-WaPK4XDxuqd5+qVz|PQlALGw|x}&MFa4RtVSK`(e|RtFN=u%s&M?) z7+HD3$diG_iYZuX{0ijc(*2C7cTX)p*3LRRtn3r@wq>%<@A9jY)yX*dv zSq7pIH0)jCA$)wa^7RfPVlWXzzoH}vzHmu4?W&f|zEC#fi<;dYS!Z*G+=!O(wLx7} zkfS~!6{@R-(Uw86L(mJl7`6&&tfKDx<)c+WIlqL)3pSX=7*`N5ysyr`8ap$bd^E3w89)ZgPiCBi|f{Ji^U)|AMCk%95n_gVk3|_XmE_Z6(keo8NCgI|@0sfZs3_s1} z$KK|ZCF;AE#cQiOrv*z^HWTBHM`H8Hwdx20FDq8lu^{(Q!@5s%Urrmi_ZX=7)j%7* z2x#|wO+pMI^e#2DpLkU+erWUorFxiNlu1s>XIg^5wIEm|joek2Rd2IsPtNkBRLQTFsnoh4v_<(`f@uV0I_G*I9RD+?L~j{1bx`#0ta zEeZiTNBzhh^|GEN+1vl7{w)Wm!`yhLKAuC&Ve`GhjRo0c|E^`tZXfkQW;&_kBLS|M z7!XYb?!E&&=u`h5Ld{_dyivFMQHW{aI!yVS7oS=ttZ_4U4sb{P=wmO6wCrO3g8Cir zRxN0ht{}^=kNOy`2fdgiLzr_8?$^fWMSdbcHb<)&+4+$`i%$>mB*aF7fv0tiFWhcK zRThLy0Mtx?A6Q34Vn$tJOcHkv?-ldg8_%9Jr8YX#=C;}%u*pWq^?L5VVi61EUkC^@ zTi3LAgna%bC9aB?Qos0?XlUZtnp9cISx)1AbGeO~JGb1<*DpHId@iRrT4e7+!$h07 zWDZ4FAXQ;*hdB%9)8U`#Aq1XW1`G)sm$Ol@ZCv2#2r5~I^BXuYJm%NgOkCQOAufat z)Mo2&C`TDc7EDz1sE;V{`=Bx<#5gYrDb+@@FE3>Yx=pZB79-7UjD-g%Z#qc&td6cl zI`S1u2Q2b!m^1LOg{LEV_eV*@cFW|i{!+a94itA#8 z2;?I%3?C8LQn5B+Ac|?$1Ejde^`AH_B}3`>#H=np*@XDR^y^=fZDd~Fz;wS>e@!M7JaPvv zPU?=U|2$6iw_+;&j{0oiARgl1!2p}_PMTg!Yxs?H%{HmJgU62_ghA}_;}{7x*brZc z@>!rSz|M}1YPdKizI;?B3~2O%LY`8A1SF;-m z+Oxu{+PYOU-V9O}bVd$T!;AU2M<2*KtciMEC29!H9V-u9ZUJ$M-4#Nb$5QVy@LP8HyfiyK->WR(e1g77J;isq@ zxu$>@C(@*mf}RY@L8hJXBrWMOEKDqt3i8iwFSwpR$W>G_j=iMN>(!1>S7GdmXt%UH zpfdn%XxP3S<>d1=1{yBn9c@?(YZkyNN1 zQx^M4-32#mo8SKR;r8t_CV3=RwbSNzS!Jbd%GS0L=qT*0!ERw05x~DzSsUKHYQ||Y zuwKD!+2nux!l3~g>0-F=;qnW{w$F|jqXuhZz#N`4WtzLDj_MYvu(*X@fb3G;s!oPE z?QMW|e7J7#=?C#3QWQRp-~(1;_=?J(Y^}oNmHRoN$^y4Pv2Z8cL)EmwWVNJh@>2ER z)el6y-IQ`!2h2{kx3}jwTf$_!N75)(mi|n=?Ylj_>QzqjfMiO67Wc4{rOcF4JS+{j z&z%duf1`r(U@ZlI{F=sZFnCGJv}cN<(cA|5AP8m+HUK z@vG9%#_zOu)ChxFSxmKsBSSO9XX%g4SU79e4=G!|Cgo(;VeA8dsRxIZ$Eqhj(brh0 z>Jh)P2`<<#u_i^?L>%2jxXAxZX%?<7l073C+~1p!t{Dj_9ZxL$sz|_G{C#{Hv@t=B zP}EsMr62u$;U#=d%MRJHCiNv=5OI3(_o-A=G_9B~AsrRui@pzUDE@tHg#6PmWEuT^ ziPt|@8=kjTNmkqdOlyJS!m{E9I87hqn;%9rT0<0-L99QeURoyK-&OxH^mcao3^t~WeS^K zH`XC|VCLo6*duA78O!ugN@5Elxkhd!CmdSX&*f=utfmDFD9PkBHMk3&aFB&)R8NL4 zD&i)OQLO z(Z_o2Zs~o#^$zu`{XU~$I{T&vAH3;ofJ*ZpJ&JR~s{J0}8cw}`t#a3NvWA?#tMY67 zLG}{Q{#6^CipQ$*V2|W$g2v->Y9+4=(K+K`;I4$BFUb9!Nrk0B*fL+v z_lcdO1uEs@|8I@xoKCB{68@q=)}90JCVF33Lb?M@bC5mog<2~vPXXzk7B$|75Lya& zL)t=%E&Pk`S-PznN<)4iAI;NU!@f0_V&wOND{4!~b@1&pAN$Goqzvq>;o=lr=43Xx{tUtEaN3B>CWZ)Uac%%Y9--wFCA~Ek7aAC_APm}b zpXAnlNOIF+;t%pPlAxIkvv1neXa8*XxNLX6ZDDR(+U5bi-=^>US$+3TyUFaf{gSPI z&A@*!TUbRQ-p-3$KUDc=Hp9j|c+t%)Z{KNid2DyGia&p6lgtpOkDeM{Qy=)H&22V` zFBRKM=Etf98a&;o2pD`R2ctkyWxz`aTDZXBjY52aOspy*2=?xDIZi>&&))8y?Pe*( zt;DkFm|`@cFI!Kx=wFn7fh&cqy-f1RZb2KRCK7JNBsApYHWk=M5J&|wBQOdb+2_^g z*;b(s3o^wX$sWZHhUhNh^+UU2+hPaWw)eN~kHy66akHOp4#cDm_4zDetK1Mqx+sR1`nMz9wwQP*hL>=&Kei3+FtV>|yg%{T(6f`N5BR!MdXj8xHG^3) zqCJiEswQF>ZLP}3Hs3ciKciD63}0Z^MFL6+`V473sGm^=U1^Mx3`Y|Mrl>H0pEcT6 zg^H5MH*WeRUNMs9VN5fcZQ=>}GHBs};LS}+P-y~P#IlYJ0P8ym@R(0L;jYe*1D4ll zwDy~vES0HtyCCI2411OeiC>SA#1wX;8DRXzVihdy^T9BjrZUmN_=b)~n*!R4%Wps~ zkbFH!%W;I*pJZ#8%)c_#RUtKlOksrV!Y3i%vh>?b076sjL-)-NtH_t7E8;OBZOPa@ zAofQ3jdT&<%k!kzaG)7qW3j4HcvQe1&&jd+f8}J3!f+>UDx7H_B8^6hA&r*!PDQ-B za5jys`+BVIUd>7lmgi)Y&fyh!`yosPQAwyIh?7D-h2#b7);pTpdfDrCm->#&W_JPe zRvi?=>OgitOs_62y`!|JbhXf5STOdjJDPjj*#EK7D|Q>bl1&L=hPkN@2)(QE#vP@l zt9uJeTG&n{WG78N)aYu19%#`y%8i44oVsSwNLRxgR6hF`tsw;8VRy)COB4`B4i4SsLAa4`Y(WRazi3X`Vv!fMiDilJX?r1a{9%U3-*f6J-iKJh{i^La~ z$yJ?ASG(MP>=IKImh$g9bD7xJqR}YghlfIHszUwEmoF2yQ`Xet0HgZCGNmYge2TvH z+d^IF=q3{GD`-m8K+R-7AdPA64e{l|c4AofbmD)4hUvwM1bw^%@mXLok{H%R#q;qz z+gU3h@JZH-G^8$-2?T_&a!E51(fhSa5Q$w^j>=mA9b7)O1^G1VKyM1v8fOAgDLfFwlSN7aDkBbh=1Vofi; z{_|sQ`!zOY>fWC264~Y0Y;ZbE!j3Cqv4wlfV?E8SiTe3tr;ceTaXo*JV!Oufp0KT} z!>xB&7aARQo9It=F0Wa;$5j)X(=fKBtv5LhYKFC6eJA)BwZ>zny85O7zI6@a-&ln8 zLF2LorHz$i{9dO!8mb#Jp?&t4L$8*9&!)KTkLxQVHBP8FA!bZwX zC$1xtlqa{pU|8*e#v_V+#E4OT zjwi(7(vGZ$V!mG>tD`=FtRvSqWZ9$*B?GPmVd1ek!0@{$s=gg&_gx>I&W_E$e<7Y+ z5K(_sDS$qH^8rKPSita&*B->#;u88_rMf;Axsguitwh`|=XF8(EVlU^L*PKbu#TN~ zwj8|9X*SENE}$egSAG|3#!^5By}_`$$?RM3+{=QMMid7b`V01GIvvI+&E63R2wQNp zn}sc$*2c&2oUL%!tO4~7wk4n)tpFT)D3<_3R0r=|=}&0KCf!VqIpm|jC(z<~qb-#Q zZxk@2wJZtt%hiN1;J9w_Hzt9B+S-HzVkb8@NIl-+0XLm`=_dDWyDqXB zn&w}0*`hmpYVLH;R9>jKpbgr%Tssmku7 zB4?i;DJ=yE$6)n>a-tiWd=_(RksK=Y6Abz5;b5mLI|>)(FA9o zGzACes-Q@1Vend}5C)iY7*G)}1M%Udge?eW(1HnSXri;yq(~2bXQq`x;Yrz#0k&ke zS%JGlk~lDWC_ny*-Pvc@4#dzy&@`+2PkV%% zOIv<3)+u>drFF184*~^AoZL$_J<;#J>d$8hF1HEz)8d7HT$%mI=(a%Fw_CitukY~T zzCPh-wvU#V(e-YoddEiUO$O~Gr_8a91@$Jc+rpZOpW6;!qTct6s-1GiRv51Kzn!ku z>d;8_q{~ie0yF5Z-59^#vLXATUx*cq!zD=G$XZeu&u5Te*HqWE4IIDJ=3 z;X=s*MnE=AeJ9|E8#P5YEW>Y3>i7+gy{D`72zWgEJ6_;p$$k1u>hqEMJ4WhXT+1`J z2UoHdw1-mEKE?MEYBN#+HGKNk5c-SiJgPNDBrxIO3hq2zQ?Q-Gzn`%I_?VYp&dv2M zvIvf0jiNBnpf1lm=3_A6ApuPS)>4!*8O26GMgpxwaM6T-up7}x$fShgk;qe5v^RIo z>TaB#z4r{2{wUbivuj#sL%^MIIAif88=Zo8VO`(VhtJ#lK)G7`AVbhecjuza-rrB| zo4s>x>$20;IoY}UyhY=kM#Bz+WZSjeUwYHVtw){{#_rt79ybJJr`6`3xa`^N&f)n! zT=yimh90T==dW``)l)vNIle^QUoEWPPd=w1q+I0(zj?aa4;5EaZaQsy5FJ4LeF}5{ z$zg##sP#GwKG2!Ph}IYe2=jqBViZeEZy;=DiXR5O3_2O25Y~Q9y=cg)D}9l1=&&Xw&3l?g{8))$`(k@{a1p3a{ens7utuI^2=vshxrlD-kY-br`D+hAM=))3(PZ zpyB3*357l{^D%K-(OTUkjEoJ4X>x<^UfmPAA7hlXG?QgK21ybCZk1lxS0Sifv<291 zEjcA#Q%-#E!a(4PJtQIWk)#atL{s*GU*JZt07Zc#S!1%fwV7fXkwZu$LI=?Jii9b& z9N7&))d3Vh8fPHy4GD@Ijl7yD&?%NGuJ_OccYXkIaDN7{Ux?ntALbeUyb?sbz03s# zLfJD@r)GcJGkZS!PFErpG3low5RJ#jCL63{qLHqyaMc*AVNejQp_b+{ucvHN$a_^~ zK+n|6Qz^l#n5WiWi;#UEURyWC?C}74{5m0i9bm^jS=(82np)-?!p5j&Hj8-6#y5q$ z-cZx{GVhaJT^!E3OK(B$?9)Oq;h*nmgonr@l}$~5ny#*74^BUz-dtT@>WZ;S_3r_} zQNaQi9BKB}jHzND-dA1Yeacj3_qnU%q4vw$L-Baogt=3ig3Ri*h;4T_HQn8u6~D8% zu3dIGR>z7KUO$}07IDA zm>ULZ#zLtQpB=zl`Xly=k@2w#_&57?*Xi!kJ;wQT>Y(diU_s7c9> zJt9NLo6(QTdY?<&%(7s~gGuhxX6Ia@TxNd)1c%NSn z1vg!?!9F%t+BbteRT}T^ikFtgySn40Y{9CQ#s-^l6%*Z|a#r=PT|QRt>uzZ1KDuU2 z_UG&)_39e07-r|Hmy8d@CawADtYBN~ud`dnC6l4WwkC7cwB?%@#G0C73m(O(B@{A= zKYo4MwAZI+m;dFW_8z_0tM6&w{t;apJRSqCB|8-3|G^xy4{cteem4EFg?KyO^H>jM zvPiWhJ7a++c1XQBBKT_Aev;X1adZCx?O6i7i}=MPVM!{DFhM1no>Vgi=FJObSSzE4 z!cz06q4?jt9&?tl`>Ym||8Lbn@fQ|L_G8v#F`IpVs|l!&x&>B}_z$1B(XGyIsHAWY znA8qOJ=@^)4xPoaU-h^g^}_jK@kTQ7$?aFf|5I6D)sIC2%qiC(coF8shYu$ie*)ue ze%G2{U`NRIn<&=&^cNmI;H`MZjd~?#3I1s@KF{obqiu%g9@l{o^DS=Z{*u!j)-EktzHk%L~ zUeueNeuutfbuxAHnCfe9zB#!P8?xVF){CM-QK}``94{Bxq4Q=lI*@*(t$ z0*llTSuC3*FY_i0Esz=DU(#!`f?@wi{if=Z>r@~3asMrB8H6RvvkTcW)vbP8ZeWX4 zzxps+&i<@^TXl<*)K}C$u*vFs=c>O<uva_OepgZ3^mp(p%~u)K{5Z{k!@f>W^5N zctHJ;`gb-C%!>u<(kED#4A{XPx$+SHa}?%+(O6P8P)JhxL-2PKS-#1p!TbB=d;5nL zMMOs=yP`{Yvn%^wn}ki9e$C!VtI_NeVz`$Lz%L_RchA@F7J^6AM{gFM+M7MOSKOPu ztXH`F#C^w(VO);r;56Hd1-i|6n#b*T>ceqoYd9adu&Oc+x`?PF5k{oi7$_HEV@K2z zymA4)N+`DI{|3bN<-4D@&N)YxIVoqR5q@8N=Kc5COtz?XZfomYb%y==nU^drYn>b!5Ctr?PZ$sZJGC4(Lx<*GmYK3@9};69v2?xCz*86!x1fq z9-^Oe{|eU+0lSwM-%%oRlZiDYBcsgabpN8BFSM>vThx{{TLd#395z2-=dkJ; zUPumj_0A`QOXa%S$dG#HKaV)PHrXJUqTZlMEURp*D&K#c?PX)`>TojQ>yzh(U5ggE z+}3v2ww-mQmrPrgHX82`E)7LZ#9*S)OrYMVHZ2*%Ix2 z-f6n^R()lg_{@W9puD-%bs!$vZY>)VYBn{#u=iUtgZ1U*4oibOw!C4kr;~&cIo+d? zul5rmlh}%uY=)i|^mJ>IyR&mweFZIu_7x~{W-C@zr5Q1cK^!y+OU~frPEZqXZ04#L0$|tY}D-NPT^J>z!>2 zLk;VdDSg7vTYSmLjc%I1lCVSm>+G7BEY6w@(XH|*G{ zSt~)o`-!M-5J4aV2N@%gOd!0FRFIBn|vW}Drt z-eWVGJOi3H9hf$!nudR8+Nmhg011-@!@NC3DA2QVhVsnWtq@_vVUsn7Lgo{)!})lf zHnxUxXX|Z}q6~&9Cutz=WXN1iJCP;&D8)pBPR#N=xfBTp2pd7-lFF5XXBc!;f}%nR z1Ca6zjC^CAo!5Zpsbiu(lgpE2dZaZQmR3Pl1Nu#$p&}HOO1KhD0hr0cDxiUoC%PDR zz2y;b(?1FUenyXAUfrc`fgeIi%?Q>s#3O>1`S`d7)!ab-ztxcdp zi(oNgfzqrSy+Qa-h~$kCFl>tV#u zT0yo>Sj8|%X=Z5eLYl_j3H$wFA3GlQ`NIC8!J3ZtWgQ*Tf>iySj%6K(I%;b=*zAUs z@a=8sq4nu=XBezD!_2jBtet7FSqQn zIF@m`p^X#2_+Y@)f(;Nc7NdxOl%T-$NRFKpzZ*Diiyv-9$byI~Y_VA7@fF$z4H|Dx5g*3@-my-zW{NS^+s=4LU=S;5ULvFYRU7E$thNp8*A(h3CX5s zqQ~5@=c+ot#VX*Ndavjg1ef4*RI#r4+51F`-Xy>#L9~eMYl6w8mrb%>5bZT?ljVD6 ztEdNv0*uOqR@o*xU>7I~%q&O{-x-#ny*Sp3}O21M?Rd(O98C84<|F{P!iYQi+&Y*nsLu5^Ihu$V)k)=GECZL$l#xZCMb z%xz~?w@;eYGR~3+M_}0ce(?P zl902^TxqD4$DQx-Ouql3YC)>Mv?0+^0b7X9MdejK@03cTh{%+U%}ktHqQF-^C6`xw zO``FD0}P~L0z_&PDjancf@m?ZGR0TUYN{lM-RfudpltLzU;yJ{R+GzQ*P|q&zCuzY zP@pguLKr`*Q*oFilK?v&y$CF+j-b`jSz!_lC6mW>m+2px;ND~mcq=BCmMTz-PuXY< zOa5z2j)rQ{(LTN*&~0=Yh5whf_W+NhI=_eaPTAgjUu|FYx>|LuiX}^yT;wh{;oiU% z_p&Z@Y`}m`FN5C~v?rUXJU2@qOB4H#QH{+~N5*}@@#Jm2%V%+B2D zcW!yhdC$u$WMz8Y@Q7Sm;An!nZCaUSSuojY3}>m>9D|bq{)XtxPsx!lnpMKJ$>l0=VE#0Q${LhbVQ?(avB~M5H(A<6VIs~Hmen|XCr57cj;wDg~y7PjIZR* zau8CZLCaPfRJMsKeNi~1P;*LSAkgMF^Q=afBekooDqXYIppZJ`(kv}2%`0n&8lEg` z4=C(+1ET{^|A%kM#z zXK7m|9Wcfc3=~;>1jcJfX#rU|Ppz!j;7pMyJxd%-z##=(QTY&BIZl!@lVSAb*KE2t zsC)F&?X{LH;g7;@GHGHi9oIy36f@s3g3 zRt#I$TBG}b-9;4UrV$&5Ij9vP)Y;Np6VLT3k-c!=P<<;z&y-p^C+_T2?PjhnuA3&) zZg_w4iMx50MTey|GHd-~Qvv|JOonzEpncEx-PZbcYu(#|MF)Yep>~>mY?NK)j*MDlofYp2?IA zdWFjqQYB^@4u{F4kONMK_E=?Xxs$LThk3UpU19S{Nzmr?e_{2qb`9sV2yanqH0d@5 zKGJp8aZ;((RpJ-E(g5Ey-P)#3bab(6W+bgQb9J5E$fs<9fcfNuxIvFo=h1Dgwcy+w zPuTU(HesXi2ZPm;XEiGog3BROSUdQwi5UwQ_J3+1m1G-UYluB@01JOMr|AGf`7CDG z0ig`8Ee4)kL6qbPGy~CNdwL7bt`jNhr{b~f<0Mqx@25+$lS$DH(Vxp|&m0t?&qQTw z7?k*9V*W>p{DU=}4O&dJVTtJY(^>`^lPL~F6O|IFf&j!DWck6E9}tqnNz(gl(B;1+U04#Mx7H@PM!jr;8}`p8X5AFzRgZ z`H&lBbVagpDgs^cAL}3%1zD$XOne$PNmH;OFF;TKQt?TS2u1Xly;A5E%X>i&LS8)c z94WDnS|omqYiN=XeK3B}x+|c@HmfZ(WQ<~YG9AvJ!q|jbd#I*5WUrl&T>ys=H|eYa z=2P;fwY|sZguD`qxdX)M>uI;{{E0Cl55B`!K{}wLHeN|4VH*YnBfJf$tm5E77<2U`gq>@HG1qNC7Hcyb!M;d687pf$B(PUZ=T|xM7)L(EmRVw z;~E{-q~ZvOOr2pdE3KGuy*wmJ%9P@R0*A2yuAhIFS3E2{e{lXEPa&La>y?-W>-8zjMwKGjQ$BzcAdCp)p^-It?U!LP5Hxpchm^Keq$?$57$5a!Z+()BJRD{ z6WgCQN}23z-^iC&TytVqsnMs6p-*RQ(ixw2F8vzfP=&GB|8F?{vwhrLatNCSGk0hY z#-0-r+MT6XGIxqGf<)4vq(!0^mfU%UhXXyCkz}3fmG;0s&`8l>X!W^JfDuz9HUo@{ zuuFqpp>Uv)!psk76{RqQDF$&!v^n_ECT`}V@{zZoqC)oA7_w~`M~N|5Q|_k zJ;Up>vyh*=Kjn%>HQJW}(v6${w!9Z%lq8ZlF>@K=Ek<&|IT4DB~B~Y_O;v9%9bdID;FI$4}a;O}@l!+Yy zZ67)fU;`NEa8WOT7DH7N_&*q17&?q>qwQXMcFgOOnF<0N*-^sEWbzzvC)kr_vv+i5 zgPm2{O*$B>IAd@{>+WUK><(pc@%$Y%QkK)@5Tn}4^Ln|tOsDsh=f>O`Mru?jc?N+S zjv9?oZ;e0J6*s%IG6n*@)S#6c137i!nnDgDIU_YINmjH(${tUCloc<{sdVK)q-C~s z^SX%F!SQCb+A?8SAq-ab;ILesL&}?2F1w-0Zdb;3_7dq1y_J`mAZv20%2Kk(?Wvhm z?BgJojYahs`X@A7)HA9Qm5P}EkW30FIDr{C1ON{u z1g5dIMr=}b5GjQLE~kiOEsekhAqGW;iWew{c8QDP()f-j!!>b}0<_?aiq6~yI>*3B zi`CdXW~Cg76+JS8SL=N!|F26HjVUaAW#N(;&=GruQ@h?1{-Ra%60++(*a{-;SN={& z3m*yJzP9zU)P6F#y&<2IYIRcSWv>_H=QF%ksji&bymFkwB+s?s!OWBD?KvFpwAYaF z6HB9tl5(fq9jdFlXQI1E?Q^gHxncuVOg#lH7*|HYd$Tnnm)HD6gV_v+Ekb4 zp_-m+TC}!*?8^M?Y`$XK{JN&qk1Sq6xYYg&+mlym)o2Awb#46$jTWSN#;OI(jOptu zaCbaIeUAorw`cR3Q9bDuE~l}?)pf9WSllS}RTN5{AmKP8TP%l##64O+ z<9w~)>KD$L^#-v&PKLdn&JjL-V;0%hPd@a%E}(nDen@49b&%5#O-QsX6;-7Ym_{)3 zVl37&u%3X?ma&!7b)K&CFgV2vcWds-QvlU}1h5qyxV^(mlpUfHjzhVqKa?A?iY8<~>_=ad! zk8dO`rvOwQj>Y9oP2*Ot9wKK_hBC~WVtf!r`yU%(p%oD8e+cg4QUi%h2a{}O5}EG* zZ-HLS&Y#FkWd<|*0G}o#4taLmE^k0-iGxUlg8Xl6I@jpH*%~?tx@JuRJn#pu1 z@%_I=rNM%Y&`YFTCG|8jY9=GAaO%H4EqhwG9gJlaZKg1oi{db>rau>VdE^b)^5%>b8}?cL9itw!Y(Bor%WpI?%Pj4J{j!bwjl?n=A z?##%PqWmuA8zS)5vCxk(#bC(9jFU0xQk5C=7R7TRzMFn&JpLe}gI6mL{C!MbWW0*I zJeV8RWO=t%FK{h(m362pOLR55=AN7W`u2&T{v&qlpQUo)8&gl^+xyG^_=H+E&E8{g zDtj>Tm&AiGOuNYD{?mSBc+fDm!jX{TQ=#IZQaQll|>^G`1^D^SV zM+ZBRqk?)b(96%pKAv6kG#;Gx_9RUJOrL=Ch#REmXQRXa?RfD@|1DZPOH<>K-+Z~L-ZeSdCe_=8y zv$DFgjbD+f$Xn5p?QtF#T$_pgT|@$@QGPJGo8D>TeAt8fg6onA*w0M>p@iDdM_^a=-IIAa==ijmLcDs$P+!j}iuEj;;q_SK-hF(6t&u*(3 zU!LE)pqCz!$h##W9aWv*rYjeIUm+JxEFjgC8ezyBN-_G-vS}?09R$E(jR6BMU5U^@ z(V0P0B}3^eADjeW+@$S6T2jX+!gXXQh=c{DMBthD%*Muwk`k2(;0!J{>|O2$aekt_pC0cNlWBQj*NqU$H3%h)ui z?qoV$6o>@NL$D;;M02ATJ{}%ng;dfcXd{fw1p6fDH854f8 zL_5c+rAD;odO-?4m`z)jE@0QsIP#m%s{3yxi%G|qJ9mC592Bk*4$?J5vvrf&4==v> zL*Z%RPT^^~#-wiB-EW#fR>F=Qt#Nm25b;_CbGzR|l<+O7jV3LT3y%tNHaS?@`}o41 zF$uNZFw7Y~77Aa>jb2bAph2cqyb2hF{`0@kc^4I@JroH*5@Ck{3%HA7J ze{=QfTZrXPG(~C3e0zG=<=@}#yeD$(it9e|@}t3Eyl(l}7SBEY4FhdhBIcb^!*gCl znFlPvfq4vU4akQLkM!yPH0F@Xp4CK5WGsrIY#-Z~%66Yny0cS6LL^vZ{#CoPf547v zDOQeSMJf?e5Ldtea!LXg_#yu@^rU^*gZ%^VuaIC)(1`K^c$#TLNtk$0pons6AR0!$ zLUWQKxeJ{spst%xMbvmTKy*u_|1@&<2(Jsb3$Ne98JRk3nUx!DJ=x2tx%A513Tb^+ z6{A$>`g952ZR_y#^#BMQ;Q?NEWr8Kwqc!wGt6zh&EFKrvp{{ zN~{S=Y!iu^0Jos91XK~^De&WAO?3BQ!NF<=uyq~mg=ar(~#oOa0#k@s$PSzc6DGpZY zT%MiJKfg1}p{soS^vIIw;22}*cuMOjV++=yo`T|dD%z@Ov!(S!t0^oRsA=_x^+YR- zRun2H5=~%|fM4gQs|vMD>7n5f8#?tsN@5RaH1W^l8V#@Kb6(2f^@31PSCF5~CtaD} zHvqx#ExV!o0Lk}Jze|zj2?JMi!xC>^ZcUbx|8oD`UrHT5QaV&bC3|pDTvIB|$&v2% z6%>eP4*a&})c8hn-$b+WaF^U1-Y9%4?aZpl@s?;DwsrU3yUt6`1&HKhr(r4L3qt&ZY~Ue$d;q9YOJv}hM+5p1Omb%T%HEakh-=S^t}!cIW|NCt zvYY;N*Q~sC1sQXeEuA^!svEU*$tdANv&&^(v#x9Tve5*SsoPZk-nva@m)o@7>0Un? z!Atj^ZD6Nk^lh>fKMh(sMon0&1|FKqIv6qslh=z6Ed%72Dy!IIOJsI&k(zNe{r5j` zk_^X6`ZxFWKTWP6!%seNfB&|pQNmWNqVSmX-rpQQ`2bN0Cje~8WfmX!`rCUhuDV6| z?tzm(+(*>4Rl?Uf)zvuzW2UIDP+k<|WI}{Ib%x>RC*r31(n%p}+BT+-9GkW+IrRJX zl4DHYwrN6EI=PMW4E<6fuero2mvA4UMJq5i)7)epXyn;=e>z3@9f-LGcf5hMl*Uci zj^i)l8w{96&a4mrQ~GllC9!c~%TH#{M$B;EW?N3ttH6-F_R*bkE z%xs+9eK>1JJlEyUi3|T4SYbBZx6y2}B_?h-TH3hruKPE(H$8SVQM-|~4Xr_@In|BW zVgnhInnHim#YFuiJF;qqG`&6hB@?p%o1y+ku}Y5rxPFzA>{ANaiBNe-q$cmhZ(g6f}5CD+Sf>5JC1{YNhE(3F0!pqbX3(RwM@_N|c zFzw=ol!l+B7sM0Mdy|AsMx{HQl(76 z$#hO*p?1?0eXP0O(<)bIWm(nM?>D&fvK;|!P?al}G1;T~4{9s&3~cWA(L?15m&fK{ z)~>Hj3O^K`+eU6-gO#NfAS4*o;1-7UNR|0&(@~!?n_WwQKqAZxwyrJL|JM&?c06U%ORPS!-dO@oAf`H*?OVR=v)~F4S5z zN+5)YCd&}E8gy1RrguKlTO10oX1m^K%4>6G=~)DM_>yi%EXJsGuk#kUP6`2@0mFH& z*Y7NFja4Y}-Gp?I88a-Qs4d@6Y3k4^;uG$8HkVZ>6{d2Ts(+j_*H>Op!RM>kkox{2 z;Rsw5Iu&f8xr|1}tTY4tlHM>@EiDGFo?bbl;~Fu({1Z6Pa>+DgRgwURk+FuLorv&p zv=R76sC6XM%S1>W=qad%1G_wM3Sh6nDM0zsc0|E!6pSFE;zY!kd0?&wr8l1tn`~l0 zKjN<7P2T10Tav&7>10G6STwUFdt$Ckoo6!J;)Qlku~Vxs*jOESa`jr1$`w?}mAukM zx|OzkuRpal^rsm`;TczAm!Ag(3+p`9y^Z2s;Xjy+&E`xnc2|LnIxpPt&XsPg6uUf-7ft7w~JT& zfw+4o-?d@ch@?j;51V6l_vA4*Mm!^38vC%}t2Q0LXa*LS0U5%JS+ZNQ2IGMa4z4Ku z1XMXlM4({XWT3mXmejMX4KfvQpFUQG=p6zh1P(#hx0TaeK{z8y&FKjo3kEhe;iDcE zfcF9NrmRd+z#75I#zyOzI${$C4z8egkGJ98@%p80)mt99&dA=tEGF*_>L9oaR=CWYsR-P*G_o6S+z$z#(P~a{(6#ymX0~h z+zw|!lNvkPaUB%ja-FB?(Fv**Bgd~HFZW*OO%_;My4Q{$zEnTq*A43HRN?uNFg=hl z(mS>Jp)!boM~Ci|rMz6Z8QFl};xW z+VC;%K?kAOOY{Zm7ozQ4hK7!RFs`B9d6c9mQ-&9ZPv@IOdauhoi;5;SiiX_ zWHK;M)?aq=IP-A2oqKccL$m)pH~*+mz|;ySZZ3~)-BsluH|nc;xl+!#{ao9QcRBNG&Y@@wdtJbh8!GYyZ)Aw zzW!rQ{z;Ot{z+k{O^#r%wLyJLxwd z^XJOJx5eNf7|~5`*>4^z8HR_EXsbFq6_{Qh=&*U_cl%k zwM=iU2Q-PXbe70@^dA>Q@*j7JJAQ6|4-hly6bGu#Guf4I3#=NJmMq+jRMnDLMGTM8 z6FZqoQTr`j5OI0-s_>JgLyrB~1ISJSSW>S5iIM8Fd`kT8G)kmiG74kB5_qw%knBSo z@oyzBOWuPdb_$`9K7a)3Pq%~9W`D>*IUiM@0O!f@)4ww;cr6QD5gESP1B%!6;MicH!*-Y@P77+wB?U{(vm~ z0JN-bp*I7tds}$B|2Yv_ml9GUw621L=mG8zKA?tYOyL8Y$OA*gF20al| zE!BG;U}OpgXwsPQkfX7WgsEmUAWlI(Q%5G%c5JA@ zvU7cnaQC>*j%_XCf?T?a7#|JPH|92fQQw$ue`M)hN67HnNs*fMopiZ@%w_PtA1jc&hb32b{w#B}vxOro)&kk4QYrL#`LlzCOWDbu%nMm`flvZfG|KV$j$ z-FNRE&whE;GvWRhXt!eH;b*Q&eRI=I-{8}UJ`2g|xFh(1d6<`@`9woMA|kP%%i+S5 zK1F0WhSZW`Qt4EZc`V(MZsAXaeCedS(Vb5ELclEaS@QrmjTB5H)0hpPEE5EQNlSt? z21ITlh|EwEWF@giEs@COAQx(+_op}^iJXqHgKDa5asPlpLpVlbgj@6s?#6S zYL9`li=n^zx)AA&B=wJxE3xcTD*N=wh_LiAeKO-y5#$mc`A=Xw@xj(!AZfrCg?F2! z%%%|*5?(3e55O%Be>hdJWqz|Y>@NYc35+My#uxNsQ%rG0cZ281FRKs`l-S?BR7$Qh z-dVrO@Xl=E(CcZ!zjWz~bC~pbD^8Y^*o%J<{*O3DPI*%37d~UUCSH7g{XNT97LQ$? zYDwS3-Mc~fzXjb-ryofsKuafo;|MWb{O%5q#oGdD3s3+{Gu!C$mzxRqo(e`nj_uaPooI_7+V3f_n$&KXNEvegYzVOAmOI2;f z%Txl_vJgS~zx%NlOt`B5A1jvKoKv>6a#W5%cB9YQE}Ng#F-&RRe*ZmNFS`A= zffzY&T}2~NcH;d+T}$M2l)?WJg&c4iEkTi+0V>Z^9RNlas=*@uckms`6J|+}MwkVl zE*N-dTsD!&Rw6C9;`uACcs{*j*L;_2erJQvcU_02%bc~Ubv}FK!A+YVd~oxo2X_nq zIxLJ(Kec`BV~&r=1*4{GtdwIw_4r|;;(YY{D^5OnWS2C@x2K~s>682AHEryBn;yjZ z4?M8>3E?~8cUvB~Zsk;R?@dJv+4DFYRsX`H578avc%LRj22up7SnVaEaV$dP+@Mb2 zq4CIrhOkSI?M#gOW_%ee~$=YyOXUUtta- z@3Q5iMlTbdyK_ZVk=cxE)U2`ldFI@H5%zHXu&HYiR*LHY$S&l*@|^Pwk?pbS!QI|E{fuLT9l>Vn41g5I@&W>ri?f&GFo z2Mvui(Ha1iNH}VO&gaA?EjuED!@2g}wMSvNZckt@^ zbBcT{_aqY7%7ddWm!=M@i%rJXYvdmtmEHZ<%5=2wE#Ya?`{vOxdvUPHUc~Hq)u^&+ zVxd}piz@JUQn_L0+rqRxfv#aS1_Qa)SFTn?$r9m8tB0)&yDHj4Q)OzVO1NO^@T(S# zL(0QB&KiTUe&dAnr^5A~AR?Oh+sP8L@Ls*u%05spT>iM4%=WoC#%#@Vlnc)Y*M>(1 z%>k=bX=I0!#ZUiZtZ{s3P3^i(18oF$Y@`P&pb7q@ zvO&%Rinll&IO>Nvk;2BP83HY%nxOt@^RQ6}1388?OVhV+Wsgs0?25ERVP|+&EE0^` z9;D*zmtfJOHEx^cUSPX*CM%hFt8IaM+BUL@o;Mw^gE?}ONuG9OHsL}9goCExOl6k9 zcBF9hZPPbzo-Rz=Cbo417-4=XMb6q`w5^}k)dn8)rye-Nvy7(}Gh*3HgK@Lu%)3+n z3oI%!*v)_P(IJ#lCcqSZfges}9(VST_vZX!8Iyu_9WRljFOkeF&%DGjD#;zAuOeiL z)kL;tDxm*yaTD@D7Ic(j;`>P;SyBFLyqBneU^?`pM<(c}IK9OD2nZ!U*T9lL1{g;P zQHC5spChCsLWwhCBD+2mm(S2;iqgWTOcCcZWEYknl3hS(8+Jq-!Js3u!vGXFx%%`X z1GZyXL7}pT{gaax|rmpxnPf6C{R0 zTib|2S=j5#k%yaW)!9?dat0A=*X;8^v`SQ&KeDAp3DgrAcLuh@xA;PZBR zg`=d<4p03_tdo51mGomi;T*5W zBR30JjLniAk}JV|c8{b_@+!PN3ED$3pu<0a5gVJRMq0Nr)(md5j3YKqt%Cs={mM&V zt(QUujwTQ>MqnxgM4FbD0^omUM`j%X;ov|kMM@GAVteUvCTv*~XK!V8i8e-rGO=_w zoddypK}UkYEyU(oO|oKfA7hGR%Au_RIi%5mMX8P!NNn^DF#hO?MyUXe5YZ^CBuAyz zAaoLmQ4tEOMf%#4pPP{;jWHM)?Ifp@kt=LAg`7AKI~*z{W3ezw)pVPUQEMy~jk*Wh zTB*WpR!FsEi}0SsqLk?wqmj|el+#Tnl^ko>maAr>%xuC2=oZxEl4o@~9aI9XR%h1D z(rWcqJyENP-l}^|YjhfkRH_Dq0Csag*5}@Ne*Zr;M)&xhr-|1PuRQ|g&-ss8aV zHQ)cOM)PgI#`o!W$Vm6yr&5JrWzH40eATw{n%~Tk@(&l_f~OwphL< zCqVa}HZY$G%oj?XR`mrDRG?uJ%%7|Dde!ITbG2SC$p5Y}8a2z$XEq>ISjNkZ>1)ov zgE4B@ZHNjMe(1B_iMB^&AdI3IXEcx*Chj7 zB70ZAgoM~V!p$$OCVPKo`w;0RGhZ4!{v}p2VcgvrJjUJQ`tKgHL2`y{a5*?8l{pSS zVw`E_9ZV7@{DRZbcUGeBT!b+Rqb4RXao8LXXKXTqpXO606l_ghxNxwE%@d7RW#3 z3UEXjf7lI6*9ic+0Pae`^tPR>QL2SMsL3oEYnGOP$E&ou>S`~7xQVo(=)(GU4qQK3 zr?C@W$tk9f*D9E@M03cl(WrbDVpAIxG#Fl;5L{*BOWVj61YAL>qYM>lvf-j@87tpW z>ZJvtU!o^7M2?;aC>6H~*pz?_@A_f43oiSGu}SQ@oNif|jUiqc=UP!8 z=>_F32*pk3PFPZ*vcpA%CN-p;Wxmn4U-oTG7E0BO+K-oF$b+b15-I&yI4^>TevPA| z*`O%f1ySQ{Y5ZqvdO^$W`%*F%#Lt9hQ~Pdj5nk<{#WM`}1&EZna`}}EkJxL5;b(RK zf@)(^i_(k8hi0cS63J zs|Oki5QJx-ntFo~>>H%pY^E}xqM$b5MkoYvA@~kW?9WyLsNftU=J84%FU=uI1-qz& z1e^PwZW2CepU0^YenL2@YGH@)Zu1jQ{eo)vbm78VWF|Q$<=}w5W#K|%AkIaL_Q^~f zi|eTOp-#ROKBVnH#1e_)P3HY8s08{;dZ}0gP%Po!hLQr;BV~334uMWAl-Bd--#Lr4 zPP?Qdr)gAseNmTiQDw`*c6`PC1Bk z|3&YFAt(-S5J%N3gxme>D{!fPNgp+SjP6|uarzfLH$e)iK6*+D$1m-L*m8QjAGFH^ z!4#H29_}tYGe9>0-gpLnEkFNVf|O((Fhz0>mN{pkLJV{|+nAL!+nm@Nc5q(1;$0 zM^XlI4futW(0Z&+Dmx`;z%>=+F$`--08{c%b07caoO2rfcx&P4E_cI%*(-V`x`@j; zY3;gE`&aF}^~k{oo~)8NnyMR&zN(UV^8aqFW1e}|cCqmFEzbNRLwxxa?}InfKOla<+Aw3N@!C?SkfJo8^8o_ zI-fw6;_#rs8M>Q+4?{*lf6ip$gGD1_2)F*3nIb$OJoLNYv87o1MtGo;=rMVHc^Mg* zzJq)5cfvzNlfHv34fMZg$+Pso7znVXSU~|SIp>ji?}fH(>3^H-I{4m&4?q0ywD-t7 z&`*A`g)pImWS4M#Zu;G9Tl!s%h6&iR8RREo0+8h2rQ~oF4^Cf%UjrF-Vx~<}RSZ*I zE(2MIVn4)+wu!iV_&KCBJ7WozHtAvFJ})oAL?hICnfWHzmC33lUvkOkcX2xQWGg~> z@BaL}sp{L$pV2vjL?679*l!~z{`9L2m(0`GtD8C#ot^Q#F%1oEW0p0nz3W%&ub4Tl zv7>Bsdu8sZhQ_w8CH3p>X8H^MuC2*;raREK{(9zN$DD5BT3H_a=?1Nud0!pn*^pUZupA z00^Tj5tSm3ES7<&%$QX!=9c9_0)sU3X6E^ShyF8t!uA7Cb=}?d)XA@&a=V}EW*W(c zOu_RclPZ>-{Zx1NQ$Vf%1X5Uw9d3Fmy}|)ud-_SSfJENUoGgFpK<0AjCt1h|evE%Z z;>VXe18_1@Fu#N{v}Dy$lYcahh+FBgOa3nO3B5w!-!FNJjDG1I;T;eXh*@fdciwr4 zjDCtq-A8v`@^_NF?=`aGOWz0iLhnbEgMcy@d_;QkKk$7ipcWA}i23ZFsLEMr>E*^m zNiljMCxS`D0CtQRk`;cwZFtH2PC&AwZk-Esg4y{wTFw0ENVACmqI*lPKgx2}QEvCVye^Z; z7cdw4Cy!~hT58(tTvkqTwpOE+DP#Ggikowbz?sCpE1Y-gkZ|y`3z*$+64-JWdFkBM z*Ij#OYe`h^Gw4gVEuZc6IEwvFsdR;*#pxI9Sj47n+C_64wj)Xcy{3t;pT-^ zp1g)@-ZnI(|2o#{s+>8q(rfAp^75*M!p%o28Vqk=(~!6B6Rq}RU(=z=?xM1(WkubU zhnjpJYqg*F8xK`aD#}}&S2U^mP@|C3P(crm1S=Pk9!@{A(q$bR3U-;imDb8&gx;j0 z;T429XfFCd_&s7}e*eKm7kxl#5W7Zh_&9LS%OJK_PssaKWeGE7bk2mF(NjBbZ8CnPRDNY_y0vqvSTwEU)@I|E zO68Zv=36_MNF$?~kh8xcr^0{F%jpBc+=KqI8uz?&m(F%qRQMx)?AV_(LB-(KX^Hq` zc*ZkN%k29pbUyV*rbJ(s3^CW0uoy3ptf1(|FpOf9QHdS+wI<@yAcjwBu(VmQ6c=8m z6b?EH45R20DOnSoM;S*<`PnH@ znU-mbX3h<@cXoy%caE$qshO~gkdgW$q6rpc|}mM zfW4fn2@zHg?ak<`h$MyQiiQ`Lv=lS5hhmgJXsl0?YsZi4E)8$=c$QBnnXh9F&2c*$ zo}1qk)E{n2YI&bMPp&&}lpO)v=eQDNTY=41B&;b>thIE#&z#?7w)+at2l>OB;qvN; zop}qqD&bJPd~C*5L)|+2Gh=x(#-YO)hiLs$8|GplsgTtp7@+wT*fLZpU7J+vUEW}w38eItqmZNf`rIh|C45G*4gvtuv2ThuDXc4 z_`F(~o4xr#n>-TrA-kYAe{7|2#8J7Z{f-(gd;Ga>&c1)lWrqs;pUj`koHIS(pOU_D z^8LS$#%g*dRg)QD^LVnOJea-VNlv(W8>d}4abi{VBvc^g{(<%>=A~8;kSobx+W^dd z&`(FbE}}m!n<$swWH;yBxQ58)FmSG&`4)_se1oQtH6u;oagR#y4*UV% z$RlzEQQ?Bxx~KCmCdnIwnIbM2*apCK_K0`0o;qZC^gB zrnD~peLitnc+7HIOQfYaR@=5i$KjSiQ`sTL}ZLR4Z5zHCAtN>{bMsjN!6PEI-ku9@ESMg(;v}J0-^JMuS7w0b5 znX@cD7-?=8W)2tRaCYfAMyrX35sT!5f6!STjzv9;6_lBvK768%HD@<*NHttQXnIdk z?y7^F`IN{L?uU%rCUVHqK1zo@akLs-EoXkZnBZUz#7i_Tpn#3a5+TYeLYd_#dc{U1 z(h#`k#S*5uBs;gUF*loal*U~7`L0;$=f#;4=AN=BEs2&1-}$2Zg%57C1^v#VI#-t> zJzRMAY0~-3eWdazv*eQV6Mxve+y^*iS4kA#R|fn- zu&3e;qG3vLMn`=l-=NG{P!dW@q#yXDaL&2329-vr{@Uo%C`>lC=j2i0{4mP|q$wR{ zgn!v%CnO%Y0uBjp+Bjf5$TTk4KkHU)cFe@~QB_pz^SCGfJ*?JQKf0@!=#AcW;GQ7N zoi;maX8SBB zw0v&=GnX)%`~NoZ44HYcOdJ!a{DCi*(Pc}iWH`|I(H=k{g-Q{v<}ma?m=r%QWf!J} z8H0%E83q-u1cZqn?7c^L{#>B=FH!3BvbI-O&wt|5F=H-$V*bp7Etk-A)B;d}v8Z?J zB4WCFFCq`qCkDZL$3!R|>lU7)++0^}S32aEDj4OA`8fRuuF~3gDH32)EFsOzy=Bgl zbuV3)$8@b(Z6hmq6?u zdXVtQzxf91Fn&M9rzk%aFfXVsQ6;NGq(q#$=}<**)WJ{ZWib+A-;a)nqTVnf6_5cn z4t)>}4PzEXog;w~#$Z1ki{Lk<(qh}xw}&MofCb9!BjRB5?P=tIsR5L1!lWmvIA=!w|rhUdd}Y5$nj z@Zd2XuQLzdk4WtBzY3^hY>D1*R4J-QL@7{T4h1Gs&|F;1!b2qrcn-4Ri{yl`y@Yd0 z*^pzgBXmX3x!4)Jdgi9aQKc`rW~P=gL~>^9sMO=stc>u zp1E|DPH z1|+>G%%}<4&@;lb7~m`>2842kdFnKRX;3oaB^xJ=tNn^$zN#HJY2(KGHZfn-jm65O zv2|Y|sE=$MDk`P#+f=niuhp-qLb%_?NizMK%8mDJtX!j)P1?vF8!9)6SVmEIG{8bp z2aE9}WF=dHrxwk=qJ>vZKCOv%Yh zo)At7f2FjnBAx2PwiC{psVaa#f^a&N&m&A4FlmWM^^S9%ZFIKlfmIcYLA zle~cwab?#R3c6H?C69~O?j5+5(Ku}I{&=DcPF1X14!C@Ld06RKKXaA|hyZ9WLm+u1 zYU9HRsSL0LRFN&gn`8*8j+(;EIWTVc&J}Lr|J??}oqO%vFY7Pd{Y6}OUwA+M#qNvh zzMOllm$Y2A^8D}4UwIj6VU8R*BHYKNenP=LIsAo_?BrvlN&QmChJE`sbiAY%o;Ws{ zJ^8}+nDF|rXml9KiJ>Kc>Yu7U7@IPDQ1zHiY1R;GVYn5!>kiY=A@hYZ6D5!jXKm9F zjgDUbX@8jR^5dZ3&mH;m`~C4Uo)bA9>NwaLyc_};espuXotf1sT)&St6D)?TGRdDT zPCw<2Figb7ochV#|KTi>N(;hPVQX42l#brCNgD1 zvWp5s5{;f&-4$_d+2V?%|A$k^r5fdYhRjiF3}qc7I;+Crs?HH`C`>$a*KxQcE=)hS z=pzx^E@g3}=pCRZL~ZT#1ON~Xut5lx&eUcc*{uON08|U3d`6q&Pp<)B?F42E1NRRy zJM%GAHH^}96C?Sr?6UqhDb*1YaDnW1aE>TLszQtvMYxNSj>v)_3QAO@Im7ql1+=foE6>vkVT=e zML-E2DW}+g0qxjgNR(UI1)Cq(jDO_2P2H0>Z=T$}>HXxWlfN2Uojavei`8=j+%dd!-BCV*E({dFq=jrOQYQES*I7_41O!tkCj<#5M2QaG8ryvdqK7=gu9TZr8csspKTHAy4i_ol!q6 z<&!|m64QwpObHr;Z$XeC@yn?D)x@T*VtiL!l|DIvw7dzSd8F_dSYno+%Z(I9k_YJj zv|M0aC;$HDo7~;~Dq$pkFC_j<8=icM@OSfRWQ@v%95YffhmKT`I%QJSENWZSf?);l z!poo|oEX;_!8Rr%>f(a^n0^QrUm-z17`_DZ-=T;mxdE-G&1&Sa35xRsy&xnq5mJN0 zK!wb!qvfZ98jkQ>%^p&%D|XmjyV>G3!aoc_lNykvoS^23*1T~x2U{uIUmA95?=I9L z*Jlw~^}!~T5!peeSTkrd+Vf# zRppW?oSGxi$X>^L&`5?#8hsNQ=(QGe0tSE&-C`W$&(dQ$TdnBh+>We?VZv27Gv#S`x zZY2OyBt_P2SMC;6st1M5LWQvTL6yp|2gJf0<7BwUm3uT-o3rxrvdkMw@MpJCqwJhC zsZ*&j?k0Nqf?0WWb$PpuYUTD_yS6LUDAXx#+PCi}1wHVwKmF-3dLTu?Q9A&nV6oSo z@k-UhPdpYrmPL~F=$s-#*jh4}6K)VM{Y!r-HzX`A;+Gyg=WM=6{lGoW=DZ`R5fm3e zUJ!qT%nyqa{2SQ%$wGES$NUcb69&&849DX!S%_!9&{1|m^t$s{#zpXjSU!ThAZ`em zpMkBPEKH+)mURqx;F(k6X~?W8PDi4?A>1LBv62%KdYqIl(To)^r+k4rkHRibtuKrp z+A+}kFuI9BP}DF9=o3}v!~q124L~~#QGm2Yp#;K80}BN8x{HW(2&G>btrLYno+H9@ z35Jh4PFn1&B4`XL_{g>k=KW^r+_+su5K}zr`hwB#F1xI|d$y4oOH{&}z~X<*=X;n5 zfz3sWma*%`tr432PLpt_&gu7BDvm9EuOiIYq6=p1X{ncj7rFYuMO!}UiUBs)BTs*) z1o`Z5JrSoV`*u2pM+f-Tl<-D7;B|slWs{gddl4xwg@uU$RM2QL(h>#HgZf$A;YVLG zl0$wIQT7Opo4-^W&Ft;P9i#4#aYx_(jN}G|+H66>&7adGyzLmnne=3yCCIN}dz^55 z%q53NnLa4o_=l&E4%Pk62f{t%3gK|tBrIdDXQSypVUnQ#)ZYSK&Dbq7n*`JDF?m)27D?iLX(kMOA%T@ zfiG0Ffqf_p6^<=Uz=~9Qb}N=Wa;dfq39?xAiLF(tr0^|+?3lV+4bD}=FZvDP!*|ZV zleuo#==FO+)Lay)iB4#-+S-?Fy@|QJIIp+>9J{11)nNVZ*TGkL-3_oO9~YaG97`l8 z*{J|YePRu82%1q-h4#rUt33k4Y)Nlow(4E0rq3O23t7Bbe$|x$vS#+eW=Ftc^%IBu z#`5&R9&0=M)JgGTyx2DFr|X7BOXMQjAPG%>5=Me~z-OXC8J2#zo#gSvuEokmLq13>Ks;moLJ;z3yyYjIm? zg0+BGvYJ>*qa~#P6T$wBIE>PGX-G8vh!q|}3>8NeL~*NpU@c$^L@~tDK^DVraY>x& z?bc$O#cGkc2@KvrDU$WVlNFHR@nrPQ)cb{S2>N5OmC_7h^vhB+a6Q4DaVe_5(lU!# zw4+1&r_Wz*i%LbWS3HQz&{u#fCNW?^PSAZ(dZ*GecfnPx^t#xIhor9}Uia*q{^*2( zor4b~3k1>VM86!(%Z+PMc6V6DU}B5XdIGL@P}a@}*xZcN_4A&%c+8lK56{0owQc&0 z+cr&|vU&5AsnfR3n7%D_{rtmp-xKq$XXeNZGSNw8Bf?kHe2W-ikXB#O|-cKR7uZ5(TT(GVQ1;IKD*BA^?N;j z@0}ix!ATR1xOEQ{YHbdiSq;J%Z=uHSbC@*_zsJ8-uF;r^io9-jp=FLI67~A6TB9W( zn-kh*Q+vJO4pAtKQNPEeH5!aIo6)4#n%(}Fki*jDi6SSb_5z#QlcAS z@#%&1i23tyME{#Ci!?+UvreNCDv`Mgsb5hG8a^*#cNk6fiCMnPiX-Hp+aBztPl4Oh zyHn6D*0IHn$3DB=tiNbPC^UlpZ*J0?V|6jJJs@Q`rA}qn+Rc8tYS7vYi29IOYhBsd zuG*5FF<(~HWYziASy7zd5#-z)PSo2q#2&G$?fT0GFSTxP_hrrNTFu!t*=E!SBi0Cg z2=SRH$2YzncHm7u96A(;d=Z&(Qi-??nsK-hIGvf`4q1jA~oib#XKO7tb8)6w1$r@c;e$bb_`&F~Ni2jzvZn2Fw$ zz~B)d_)khjggJGS~kwcJ`S$EEhn$FG)b)C?Be?Rg4{?f);@1;dk*(~!#;TB_6ue~koujG{(Beh zUbt{KVXkcLp4__g$fK)QtXTahxoGr)j=G9-8WhCenK&*7rYIphp6F!0FZDa$cKI}A zbC$PH6CR9|P9~in$MVcdqgHQm<%JWmV76W(Ra?!jyjZd}yEEKSQq&abG|$;JC;bSc zi%r_Ko|C*fHU5MMZZ-d!_K;<@%9@Wx|6OFrky`ijgBLxNotf;yC;P z19KdM9L-wjp>Ck8BG5)h!T0r&0%+sf$hTN2Lv zkjxKXirD2~To#O4g3+K1RK6xdDPT%wEeGp9$`BglwrgN{jB|EL-iaRh)`YmW(^uJ7uLBa*m(&$7XGI-Ke zN;nA09{>_C7UNiom=;}hVi~*+tXPQjh2p-!$Alh2G7T7~LDWZk#B@Y`_||eS0j5c8 z+}MXS8)x<*jNC9-9f5cm&Im-bpfa@rDJ#}aeD&mfrlGy%ww*gk?W`wa$f&eubjT!agn2CWzTsF$9FQLv-MyCyzdwe%0(XgSv}M>Fy@F$&>plh^`XnrC<3lF=|wT zxwE#mprEjD7ST?yA%cmit*xpe>+d> ze4^cc(iT%F0-o}GzhxHDd0~0Nw%;391a(%WY$gC>p7cuGwE}l#_6uJTU3%q&Du-Sv z1BNQ6(xHc+GOV2wta51Ju2zM;w9pK?-$vo<7hb5Tx!}@jjIK(9#}tXZhOa3(4AZCt zeR8mWs=yNvM86y>IS;5hz*qP;0}qHi0D~PqBaSeil!iUQlCV3>8lbEi7?siLw38X7Ay0^wp7>Q~U9X90Kmz9u zGh;-Yf!@kam`UQaU~ zKC^g{E;aY>7jX`w7r}f$FY=D2T_qmcXkvb7<8v^QFe+0lBwIdIEMQiJi?iI}QvaG9 zFIlAGEc-(x;`Yw!xJj5VRhrI|!-jRvUkNW&`eTdRs$1-4wL%XTJcV-aZoPtMmT%{l z$~8)|v|`{C&B}j2h3Jt^>K>w12|Y-kXd!bQUbiuM2zE$ z5%+bOo?z+mdio*1I#~xKh1Nl9@bD{9rvijuq<*AxPY@W|#D%3Lf z|LDW95-oJ%uc7PzKjz*$Fsdr;AD?r})J$)wlbIwl6Vlsc5+KPWKp=z?2qjWO?+|(s zVdyBJ6hQ>RtcW5iifb1!x@%WfU2)a5#9eiDS6yFsbs@=IzMtn#5`yBo@BZFDewoaj z+wVE&p7WfiejXa4W`Z0o=tf#%Y#8W@tEJz+IKR>U~HRPH7}){FA_g z2@RTRpp84qzJ|6Tbl~m%2s1O8`iyqZ5(?E!d*MNCf_fBIp0pN>Y$)^p^{g6c-qdT) z2G|`q!rdp`_EOQ1xd-;oeZW1skI7UsOBvE8XfB>qbJ|9n@GEyp#)N$*zuR$;iHTMl zMb6o*mJJixJe)xE3Q6_4>)`+&0VYGZT=+r_+-_y*&qQ=9TDu^?KY|vD9{9zI3DK(5 zME=Du$arMS#9PPZ2`ya}-Oqi0SJ|R6){pAu>P}GuxC!H>S(E&)JRvc zK(%pLIt!%_Ggh;J!P3mN(C&zQ%b!{2zgdp>O3i+p(=nue_40cDaryCg10&jdx17tO z(^oG`_H-m)1cDqwb`64b;Smyx)_@t0hzGhdMCC4<9`|!TD8jm$rK?L{m%e7ES5xX| zjVv*(Fl`#N^Ymjk_TQ;du2gC}db*#$3;ZWOD(u{Xf?=5$H@|z8nKTK#24ycWnW{7M zAKQD&^LZK7DvgHE{3S1zo_>f1NH&P+M;%Csfl8EPu7x`aIkw>Sb*g?XAd3zsX^HUS z;UC1y6~<^aDLl9k{x&4~;8i-HtfOnX;mQ^KYx5>mteILiZ%SkHXs&4RwL5E-R@LO( zM6u}hNxwS1`A=KMZudb^r4d&kLjbo*jB_XUZm7xw()$Npp75WZModdD;0bDHwr`R1 z_{sVCpn^HUU7WwBZ2nzSn$~Q2(Y)xssf8Q^yiQfaGpCL)?csqTYl$*OC+Z@HVq^XB zOye(GF$~=Qgsvvqt>JX}F)?~g{W!WMD}jH~8i`yrp|6CFShk_1l1@(nOjnF*SpCVK zPZ>c(Klp(l_zKcZz|T@YCZ0yA0EZ^D{lW`$b84Z^U^;j-tpQBvB00=t(w>;jRGNw zHbmPcyBkeUMyN*Dp&<=!4Z*9_kr2sB-A2w*DIcMAtDSr>qu8;Cw5OT*sv9K9fcGOK zSm!4y(a2K=dfsK5;!ihJii?WuI$xqIGc`8d;YdoW%gL@wbJ?B#*wjo{qOWdT^k9m- zk==Ptc1~SdlEaZs=lt{%`6zA(m=DT}5dFZ2(yka(5~#H%rX*T@>g=_aAidv5RVz4Y)D3sGFSTS2r^}yJIAKH`4lg%ntx|R z@g|#cj@ugfX#OhfWp`jJqBtUbHkZ4DSHKDHin0O4ELt|2GH9gHaP!L}3}X%RMu9^v zuS(%Jt&VKN;Q3N&Y~gBXg}t%bWVW+k1Gq)5L#s5@ZkEsLIw^XNABqBodZ8Z+V-=0W zNfK@`WLS{B9Hl>p2R#J6Cms(mA4-IIVD5qlOg);Cpn%vztqY4NIw=`LQ{iB&^7#Wa z7a&uV)>V||WdnY{zt5auLkdb=`8s!>hE*dQPt81kI ziO)fk1BII*_SGJx{lTuOLY^sHz={3|Pb?n%Yie4$M&R<(ilKI}PV{R%0}AWba;7QM zlhO+kSbd)<)y`7?fZ^f#8IR88g^8yYJUP*(>zlFUnxzNtoZYl6N1f{El@=@+k}>b# z?4Dj;?9= zS6nw@ob*rWHR+$@M%;ibXjl5MM&Dm&83`?45etEsp3Zfah6&wn{SbZWiSl#g2s8QF z!b4X)kx8BIv0a|9d#)&qO#jKn1JeLSU&g}PO{iQL9$?_n`%N@9{Doli;kV#$3Nk1^ z#U4_1qX>;tNcxH3ovQtK_!)Q;noSJxssaap?qI9Elad>s5bi2j#ytCs3 za>OCS+>#mBw~`ecHs)WC{zzU^cx+5Je#R3lToHj6;g(tCOO%@6wkpq&GX4R1 zbtJ>0R7-sa=3topyX?tUg83mJE@(3F#$*?KY=Y=`;PXg{F}hsA=r60uXOmHR?c0m~v#F!u!V#*&AI! zFCAz1AzPG%yv`L)O!?wt1!(?ra)UJ3BIHo!{9Yy?_5{>Guyf`FChX$Fc_I zzkl<0r)IOI1!D?xv z|1Xy@#d)U%ppGeWtaJ{l2B)wBCoHNdN?uM*O~xylSFjm1X(4SGMWdi;NKxSuf(5t$ z(yq)xWA3qIH}GW;dPcJn8YKu5f;{oiO;wizg-JCFwS~i3j<8^y&6ATjN8`%xe@W3ZTPIsDF&xo?<=iJvK1bU>vQqQpAR2|98e;? zywn>Lli7c4!^k9)D%NBa68o3AL)UnD;d+hQ!;L5&d5@<^J+vey>4Buo;w7UeC9Ww; z>UC`7uuab)c08w7zw+VUfg^7(8}2hqI@xh>QPckSg{{)#cJ`ZoB^^z5>Wnx}rQ)|t zm9Bv?Y4QiD9p9(jwKLujJIq}-HB>Ae=~c1k&Xe~rE;Db4B|o4OT`5J0Rv@-mt!atz zj@X>-1Cp1zVgT55j#C)|HMfmO@q}V#n`2Twx+XYdZTw(Y`5GfTH>Yk!#zc-pZW=AdnU&ctSGLmPRA#Yl%*st2 zE5@3|99PQ)1!p??$QLg?_qS8cq3YGk^9J=x+wtQaLmvIzOJ(X93s+Gg81?GDFTVN4 zi)CtqLG-vQfkdF``vU)J8+thXfiD0dYXo1A1iUiY;}P;M1b7IG9)w;9FLlWY2N_j$6R}D_C#tuFLyR zQg?8Y>?h+f4n;=rDT>*O1&SreUa?-W86MDk6bIlb(X6-=xcVo7u>QE>DaBdEvx-;o zHejCOiI7E?piCY_R(m?>8YV(eH+fkc1o9v@DE}J~P!EEwJy^lDDl0jm&=M6(WjI1} zhsug1OnxZaJWem}2`>S^DmBPMa~QOGSg}|L3CHQ+J#ajM_k+p-7#qsBCaS65;S<0J2iW7)(J59wVcB6%k{?6%EJ!OsS@Utz_$(y8; zY_=t%V?5*DFrIlzZ{ki!YtM2>w{6Pe9$-Sq>~eHS?^dvtrb=lv8>;ST64@AOhk#MC zHzd7!sHq55P!v@j9C-9X0WZ0+LTk2bC|f@z1F_*7DLz zruI=vvH$QnNO|>oNZOsqiluu5BhEgp6xpgOR(aQlPoGxv0hs4a`qNCWlU_c;dVlqi zTDma!WiF=mlT6^9KFbP?yQEJ)%wpTyIW&YF?FBzULCQyRsUJR;KJU0*`iv#~`OnpC z4l-gG(E_)Pgd|FRRmT4(%sYi_RPEM6;$3%-Z%5%{n>c_iJhrLhpPL>N-gq#SBPHg9 zDzo{9P0z5IZB?7kp52`GFuR8^%q3e+zbL)g1bTBFEEJU4yBB)6py1I-C^!=N&1nNd zCbKBK(G8K1;))gUZ+7rVPAR3Vw7t$6-x$fJPaG&+8+m@w#PTMtSUR>8IWwlE8>A1U z(8^i-@18xi?eGFN_%(Z7r8sxBlq5ZS&Db~Cl-F;l9Je^~taR<5acm>kyS*=)&e>K> zn6*kON8)>1LFFjt>#TO+!OahJ(gx)D`j_ncOO%}4G{JPx7gXF@3{UmqLN~)yN9>Bc zpC>`rSsX-oGVPMHLph6`su_njt$XR&Kiz!upPqdwyjDEi%D68N9r}`S(*JBYcVz9o z&$k{p(E9wnYv-(faNH~R-S=Ja_ctH>=)vYCYu{Y{=JESp5mvRUOUK`Q^Y~KX!uq*$ z+wUr^XJ)0&pP$0-5Nl^v=I{ zJj$bjzVt*|k!cGIjUTvd6KyVeA${ty&7gHGB<#Q1y14zTyV}$4`fA-A?XMQk9G1;8 zp5EWF&#>*jJebfrN6kWh2{r0A9OgK6uv*5?N2oX#x;mx`pR@Uo*GrC8yA6OX273VP`NcBT5$Qr0j?G(M{{P7piqRt*) zN=el73s(VL`SV{oUT6>g%o)xA9Yvu3PritOk*PmT7!2X&#aO|Vk=pG~2a{1WGXR_p zgE>l4UMm$H7b0r$wzikJ{oJv(mqs9+QS`6EILDZbuS@=&Z5%$wIA;~Ut2=)?DwiM7V8y|a2de7gte_wyolz2Y5-{hoV zNoufec(7NxJ*CD7ZahunGQ>M#l7ayb)Ka^pQ*2}^2^dYOPAi<uj~;F1rK7F4-`>hvE3z-Vn_W?n%^t`Kao>fq*aO)WY&#u0N+&ig zJ}Q*7oyn@G$P)Y0@>jpY5>F&PG#&KoJ^YRX^+K*%Ss=<$$y_-}L{UXErgc(E5-&jp znr?_BbPwuI#L%IiL?tQGQxhLhEFNIO&2PPbbo8M$OJ>hnvg%;{q2Ii5`}B85i|$0V z!QOX<^!@rRpKN0Z=T@CRx@XJQI$o|_piwYoJ1MS+k z4@{;Nph^J0Rz&vw*R{6pWnO9y>5qG@xbr22mF}0)L#gr~)}4H_qp>6$<~$925GmFS z&0^K?9>3KCfKji9ml=9*)MPGa_6R~d<|%laTO_^BzGM?4)z`l!wMngf1bd$Dc#b>y zn)D5~h>eq4r8agA3&T>^5wi5Qbc9S$4}>iqA?)E5ky+fW9UZ(72IOS8<1gH;@(K&j zloXa+bBDra6BOoL3kUoHL_@>&^ECv-8f4FE#sp1A{n>?AMziib z$qd)|3UYAtV1Drc0u&k(6_1!N+06DIJd)YHfVjlPDl1-ccwBwGrPxwmkM*Bj&`JO9 zczs)T=dI|h&|7Ak>vWhY=o3EevYFqaC&{Tq z)3qak!8J0(ysUS8nYK5}M38q_I^SDc7B9UZ{n3JhIN{&iL_m^m`s*5hGQUi*X#Er` z6bg?OrWdP`5fltDi&4H2EUat@&_IR9LpUa5W4Rg%4tUpe(;Ger9WZ1j`qB}QTf#b^ z3yJPJRD~)R&xINrsUgCROu=#5G1XI4iK;2pV}O@}KOO%07*Vf-`?EeR$EwxqVsv_~ zH78B)v;dStjN$1NIP~7JcXh{s)q6EbIU@q&-f?ixy=5Md=FW1>?>pa>4E#k(Gs<^oc+1PZ8N16fN=wp54FANlzWFAaH=&b{ zfQAnN$J&Hh3yED}MWOIH7)ogV@}!cEsZ;SyN(m5WYD~`QDI`rOS`C|IRmP8uznuy3 z6YU4j3nT_Wj2)#Thq^tT0U!@=r>Blx9f|3`@u^wA`q~sTeE7h|h2DfqiUHkf@F7ED zuYDvW)BRyvr)4E^ilw7Jav_Gs7aQ@|s+U+3X3)W3FWt2JrdKY!z4Sq+^g^o5V&0dV z1qHkqhFbheojd#ItY@|lQRzNyUi9L?d3B#|Oz?MU#uKs^g5D++Bss#_E~hJT&JrXc zz?^emMMC_0k@h`{lHJLW=t%Jn&Ha_?_9*|MfFDXLc--MM6MEpA;3i*GXw={t1haxc zP`O~@;Da)-23idkDiZUq^f)0+6fq@S=PW6PuYLV{sqOpMudQ0PYG8bpASTE6ZY)hl zG*aHwjnBOO%*LsCJTs=3HujEB7KN<%fvc8PNnxb6k3uS-^=bnQO7TWH*Hy)gvgG8l z85Q}%i&JB8E8I|<5bHDvy5v-s&E`r=ju8y8&IB#)g!{#$77yo#OK1lAl0AaH(6h4> z(VSQ$yN2aB^90#@%0m!-u!JJq(ht2_FagGX;(L(h1it7V^eiZib?`=sRIu_INiKC4V|*i)2yOAx9uOS);1I@Ox3+wfauYF3K4 zOuA;4)LOn_QC(VE-J%WUtrDkDYIq@X0)YDCI7@<^#YJY=;(>PkSyL*zZ_nWm%{ET# zC5_}x+2RxIQr_V`A6&?+38kflYBDbn563}g9u_;~*cxbq6e@C1CRBO&B}a9MFmZHg z>&!U}3RApc!IDO{B7B9g^xk`|r1yg^5$eF`>Vbc3h|%r%WXnmGaS946*%m{#AHL;7 z=?R!_dYl?{EfP$pnC0-+&-WUwd!@fx$VwEwO6D^=?VyBEslcEkgpa6}lN3z`4yHZX z0PJK?bdvJ0Fj_W+No&{9n%>9*>{puinPiN$s+-au%71qGl-(Z(C}l zy-X=>xb4;D(X;8Ib!?q{o3`-fx)3Rmbs0h!^KMx*b`G$h3KiVGf3^t&K3Le`N(YJq z`T??m-Xc>Hm9neQeEFW!XjHi*jq+ootM5tgo!)c20)egr?CPwRuUfLyNo8iMvLbTl z7wD>#prGjauD7x7YW3UykBu=V=6-d>2Mvl# zTMd@Tw#(HL(Xa4!u(TMqUOM{n)hmcjWIp^F%XAv5s*(Aoy|L%plHZjaTRM->L;jn( z(Yu2hvm0`_bA)sevFNaIg4T5+6&Jg&Yy|O_8v!qQUC|6pyf#nEG;`oi7ov(2?tsOx zW$u{H1LI1Mvb{(D%T}Up@bb~XA}v#AsS~tIo6y!hUe3Hpod>3stXub!RwUgIXogZk z%z6oQ`n9kwl4ZuhA>I2=`@QF9hzRu%%$g3QTQ>nzmM@SQ5=@t%DGc~QxEVaeP4Jqc zE{Alb9FSjsl+J($zLMM^QvCIE_uhN%b>{Eb2iB!!>8wMCW-XNs%-qH6SFXIC z3q3(Y{R#O1|M$bvH>XTjkfI*9XHkN54q(mprAzIAYmU6KiOt`%2|=Delpg<6>)oYM zq5=0I!8m-lQR)EeDAT#pyIcQs9D(S9f?ZOoh&EIM?{pHpqp#BEz&v%nL&nrW6Gbh|z9nE=Zz&d4Rf@@`|1|q{5LbefQW~ z(y@Na-`H2D*4*%?Z7cqGjog2Fym_fl%A@S)Jyb3{)5Cj6+>5ufz_Gs;=VK3ci$ultSBF&OH3*5JvSrRY&ov&|RRcDKAZ z(cw&Ty~QfLtM*D4J5(^?V^3o8Thg=GgEmxl+BF8F4JW{^@$+qnKJ#x0Zx>;LPPL%3 zDdoN=vwA^5&Z75q_c;@~T)1b`pb6d5zaIJc$>lpxad^4*pst56UgwNs`X^hT+WSqu4jr1Y{0Y7^+WF+oE2$aU?qR7TA!Y3_<4M?r;FMCY> z>^ypYr$&JXSqv) zJkOTO`5Ya&wv_O*k&sroHp^$Wtud4XmQ7u&@r=;Yy;MG736DQB|-Wj=&+b6p7iRe>0zW&L)D!&`j4@G&%F8+)rOvC}XxURy=?4n#mJfM>!i*&PxL}F-W zkK9IO;HJ||)yaiLUj5NCL14o|7!omTpTvmD-|p^AUS5hQg_f_|cA5JFKL-naH`m7n zI=RB=4=O-BzC3o)xxBqV0Xqb!Tu66N_d)rAQ6f+M;=QQ_1*y{N7hRv__Fq%6 zbo;TFUW#~VpBOGkZ9AD-z}0_ob4dyNou+y3yBady!b zsk!m-lN*MHO8omWr)7?;DG;?sk|%t|#pff(gj0?OGPsDT8jDC;_neTvuR;&>6WRxhYVu;z}Q4(tjcOss|yB*Dg8?( z$7qdB>%TlPefo(nCH$-!{@qcKb>@6!)v8ydFK_+LNon%-`Kw;x3K}$`)|2TElxOd4 znm1NGzMq5F+ilxb_8P59T@woAsifhZH^I;PSC4-=bhbE?ZX%tNzIxlhm1xPGGD9ey)#?$3zhFH_?bxWu38Tp`)Pc?nRWaOu>(v7H@ zlDf9o9vj%k|G|rRTJ#G<8O$^XX>W<(?povI(@G+4a&HDuP4}|f?kLjO$)v~`g&X*S zz!hZRIEaPq;YHFl4|uw~M=0fi$Bt7-bx&?hoe~UINb3*u)8{@Rbbc6V9X8E&&~9{n*uB*L8l|I+P0y*hf| zNK4U>ZwhW$9hk9v`s9A;<}&=58;4Mm8R~;!)xYHW6)Fhbu&aL56A>mLqh-iT)S*Hi zVh9wVw0xuvlQ9-lBDsDgKH@D7cZu={LF`@K&_guDLmGUhP(n_=q-cY(TUG*b23?^S5*O33rKQWp`|kc5{)N;`2O~X&znq+_Ev|3VnupxP#M8lT)F{tXa(Ls#n=<(4Vni86uEij zxr*|XIyD@2Vjt;y08EWu4f$gMAVxChP$i+o2Wl3vT ze{-rKhD#EJ@$K`FxbsVGu2WcMOEg|m@UuFOGA&o#{-?NP{RjMKe8)2bxiy?IQ7L@~ zEfdOxcE*?_JT62j^u$+(_uY>$)saQ&N+fmRWYqgDRx#?5Qhg_K4@cvaa~1tzS?^#< zW`Xyt7j(Wa8^}hmNx-38$$rhAWADKLBXMvj6bUJf)Gkm>Ad7i46SLo^49e>yI{B2* zb1>K990uf+PH-K6bk+q9Dnu<+IR{;@1H7{%dPl))ptQ$`M*zGUTr;9ez`u}u>kM>G zdt?g*8%I+e)b4ngzX&&rURUgJB1?hOLAO9)H9pXprr|v~f`#QgMR(BzNda6c;P(@r z03L%p=H<{f(h)kKOoh=j`b@ino(y9E)c&-jn&BEcOpjEmQv41l;wO9}o`;I#a@++C zlTUGFbVU%HM*z_j)J`r69t!#tAQWWU3>5J`RR9)gdB0CAhvqY&gwCAycq!YK3^4~= zgvuc}i__2?MdiRTvCB_ZqTYCjI#r4M&?vJKP&BlM1bzo!Ovr*hl!mHR9HfHCSApxH z_%)>}6=iY?K;_1Ud`+soz)RIq6(jc}KB$j;D-mGp)GFlBi{i77)ILjGfMX*QP^lu7 z&l(5Uruqbjqf|dOC42C;y!70*CHgVZ)g10+)+;q3rPx=LC^ij82I1Ce|5%%_=(-gn zxbM_f6&oKe&TDW)Mnrz=9GeeJT~4&Bm2rjyl}4ACISiqiVXrP|R(u;|{6mGadqmF3^XjRN+iBC;*8a(j{I;}cU z@07mRjC2VJi8lAJ)Hr=VmtN#c3XOwZh76tEVRBtO>l&%?SQ8V{lltr9QoY8)prCou z(8rpVof99&zo$0yyxyFi#bTw_FYdbQi@S>F%w;NV(uQP>AWGk<0n_p}Cn%M=l&#W1 zQ?F8^1u*a8faiGcX6C%>K4w4c0nm)O${1f#2u;08%PBRg8040<3Uf<^7?%ksjlYiN zigUAK)MicZBsK!MG5oz&H;Abliwno-ox*RPpL%?X(#a)jVzRVWpmSMAb2e^;|)N>Gz+l?B(pIZGYpz!&J^?7uV3IA#fDWGz5!-lJEpLB;|`NorHQjTszjmC z-ebKXp;DtqKHLSOI69@rx=>|QXD6fq?ta z-5z8G>m>ry0eLfV$5^$`?5;@f6{yy5`LRZHqQn?YqRFDyXcJv_HU9u$kEVOCO|l9r zGPd;AyA6iW43kmImagUdZ_S_Xj!Uu#)}(89BpZ5f$xs?i(<{xDYZnP<%WLNGe%~&u zMWwcF>dSGPjxSq&{P^-^k`Em*VFd=2jvv(TNui+u&2AetQZ#Ze^;sFGR$5FqCvh8{ z`du#s^Pjs_ZwGu6VGOC*xC{(QwLV`|1K0^SVH%s+ssr4bxwJx~&e7|W($FlC%?8uJ z6}p(fyy8F|$MyZ7qGWMd(e^1woB-f1t5c`f)%Qzz-EQBPpX%Uwdt%=(%Pp?*dDze) z=s&SGi-0^1XD9X9Sv)Tgqgz>RGUTK9NQ_N9Lq83GlELp9$zvM%ysz-gU@o*P>@ot8 zBvrYXgP*h~k1U+C^6S?vCHzG9{bO7&w3J&?jaj zO`h0T?TZV?l6?;3_||BI3Sl44qHHcOwkQ$U=jhB-M2LSD|0j}cLI< z(l?ECuyNw1O%tPQd(WNgxDj3x#L3bUEsH+V89N2YUfIe7UX1~7qNg`14158Zng(zOWHZZB`0%GAORjEQ%lLEDZf_T|T3sl8!I;#U` zLC?`F!N%B3r}6U1%@mY$MVS)1%M?`#QxHb|q%`cV#bNea923nMVrzz3v?}Ns3Lcz1d|VaGZ6{zYv(1C0 z+pqM%ZPX1Mi9n&bNM3gq;|L#;TA-r{g+kJ|O$amzg;)r_FfI5sH8n9)NDQ}1jp0aZ zYk2S8a4Y8yvu1fU+MIZv9M{m5?SZ7OAgFjHo=>Bx?N1NlS0B$s*YYK&MZ+^&$qq(y;2J`Akhi`c2ew>|nRVJ|Sf!+aP6 z1uA_3C6dCF3pjd}fa9HiZMXut9k>Xpb%|a}7jksHyp5k|E3{*c{y2Oi_|PAG zh`OFh4RBc&G$TqC@@WrJis+;irPD*bRt2ROlCzhji^!QyY1+f=I%C1(1tSq(+8Eti zlHSo+GH4`rLZ(DJcgdJa%=4rhKoU48cD#7g_!Jcr?WTl_Jqf3{>OxY?6EV_v%-xQT zUBX^UPkbEd+B+0ok7kMsTAXo&M~7hU^b)=q#~N`GGPzUHO7LiUnVon@I@HOJ-Z=_6 zDirXC>;@!6f{D&`N1+2C+EK9_`LL3i+Z(_!_!&XEfd~XsfPsT%7pdMLl?I|2w}EMg zTKqJ4TXlP~Q?0%AR;}8pcRBf(9XpU=*4aMi(;@xluMTYQmB9vauS}aUf6bctGp6Ou zPE1_?*wn17sgJFn!PktbDh-XS0y`;{vcC6PhqjmsMA(v`xE#REiM-7hCt#Y66{;ft@pA0iz} zSjM^~tb=&Orj}C=FhH${=v%+Jm=XiYNEry&a0^Th zBfXyf>(lt}6&c)%y(v8>eTO@|xAJyoIC4Z9vg7-^8t;(adGcQAk0)o`^A)eWqB?S) zQ*`rc;4Q@;&B8y9Oe4?x%k#91=@+#jfR9jyt@?H-ORah#q_>7ARkh39fB@D3W3KC1 zv&<;a&PF<|bGI<`^2w7}d9$oZp~+O} zUY+{il&BYt2mU@3DjYROmt#gF2W44BEOhDDq81nEf`JhYWw1aXHH381y+hdo+Nrn* zGQlg@BZi7}u929YwicQ7X-uy$NOoFff3r_rJJrtqMjMfes@&YFTw(Xb8~1JAcjLtB zCDUgMmLV2l_Vgvy?TV}I6+)DKArj)lxMkb-GKVQIL>(R~uayoQSSqiWaPQozjwvmWi`5;Z$A2@%HvTz`RJQFbywZnQ^%PNos)tAUBF@Ka(SRW84X)B!CJ#z22<*6 zFILV6JQ&l^M}Q6(c)JH(8`__uVljNax%qswO+r-n#_nxVZllNzLw7H&?od=O-96Om zbXsXk=-Lv)$T_oU?p$e+)PA|jkP`P`MC@VW<$aO9N$Vf_Zu92v9$KHI@}zrIS8hh> zCproGM>Y@@;Nkzjs$nMc*boqi&}q(}iu(OxwOTtA8vYwi|HV6pd_H97;{N}6O{&Vv z+WKw$`|0(`$?H%5eIwCdqWzc4PO((~o43=5~p6-pOh*OVS)S?o$2~{+?jdTqg(ywmH0_V zD%`WDkb2Y=@4*P`b`9v^k4Q=o4#_!czsI0fAd?iXC@_o9#e0#hy+pL-V29`mXdqPPkfAXtkqjNQ(vnVrWf-TBTXy%VpThV+J86Ln zRRp#Xoy1s_v=%@m47R+Ohj8Q$<>ge#i&R$ZM_w6-#oGB=d2fN=puxe)0#QAxvb3tt z?34ue^qu+z%BH$Vc+`C9wIREv=|ts@$wfJXgfPG%Cg$}+WMsYTKKgCVO_kpDSCH5n z*DH-ZoYw0H+U>qBy;99p<%HK14i#CrAf-58b<^}83QMISvAK0k%SW;FnwhQBcCpDD z?E`46QTr&Aji3|xKw?*rVpx`w@f!#AEj1H04z&!L1u};mB|_q9*O}dIf%q}x+2Err znV;|_NIW5zU}}w{6RO-*6RHmRLV;Rx#SL)}rWC7&h}cK_-4AbHnrwAW+coDF^$^2# zBO-Nu7op@XQJ@X$hVgiuNT$^GE*c)VO9#;?@nOf$#J9K zcAdcO&UtQNnXqe`S-EqLWJu4H<`178%;gmQ$ILyD!XBEoODLoI%RG#1>xFj%ydpNI*<~C9GFl(tM$4k0N>uX1e^R$82$DfY?lLM-#^|M8<&5`68_?lI zW}+zONRW(_aFD}MYD}OJQ}BB<$_SQq*+!ufh5XaUDxBptqSQY3z=64ovj&epFgGWg zTZWn7!2B`N{S$6Fe9V^`4k@*!YL~GJViIz;0siMG!tc|X;FCr^q9f8_xFK39z z5-I2WGH22Jku|J7vluFZ*S4ooyO$OX$ni<9gm>i!MAz~GJ}qp4=EO~Pa}SvReqe57 zdczL;XeamLz`=%~C#On#NLyEMNr9EkdUd?r>nI3mnhinTd_i3sNUt)y6hfHK+!rb` zXLcy8qjdwaxZ47?>pc0=yE*06Id8mCouwWT$QWb>#q8{RvOJh3vil}EG_c8|{0VqtyR!Zfb$ zil#aV30s_eQu;?G-UNINjDl>lDw0u-0?ouQGHIr^Rfa<9+R@KVF55$ zL9={*3VN0oWRD^8lK`fee&v8#z7vuJ@%hSBp1jjjG5tlyuC>Q18Vqs$7|RH0l1ZNm zcn$F|c17tRF2fKn^08NkuC~t5i_27NCz>~nt>0*?pJm%vf6W%dgjK3*wLwQ-N`Bm& z1EmF$*nf1suS|32`aPO5UtWmc96wD{?#r#>m#GBxbaj!3do&}3wU^WuVW_?y8pI2s zTz{EnS^NRM;*w%=E!$ICnC)O6Cb%YU*N&b)YlL(syKls-rDL@>OpHyH6sk;-CEeXEy{d`^M~UA#LiWpps$zpKvy!{UCw86PWiw7no zP1=|^!8E%nQV=DC`{xYobKtLT=B9rU^MRz0!mkt$p_Ww?B37WOaq4@$`j(`Z(L4|u z7aU$2XykeahldZ(`+yr@AFJ9n>AhtOq}`zrQ8GB^mQ*fv?g2RGft&C8cD51mja~(1 zv7Mp-OGapv@?00KVgP|-Q5U9UB8o&0sS$u?X_TP|8;v#u+1bLLF4)iOV(`qOG z_+Z!c5$&Z+J^^45xIOwhq5%T9hKM7@C1MbZ>b|+VoTKeK8Y0u@9{9WYz}&h`iDnS0 z1p9#HPkMre!2^Q@b)ZdE4>-K`c(s1Bwkij^n>C^KO7(@AnH4X9D%FNwGE}8QZ=0Ak zKsVaD%RDF}FhZSG{l*(P)#W+TyZN4VwE=#$v*Ot4NfV^|$IL$frkh)qoiq2q_`z9= zi4aTeVofm3b?k6OJ{xI^&#BsGGG$s4rH^Pm&BYomHehAXa>Pbf3|N%&CFdmlC=^Bp zZ+30l--!od%UJJtpe*)(UenI&eMUaJ{~-y3b3542idFMO!6?b2KL*5!Ij$J_G7Sr+|rgT<=t zsL<=Q<``~>G#0^__eLIyF>AF3{@EC_HF6;~L6xdO(3hF2gbH=ySZWa2+&dbFKp^3e zwTe+xxh{U56e!Uk5YTuaB}C^z2aFt77)hW|=r)j$!9=k1^^Cgqj;cXLuOmT+^`K4t z++l9Xd(sZG!DMC& zq&w(71cMWseA~_!yk3%~qR#;naQ4Kj;5Z<%w`pUifwy#_ugmdESS=N;VdElD$UO9S3EG< z^u$wyF14y!M7QiyqR!sd&7JEVJjVu68>}5{r%k;7QkgHVkQADXZ z8=k=_bYU2mRIwLu>Hpw%&){~rumKQyKkbyHtNsA`x-_(n6?TPamdyb`avHBdMaWsO zt54Qu4p-qWPhP7B zf;c!c(gu=82Sjrs^=VKnkxz(6PJYhqfFn&1ZtFo|V{lk7IIP3JxOp-Dg$;}AhA&y% z+%e$T(q+f){QQ`(@z}DZ$FR}yvGhOBT=(|cwQpbd41cdAAGJjgY=W z7F48EVCw|7KC4`_@Q`%j@Rl#?a!2Y$yX(H(a#*@>XrZP&i!IpCZu?U!yMarHK0e6N z(~Bq3GZ!yrav56W2OndfA3OH>F)5v`W5%`T+s>~Qbc+^_KlJwUrEeab1kY#e#%sW1 z1)*?#;Vn+n&4y`=>8%LZ6ul2fRa=XEk^i@E2CN;a!ad zLb7BsK+ZYv2%?eA~Kv}WS~~$IVP{89HcxWKO`4m{y;*=fr#%bZI^yvS|Imm zr2~&|+VuD)mZcZ;>Dm6JFV!%e%N3J6Cb{2B()Y<@u$s(tgI-N9 zYAPLnm)GYB<)v}Ukzx7_?)1Z%r`X|56DMriG+|=o?u6{LUY@ub`ylx)dY7v|{EuBO zy=x5J&t4Pf>6Mn9U~?HP@q!^W-hrIw@fL$io(saV-c6`NQhcNa(eFK6<(5t8fviTe2ViJK=*+{_BKX?>ElzO@@yBqSvF zNz*#g`_dQso>?*!OO31{6cAu<(q3FiE&KoQp620ZwB10gn54_f5&eGl37agIM_uR9RZ^068 zmiYOw@^LW?KR)u|lLbf_jS&FekOCpqT;|9%GQOuQbSsl8$8G;idiH?_rDs3iJ|VBZkLUMlL=mwS2y9+vhCwAg2mVXn)s30E_tpJkl$y z*fSu%FhyERIvs|x90U!RMSV_0WD!gih+;(WMJf=%Jaz-H^c2Xf2DK-8TR^l&9k}3@ za?<-kgq;!0Yef+X4#trn3C^E&f>#~#I zcUa#^@*U$?-+p$_eD}hN*#47Q==?rw`4Z20{bwrngkfNxc=j4&JIW*9d1i5sSO+*FW&%vPA*H>)gG#i^0hLJ*21Q<1YGUj9u$uxPlPzLa=~j;p(&6w0j|L+ zS^q(P!zq4BFh?|wXqPN68A-trBv@WZOt~0*LGpUX%neqUQlCHr0C5Y_z0Fa9fobB% z!=ooNa|I*AKjMjt_oWnoH<+YZzIDfBUOJ{)wRz_x?uOZXVw|AwGx)7Q(WgKmaY(sufE+i9hOTeI~Wzvk|}?8NQ&OYpx(+-~s6w>BC6< z76Z3v6RTLE#1*I8Xj~zV5_+VUWov?40ZdQ`)3ig zD>3e{*bD1=6;7)0mX&HCJ~?{D_r2%3!Ka(|&r8Tu_sbqTJ;Au=dIpjraHH>dSNigj zf@NRW#740JEOVmt7Xxn|v4qS1U0*eLL?(_%RXOvtPxs3lS_1FKLO&<;PUBP-y_%mq zLRXfVTr)E;{?$`HU;V(7Y}}%u(md(;^_LVM+&8V0#-aY0&r)I0R}c{s$Y&EKQGjz| zFc4@EU|0#>8?duTKq@c*n$yrK2BItHr(uKi#^;YecUbyrX6-eCa82z@W;^`c@zv7n z_aqq}kbe8=R^qWALW^|ox{6UHZ0e_fW>ZV+E3cF8L%B&lG2y*^3onlV>?GAh z6;vKl>Hz=(uK@)_A<5SwXz?m}ivrRK(C1|69|uod5tMf1oQo@D2Uq6FA=L|rV*7?a z-aPI80(N)FXVSS7Pu=tBU0-LLC%njPkN=|rsYT;lM#ZIvLbFHb)y}A%J8J&k)vpdH zy!gVDF-vb*^H|PQc7c0WeD|i^f8fTJra!*Haxu&~K& zd3Uj4$PD=Lq^=Jk;J18h({2%8Y6Ds~_sB6=z^7_BUrp?G6 zT%8{iUzO1R?6G4n4fFL1>0@-x+sQbsIx~uaN~w| zd9+gKA|&h41|$UX>Y>0*d5PJCqE~_#2Nb#j&t^)>Yal@%pFk=(qQm9f+!=92Mh841 zSWLm`=&O{olfYx_X7odvtfHF`HL0~aU!x5w1^AiMGf)EHb%IKE6_qZg`_Vx>e6@1% z-b2TZAG~?d;_{3bp{P(~mc)XYQ^T8g-?Sw>MX5E$*wZ9?RfRp#Y}9JXt3<8Q#97o; zRVJ53uT)i5T3iY2#hmOBb?B0DEpqtnIf zHLAHY!Z&Z(kYEAn({H@z&V$$Ml#9zlp^B!ay|cz7s?~{%A2(p_%&EmCB|(%};H_S6 zq+DWcS(Rwwj0TmqvdWZX5vwZAu7trW7S0(_H(^5E$k`rMg4vWftv{>hwl~f?w|Czg zCS5_Hn&*`_&6-g?ux?O;G_7CF)(0oQuxsbeKnjQS=W5Yucy7%YzsSdmLWT!Ev3+G(b#j%Fj>TBSu>f^ zpw__F0smj++=867(&hxO&!GQv`Y@|iXYj4uzI)T`@{)$@R_&ZtU{4vVwD&FQYmwg1 z8n^EB%;|Sbsf>#>R#(-GavA!}UQpRrsZ6q(f+PCnmycgQv6sdOggjw+{)1!E-!je1 zukU5hTC;C;s5Cr)iK5A3InI=)RK>7+lB)_bbh=jWP@7HX=rcB5nOA?)_)$A2*7Qo$ zaO*4G0nXta8BFNAV*bedf|`lLQzA#lGi!P#y-z zl9w(wls=@q58ZI?bE1^#wBlgX7XKVt@AV>*=n26tghev}h|K z49Acbsu>qTZYYI_ssb#nyBT=J<#h&UrmM7CxM&D##>LSSBX0?cmY>wwAlHA`)f=OXtB?`4oRisQZ4=|BwuRxG^w2{Z{!MGYh`{_h${bV>?josn9j zE%O13HdTA$f7dKrUr7PbWp}i_aX0z4k>3ABV~{Kz<$04j=?Dpb;8r?+FhzHU z-72GEc6M{Q9QHYionTo|*EUFRa|#+Hd(T-CE%&e%V`MQsn!8EJj~<3v{KOC(JGYlk zTS+PlJll(L@ke=%@=}~dR0Y*tAx}4P1V41{3Y zb3@UnR7HAX#~FtDqpEy}jiG8i15RE?NGR0)(x9MQ3GA`4H;@>?i%F*Q6un*M8VW`$=60JJjrr3({3V6f+6E?_ zXIK%zv(tMgdB_cUh$2^v;LFJ&wo?b(l~JYZ7aDC@IueOP0qa<er^N)+%bc*@!y_d=@)A1hV&Y`*M#|WlEr?!!7C(z4)c>-EE zpq9Zhrvcs%0%=!;NKYN`75gBWmy6Ja!2^<^UM_akntdtFmX5r6)5ft0u{j5?%`6>I z_8Ob^=9_E;Rk*tL1*t8+QZ&X2yojLM7*3UE?-lFP9eL!k$%uQTM~$PkXW<=RUElQT z;DW~SBP!~LDB9cdLiEuuqtzg9Xc{ra;Tr)D(_ z8f{rHH1A@gRZ519o0R9v4Ahw=+5h5r*Q^hr$K^pAYa45O%)_JW!dBpq#2?hMh1s_ zNS)-d1Kf}l;-q2RVAu!lE@1XRlIuK=%E9l9sZEZXH!m)^HfD0b9gq&V#`}VRPuER2}!z+-;9AM#K$N(^$dr~Cf#Vz za2h}+P~E4?x|v+~@r{7BhipAjgAC%wWFrj7Ir%bpVMBI`Q1V6Rmv&2a(w_6W!t!PHqx-(kdM)E)4Q#Px zP-b~U!`iXZL$g`dAA66kU)FZV*tHD}#*n6!@*Q>d?xtGqR)#);Cnba`p7RTDL z4Q1sG+(W%5$K@2jXmcy{0MJ0?lQJ~u#~R3rEIzM7x^I# zQlrkL(`qx)(=)VMZL%)2K%*(RKo1+c7JY+ElPhpPBBke;u550~+o(>)t6n8i#jmf8nW1XBHhB>5lJLC~XT4=89`r<8QxX zqo(%VG->F%p(XKvpA?60yrrwZ%D(kcH2MUE0zD1Ak!E1(kZ^knV785N)rA@bqOc%O zP!I=&sVE@{{0sZsTw|meq5(^x*bM>FMr&&o+{dHyl3e#>)E@J@7ph2zpCI6rl)!;} zbZJoGMHSW{k6`f>o*oHDoqQ^Sg`fw6_kl9+{lVYw+IM01=shnk-1Oy;KP;4Pf8|%w z`){vX_crtW>O5O4g}6tS!BGCqqg|HrN0IE}_;t7Y8@Ic&W3<^nELwHL?hAVtzPM-f z>iO5*)3WYu>3vWS+~OUsT566+u-JE**QM{jl$JF!1d)`aqi?&xr?lc75>`tm9zoE< z{APq=n1Sfb#C?%N6Zo-hk325iZrd06icOGWI__c90jj(4mX42>@#7+Kjgvd>V#B%h z9UpOM3VF^}hM^NAd+v4UC~`(}NOzE4kg^8SU36W<8;LqX;upt~5M_!Mid`J8y?hPsg=j2!n+uy7P56f~wevR;29`yHc6Wcp z7?p{+Jy{-iw$DD)WbUgnRVP?#tmy^Jq>2%{&!hX8T1}V#BPJFihc&5%`_^P?;+n9K zze*Ja{BAR*{=e$p13ZrE>KosCXJ&hocD1XnRa^D8+FcdfvYO>?%e`AxSrw~V#f@Tt zu?;rW*bdEw&|3&4)Iba*Ku9Pdv_L|PA%!HAkP5cO-|x(fY}t^!$@f0r^MC%fcIM8V z+veVL&pr3tQ@lQ(H{B5hU3cf}4x7V@V;L~v)I?6_*wq6t@dtRqF(&Zxdh`_-87jFo zg{9(bQc^a6km*oxBtb82j0+|3Gt$9d#X?J%2b?W%t;(wOlfeAIqtZ25;A4nbqKVe@ z8qq%asL^OLI8WZ5S?G*P@uv8q)`9n^>;UDX_ULuK%KXB_tZ0`vF~1;IzRt6IISK77 z-|gv)Eyz#wx}viZ3-c>|-7zgy^wCu`W4o?X0{{rKZ1(}3OoJ%xgbRfJ&Tt)B>$;bt~Ya)oH02^A> z?zHL{FI=YWUC4L_u%Zs96<+WowQSBTzrv!*aGs7Lwv$2y=zHr!2B#q>)@n^jG<&zc ze%{XG;hsiMezkXY7Y&E#ncsi?kFPxOhr2$1aeo!7dhU;Gm3R31ubRC%u~1x$o<2R= z8k`#4%yc`wIbK)1ExM;C+7=&Q70n)*)D%-t6q_iRE0U+rIPYg$_ijm?=dI57%-;XT z{{DGazWCW)*MH=B>?8TP-^D$-<^HQvZBbL>I~nhcugb8+Us*55zK~{%u8P0)+2_6; zKQ$`angE(21O97%3H)Kw^?{5e3Q?J>K!-R4#1|JrMzTtP{cS}&H-*?hL0I&l<9B)i z6o@xu<10Ov6^e?+7tRS`%uDbl8>L@f`0%!E4`2B4(2c2kKkj|(ycU=)HYFA;TE8$q z!RSrw$;uu&5M2;nyJlvhWBAIBoSaoVU)Z|&#fw(@lk>v)QC#ne4`vi5x*f|iGwWM( z&Hnlem(96g&CKF7mzmpEY}>YC<+g1 z-E18(f+jMBv@km*uT?$Ws`}>>XgO8h2Io!Cra!F>uk%$gXCXL2%;_N?C)hp_*NI3p zLO*9c^P;nL+SwtN{ng&RU&-&_%08v`D05%sR4GB}+=id{&fc$1=bESTv%dZrXyY0B zl{^}LttWv8RCRvzoLD`v1a|b__0`w<=ggRC@<{)xcgob>IE|eDZEy5ZXQ)H;UvvRJ zdjbx$K;{Ty_n9R3hq1t>(ZxW(1Ldb;KSs(Ir|$s|xUMuAwG~zi!?c^=p=Xxp=9N5eEhR^|KX^olF;(A#aC4bl_-Q$^6);{6eB9CdQM8S1*_Np2I_X^o_%P!ZYABl3X2mGHCDR>zQW zM&Suv;SA%DgXBtCBtD({cutV6nQ`n0z7>Datx)gle30qL!MpT$DK7KGg=;Q}xGrCL zhbpgr$I8oHkxSNCrWGK9?4#dNFioHy99v&Fd2%5?fZ)kv93s_6;?u<(n9`0*t40`| zB(GDt>P$EW@i}5Ty~yEd;=6Jidwh96CF)-;PiHsfms7YL@Sh4?@@vou0_@DgLsq&# zhhK2HffFY(<(4WC=bWG-{d9<+MByX3&V*<_x!eGAnboY! zVK$59QoQ{50z>REr`aUTlM(s=hgAsum~KePrdLx~Ny(-!FvJ~G-=7XqIVNI9;pqII z$6`h} zUU)nZq6Cr^WSIYowj~UDC{{Lwnfvzd-?yE;CcnZ0a`CA(tXe+0Mt6$8THSy5Gk<^P z?*8iW0Q+#?e&O={`%X5q*H{4mUmH89JGBO)3O_&wHUI?r!jI1{DLMbgtO5wHLJg~P zGaEJlV5LoKmoBp`3*P!%#3>-bN!W00}QqoFh(U5 z_I3)fCvSpLkO+H)?~@-H`}}!1@Vqe~6-Nv>$hb*}RUVB()kzcIXv>RX!ILKas?#Y8)jb>rWA^~=6v($U zWv7;bzCwQyw=J5D9yuaR>)f;J%XMt|KlfcEXDhZ1Mq5|NV~=fprP4LWRr$)+$KUT=ltlgu{Ty{aMm#cPR0)3*R$@YWTsR5O zIA6&3uq7mxJGM^9vKoEz&eva;clwN0t5JN%h%MXW@_N4KSGXKsT6H43YU$D{@tvxr ze8cFd?$owzGFd;+so|5iQjSx)d+x!UG@i&t8RFUl2M)N;WFt$Gv>s#A2-r`dRf$Bi z>AxOF>X6ofSS6jCQVeH>63_Bk5f4s)J_ddop~SgAl^4$0uxL_c;p{9-qi0y?N@4$dG>VPyZ;IP+7B1L zH0+AXb|$CfMJ`#pILf$q_uUtd_-ge+T1HGIX8whfFFttPFP~?DOJ@u`aOZFC{&3Uc z#a=jNOyaR{(}54sc%S$VvZg_HCpz$Th0GxOa8#?DCEGdhE2#WZ5~D0D1?v+*oGL@y z5~4St@wFK#p0gJL8!tbqFgW?1{-==hxP0QN{{E++Ft;7OwL)25*Re+~}0H_}6{CX*0oRXs#@+*Y&tIGCWw(8|;cD7%( z`BrA!|Gm`Zm6GqX`1)k_`wVMT-pgz#XJ2RMzOIw+u3x!l?^F9u>>b`S`DOn1hN7`w zU@^4~_>H@!av%5N}n6I9m zvS)bjSNp!dZ_o1HYhK1z(VlUf-X{s&m6#W&542T6n!zXlB-zx%Zsmv@<^mME79>ML zJ3cXrLWL~$buQ;TKC1C5o*G0`w)>7%&%^hp`% zPFq|?O75ft_f)HXp&{OU^dVM<;wBa=KYGqq1O1V8N|07y+)a?xn6F!hKB9F>;pTuu zgG6>AWXypxT=3$F|H{5PfuwtsIfqT6p!g_fblgBT7%}xo@&{5J>HaLZjs@h9%YqV%e4vbA=;aBYfUvbgnw@=pZFuUNz%ud1nDwW_*iEIp78 zsneHMX_ zOssGM6bn=xAm$numq;aA5H6YM&=B$gPUVSqYj_0A35IkspBaRNOlh)^@*l)_*+1`L z!t%(vaBx-6*t5)Kf5+~Ue^q9Vmj4#xvhjRVG@E003zJT~Ab(+ZyY0;SBD;<`5~t*q z`YYmL8HL&7%l&ydRY_6&al}`hiH{qPhcZr+qvu&HZRLV_`A)#~k&iZ*wwh>!m-}4xID_ zG^|!*hXR=*3CtZ5mh)o)CdLgc0m4fdEPG&&LCBw^P{FgO_mH~-?9zsr#KP#mvO2hc zvxrHAjG%kK*wcGJjUx&SASDKl6_f~UxKWN0g>ATjcg2IUFv4DDhIegjnoVz(j4U&g z86~scmKM9#o8d5-jErZ*FY~#vuc(+mH7P|el=%H6I9dNlEq>- zCKQOK&1)^5DOO{2RMC>MI;)}kUHOZ5ySHYo%3v(oXq_V50rfescC*N3;p{hNyS_($ z<_6j1L5esaFF)`iMXdS*)BRx;MfGCI`>FhUYz4v5ql z6V~H?*!H|}6V`n|7DZcb6R+jmIa+B5D*-w%hIi}vUr*BND`6?@Q1GX~hzUw=5E#tG_8d-|q?Y7r{^tJ9yvIzVGg7UAc>DpVJI{$37J zKpTy)c84=_2JI+igw)j%EJDmdjF=*-sZBi{Y5Ne1L-ndKJ{HihqBxqi+G{X96iGlL z|G{@8Be)RJB-ucc0UeJ}_x-rqMQFffI}}py(;M-K+BG>`$TJwnFg_$_(V_dU zLeDGQZ8H51d)NtVcac%BMhudDsp>4h$Wvc*%4@ zB_<3{JjklBxfQ`oWI|$avv5WXcfRUy;5Gb@BO}I239C$V8ZsbNLdEKfQiTN%)(V`vnnc%4~>T=X>a7EQFGF(W|S5SHevO_?5Ko{=$M%3jD)D{ zgRAvU=plb*cVtH$vDiI7+ZVNeOUnF!A*G?{ysNXPic)d*;@O3vp^l7r;epdB;?oO~ z;?y*vF{5l^s_1`H6|*O@bgGM2bJ)b59V$;XrevjsF4pc`iDl90@lh#JtZh-o>?o5d zYIeq=HqH|^8`4>|x5T!IS#D%eZE=RGdGV8`EsjD9(N1%LIS@VjeEBG)kpFh0{8^hP zJw;8yiZf29$oLm!1Gf?ltM2PuuqZx{B-E7iYs@JhQQXAA2mQw3r&xPZW+JwBFm*)p zlny~C5zSLD`3o7iGvs22^zN_>I^cC4q*_4q(FB3rQ`|0j?2=CMIf5W2Km3toWM!vi zlzI=WCm25bfy1AalAaOtuDWsT+2dnRS<|d{TCMtOTt1GUUVG81S8Zwhs0QwPHSlL2 zl6yOPQ0GZmbFeV0cu8}`dWEfdIH$JCpPo~+ymb<0&)DTuEJ{tY>h-wVK8~Ayeb=g2 z!F@Wz4|c=GODFXP0G$2^7||CBNkB(Kevkr?=O9%lQ26Ma(f}5Hq)bnvvkt6}G@~@5 zCpaQkML$Sj9Q}2!bu^*H27(Y&q1#d!Y^YE4CPuN}&a=hXR_)?K$rrKtYxmE(`Pw)p zdhD|ca$}N`J%-q6Dd`n)9m^K(T@j;qNrGi#Z}EI4NT$cmQqCJos0+Lpu)rd9YxVMb z{q|J3!hW7)oXb7OYd+RTUGx2>y@&KXZBekLD7MHKhskO1B-JlWTi&yNZ=+|0$Eu$k z%}m^J@+>tyP^pl4lir0r`Z&<3I4dJT5Q855Kx$qdKm#EG;>&`pqBlw}67LtCL#LKr zP^n6%fyx4~<*FiG1V-UfAAC0&yp#+mgZ~~%Q{JqsuAZojX+>h9)otd^YNv~T;V|kw zjnyf4Jm%1wlZ@WA+aFxF>u}bxu>V$;T3G1A0dHd{&m$Qi&%i$XYT9{E^}!V4#yOG@ zxn-#*#kEy@H8v^5;jNVaaasPNc}0*Xu$t$x(A-sHcNlC;aGKT_T^V~)Ry}at+B+@{ zjds-~GH+I3hCelX>Y9z~a!p)de>>iD{Mjp9Ci%J+`P&&nMU~C)1Hcf&Ir}!q*G++s zxLxQS5{1Pd?SfIV21sPH1yE61Ks!KUYfG?yMm_;z`P__1pOuD?$VxJ=s`*pE`x!CslJ5wr>oJ+y}lyT%s!BB_805*;dH&79sLC)5WEie6Y2K2gqSDZl`=kM z0*kfyQf4Jw$@R<^E!^f19mUqN^*m>9sQUf1+|tZH#@W+S=f*-K_N$nf%=FprKVRyI zNz0rU^-RQ=91A7V@|>)4p(%P_cE#O=ljT-lo>=ZH&xX9AZ*opnkX1|7Iq3zH*P5qh zW)$#snXJ%ufpGPsoaB|xGLx<#c9?O}`6n}NPQ^}BrYr$x(!G2%> zr!KVMK$Rp|rN>f;J5Bo(?6!P5qU|vT%3c)Pch0badE&A0SC%xadgP)DLtKPqj?|r8 z?o4ln3%Y;A8_*G&Kvo5>0)u2`c_B+7F1@WH1_DY3yFQvf#;ko&!`5i?`K#NYoc!vw zZuhEF-$IndWj?=Jt~XTX2><-lWSdk0{(V+nEIZ#~zf4?zEI*C=4Br)kB`oTJhvkp! zW~`O_65UI;CT1r-cp*$5nG6r}itnyY&N8{3ZmY-W6;2F3Z*!TeoxgF(pZq>$PRf

|iJ)rNwdGr)EOmirSOj@aI>%6ZNkal&y#akd%Z!h9PH=pX zunSE4#rHx6xEAD*#{#Db`j(nTHb$rq( z`SIDCw`IE4UK1Cdl({%QKiRpYvTI-Ol)2E3n83%6*X4lQTMw!im@x|=F;1LfZo~Bi zz8NanVFA(DOnN3USPvw4gNFtrRu0qgkpyHaDRvGISd351$@kpw`x|c>3KfXn$u&2; z`YH>)`XD!_1eR6A#F*dni;b15*+r!}i>5Wk&f1YAUQr*cES(1_$e9xt2lm;#X>q1N z^~f!^j11l7%FB=Wh5XVRZ?du2qN$s&8EW$xAD=en{wJ`EcLpk)nsQzwbcYS z`Gd1Uxu1V+O&I5g%~#~+ly9P;rmZu+8N?k8GcAjx>r1RXidKDjVTGVLT0Jn;=%&b4 z;Rg2DM0S{X%2U^#WXLMY%5+<^EuvA1%GkN&g*j1>MX_d^W76@)P`%T0883Go2a({ALKF?KFD>=KXUSYGYYJ3Q7Tk1Ni}n_TnL=PkP}eZH%SJ7V22 zNmh?T@7kRtc?vyJuFI61o{T@EJ6rOw6X){5n9c#d;0Ek*S7H2tlnGpED3z&Cv;vSa zF%Afdu{fd=#`T$~KS;8SP>%}g=rPh(qP!r9DH^uY8h5@~kzlghqids+!c%8YwPtRg zpBPMh53UQm?!}(WIA2w`YGpXMVoJCwB|bBDQB<7UXm}4v=IzL^PMtF~nB=H+N83#a z)$d57Y|nX>TZ*nWBxEG|@?BYpj>LtRrdlofq=r;Wd8SR0(sQyC60&pBCCQOlX-REJ z(p#*)-3yQ~%bk~!kQr~dvUqFdWm_=^&YauN$6lVGU&EvSYZy4!f`Oz{;h+$3V9B;B zaIj;o02H~N=!ESD}J8h-5^cocoYSL{%o5NvbyP58+$p9d*FRvk~X$=Ub z2Ipk}2>f&XbGS231p}FPi6cOn+?AjyX?&<~CXM`ez-!(c^n%-K7h6Hs)HHe)q>mS?`Y}S4F6yJZNv{ z{?h5q!P@gT)#`PHs~cwK7U`ouDNLH`&)28CXumgfp)=WFNSN)*w59lQ;%<@eNHWB( z;4HB)EeiZSeHrV6mm!lQtzc&11LE9u=UrX1aMP?*^-M*vpV|PLc`fWelWZH9{J`%M zerZ`{23RdQ^CPZ4aQlQG&?DU6o%IWH$X3#vA(W62?Na2jp^HF=uF6HqmHu?hmG#yG z`BM*eOqoC5?w{kg&zn`-ad1+}gKuTIj(s9YpMF3I3a1?EsGAAop5<3l9GX)2z?+#d zNRfO{{>!0F?;Kpc`rtd84l&!onPdH9{rnpK!?DR@lcgVy>BxTpA1z3+&zo7_acD}> zgKuYgKKfj*|Ma*k`|StwY7TWyn=#*>3&|$?{F!x~hbaXr|C3(-$p^0Nw;n8-a=5c< z{yck1;SuJ5q2+fsZ+e$3HamFo7?&?%+qlfOefbl1lTgOs9qiBK}bP zSV!N%Eo;293od`*1>x8KkdwXXWuZBXda7=zaJ%IXKYCJFdh$1!Mt*y1V_f6{$v@*z z-^sD2{Vr+7ijV`Y20{@JRSICq&Z6Yl^wHK%S;Vm{VXvZ4>(mBX$~nkA!t_dmJi_9%^0c(_i*qJt=OiWP z+?zc)Cnq^6=Q}yLPaeN9>tgwx`_Fsx>V+|#7jI6UQl9K9!>`YmT%K5B8@Tw&8Bxhi z;p54R9^BjCYLgqPTdJqFP30rAztuAL>ayZh?V%MJ5PlVBFJa!g$(8b_tHeopS^;G! zq^Nvl&&D<3;D%|wtQE757RN>x)b!L&^0>U*EtunDoy)$wG(BO`vPBh=)dq0!I}c{Z zr5BW~6n|e?R8(2?)#AbAyu9SWkZxNYBoUo{l-2Ltox2TJG9myfNxy{BQ);oi>mE`510-d+FPV88sw+UkSx zY%s4{&0kks-^g4k>kNfQ2g^GvF1zW%#X%hGK+&Mk@9w`utges@Qk28R^sz9avHSDn zlE#U9_&CUpkd#0$3$77pXRdG+A+HS>aAHI;VM6I}830cLF{KlU3}L@sKJW|c1&ytj zU*5WAa%a!}Bgc*%x$P%xMQ?8({;}wDNC>_uHRX~yE3SI}s!5SHlCOAu6Q%288_%T< z&>TfyjLy=t@Bnotz!;F60oD&mrd&BL(<{=?pc4Rg1Y{n)uH-wn&Xhk~a_cKcrp_6C zWOUBdr>}2qwLce}yWFzd9q)&}>f^=s;G|;tJJRyFf%;XWqpRu%;_CAqJSUoyvllx1 zUH}AA53Fm5s9PM$y8v{hG1t?dc1>}O1U%O@ z`h1N(y~$h=A4o6sT(IawV+E^xz*Cty$FjQi(2bJMnqZGHvYerTc|{fdQL{pBABPLm z`V_+@>((5s?YLt_#m^EG@^ayI-(yx(4*81yDu%FC@$8S$Z%8YhNJ zp`~;R4$V~dPG`0O5dH>X04mvw4)m}Lj1BP$Kwj7dAV=`I{a_A|5QCH~2C4)D)EmBn z%7evN71PkL^|n5#skpJSF|bBy8&r!3Er2im7X|g ziAS7ZSqK+sje&V{XU$zuyigcCSx8FM!s`x`p)9I0v}Q}AI3qPPGp#{t+_ENA8C7O5 zjotZ!DaJTU5QW~gK%lp&GlZSPC@W}*Gfw$|adKLL$5Z5+O6vvj-PCU_fxmO?zyV75 z8XTSrd1O{!wPc}r1WXntL63%)Wq{-1io(Zc7E&ro4K!}h1ZXDk*sy~@e<2g~7_2r) z&t@3~bKV^nidnhyXJs;$Icr|NU)p>}78;vrOt7qdLz;_UBRLp!(2j`r}o`(yqxwEOv*>ejs@{S*0p2Pb~@x^Hu zH48pp!0Qd9rig1UN>=(tG|jw4tV&5sOQ{l{&o>HVe&NWX@>##-waMw}$+i6U!zBT$ z;p9594|3nhbxNlnDfbVuW+^$nBsR7rJvrmvM-~#e;M_O{Jh?vtuZ+tb#p{w`2gr}T zXh63STn#UnT$x!C^9ork6B>4Sb`wJ$FeC|?tPIxED7q{QNAi%vD0A>E16flmB8hfr zD)>WLegPte{;ct9Sthtuo*0*+=pExF8yjV$%Sxs;Xd{cvY}QL@?|@MdZGj5yrymyo z4MgM=JJ>Q;H1Q7DE||B(Fg6u#apjN2cE@k|*avLHC9e=}a3AMa0Ho1%B?H(n@7TO|ErL3%|m{Y~T!xA+4+ zd+Sec%BAoA?QOR6O*Z|fW5?fOFvE6B<7e}k!z2V7^!(6^>}U6#c<2wee$F>M%O1bw zGKiT=^{mMt6|@=I>tls>ga$z-7bssm@rlIo6pf7EF({ zRm^N|<~R0ScU@2Sb=S%BkJ_V;QFaO0p(3RSeUEBa?L0yGMiV67R^ZeRI|1d44$B%a zmPiy9Ed-#WCc*z)pbEB)=qu0q7VWFFq!Yh9=3JS2QB*&zxNv5X&uN%nJ9e~oKC}iF zgd{^CrXVTDpOaJ&6W|ZIZ0l$ijbG2|1)J*>^ng!P(|ZxKSvVh`+Ko?^A4{7ubH$vT zx{i*z;#KSC2E`PM*MxswO9~S)?G-o8>UCnTP+^1?NR=2@%})+=u1CQyPX$d<1Kq+A z%vs`_k3#@g0Dx=aWuOH7=&5nj+~KJI;aOdBkq8SjGNqmgjW4?p6wyWJG*;+~6Y_I& zbMq65^%add(X*g29bUBK`#W}gUrd`QN+07Gd(jaSu_U1x;E<0H zEa(9dY{_VMYlWETaGOkSN1|BK+C932Po=_l$iJ;7aH9*0Mwu}Vx-iR`*m(q*>n6aY z3Z+oO14HrD=-2vh2YOHi5-^!cm8Gr>YIa=PT`1%{fNk6!M@R#{fA#FbPKml)6~P20 z1`0*f8q`8xKe-Wgv%<12JnQQnyXU{?Qb5p`3iPpcN(X5cJ;>$v=-S#Z(JNZ_zB#(& zYdy@KRJwO;-RX|}^mOn3?R4D907142$qzqz zTB}j9g!`i#Uv|z~v}l&|IamZg&|n@y+5C0C-@AF;Dly%K3Yn4d|@i} zw0S@>)vg&21d}bg6rRfie$4_Ve@V5ydj;9v-77!*8A=y>_n#4K++X|ocGk1~^SiVL z>vbec`N;R6hI!SMe`d3l>?fwb{MAjWtflFCm> zqdjdEvu9U88A1W&6Gxw%8{gnN#=VHsa?*bB4?V>_AimbaQ4Kn53gAksICqyTN5su zJD1&}$mz((kWj;@r>z00&nlWd6UqA4QPPQ1{onQD=~bGSDuBTM6;91O2d7F3(W2s9 zLYn8|T-Uz|(uGlC$j(HT1b)7sgrKj;IXEZj>WT+fM&LD1J_OR4Ls*l*q z(0*St?x?Cn66Xlq2=RBXfAIcmuf0F3!jl#b&CDrGE$O=Fk~`|^*v=7bS7u(Zditi- zwW-ZL2jmZbwQJY=ENTCiKfZAN(wlb|t*M++%RhlqRfYV#{G9wl`NvUtlN<7qoXx9x zBKzeX35|WLYW%Zc^=lYDzVEu5<-IgK1gx>U`KST(A29 z7zKa>5}U&3kmea3T`C7PP8?q(!vL&C%aPcrM^Mg1kzT=ZU_koGHY{==3Tvr$@}meu z(76{7H1?;&I71DJEHUJbY5U7kF&c?($w^%6EDR3)04!Cc>mjVaVxT%7K77Y zh?pqBk>{-y%(hC8Bnm!1{Hf0!vV!feb#LkwVyxaMx5<@y*LL}%dvho98^~G} zG!Mgm12%DxTp%-y23ElgP>F!e<8u@r#M`blW%*7XNs4jC{))30i@_o{144R^Rr8*2 z&`0p*=TzY~ufG2^DI z;q(2Q)BlV7uRm}~M}+kHr>C!dWnn&ErK*Cu zE0x>r%5_Y=!9E*3GS~n^U_5eSLiybZxnwPulF6?oQ?HO%i>G#=8S&=)RljeYeqj9x z@a&1IUpOl(sV3iSmhVvVt^C?Gs8pfKH-G)@yI)IBZS@Byro?W5#*eMGzbgOS`0-~wIj{%qH??L=S2NXR ztHxf1SHsRpw0yA>v zFz!3P#c0_0114N`D=T_$``GdAPi)`*1iPhsjS;ks*I=%!9eIAkj-xhnU5(igD{-f> zshbOzynpf4|Gb7RU)uk6%gU84Z}%;`lj%N}&tEE7O~uhZ@RAp>z+(@yf;-KIp8I}x z!DI5P^955(tf|OqvWk_zW+iuA#iVDpn#>zsli$mvI=7$FZGCgP-e?YHo6X_93;UmF zwmN>eWA&Yr&E}k-$*7<8?giVAU#2(g{Ie=s13AS}aA?3%B=_Db)9(y}j{!}bz<8*~ zJ?g%B6!NI+Chq$f<~O#PjBK3i&fUL_9~G&2j~%7mH(fB+3jam%K`7{~!1cNu7L~(+ zy=h;dw&bj>vBtMm9KnNrBUkX)?+a+$*pYEY0AHsXIp-+-6y9(hF$h$CqJVmdLqK&a zaz)CwldWB7-owEOwgIH1fMZBlS);Sa6aa|k1qDt}&g~oVTYJssk3Tk>_X4fr9*@9T z&wOZNx4r$Zl4;pQ*Tg=hzCoX2Y{;`c@qPYdySUmWO6x80W2*PAyVU04t~7VT^GVy+ zhnU@kPx*$lr}N4$i@LL5fcjI#@d_-FBkZq{^@S`jHYmR$t@{QVp0)EJjtpP>CVHKC zwK@aG`T{8vN%%r}=W%B$ z(_Hb|gBcG?AUFkN5Y~VkE(GrtKO*q7;wN+fJOUo29}*gAigXo;osss59xv!U`MCtT z0Y-7tL3UXoH<G9z{;ZqrR6sUVoNd1cHI&I+7p&q;$?!N3uAwtrmOGDX%no4MwBE zYcw26x2D_tR;zm3LQw{z$I14jT^sfninHcc`?<&9(%S_|Fgz!CeQEma<*PGWbp4^j|Y{)20DOhSxob0p(vRs8Wo6THMV&gai%S?{*q({Z?zGt@82bgi}jd`<0OI%h}?mLwImJ5vIN5RxqA_FrH zs@2572~8G=#8x69z5(NV=>~rmtP)1KN?i~;E|k*J)1YM>DD}XM1K28x)-O3(Ze>l-?J=9$=Cy(7F3C?I= zOiomcQC#KDxT_pC^QMT7w4}n6kv>CmQNZ``#3MQW;Ul8Q=rkAw7UD+1DS2AAFt5=8 zA(0!o*B50lJByg6e69S~^~sLO zw|{F_PIhXxNfa*p$t_zOL`Qkrd0#$!O=hMi9nQo;ugPP(9?98#=>=I?S8aao(^>ZT zhF`y0oHk=sMkaa7nFW=1eN=iTkVoP4?m&{jrHbrYIKMKwrruJ`EsJt?C59YnzC*C! zQE}jx$A82GV{%*XJUltl`DgiwiySp_^I88y9q~t86c=iP4J! zOUleNTViVGPR`iymr8w3ZGBv<)8vY4j&06#i|cM)Q)97u{jKbLX4*CPHTjQ2sg`&c zEnW%xe1QwPR>j9#8~m4DwLLeN$2j6+6B4ZEl*vZl{wrR(WvDeV%`t1Tf8LPXfbq*b zW!1kU{S_xw#h^f!DHf-&ED-(&wMYUV2B-?j z6~eSPWM;Y7&#Oer#)Pmg3sa{oS+olnaA``?^re-%BGFb@dQ7QI$e5a!8S92~PqrcW z%%9*w@2k%r?vR+n>=#QrVX2g@V=IT<{4WbG{r+p;zjT3mV*@q6gZa~+$nVMWBaO)= z(wr-w`rxy_AAe~0qngDl_DX%?Ehd@uOH~qD* zwHg;Z@OSyv7j9++e|`O1ksR-mTZaNy$`}2WEw7hQ^6Gt0{p{86?_I%@+xEVSsR4Ns z&@>7TC3|*7(9tHD?tbWIUj@DF`(gVBa;IdW66dL8xw72&(=`%gnh zzCs1%*%DQD!bmw$!sq|PoyLagim<*d!1{JI(VBo(P%#kG@j!@A$c(}>yt)?AcAAc2 z@J=zY5+y+c4O{4OQ9sO*D%dbC07Zs_2{OW>#H3(>#ID;VMJbP904q|7Nu-?yyrbMn~K9OnSo4Fk@c z)L8C(P5yJcZF;~~_JlV8LqFap?nsI^<-%FC;u!KJ(Ug!T#wSog@j;JP4s(1%Im~fR zISKJ%T7pTGUs8NphLdtl@$8n=Zd<7rjaq-iUuw=|`8UZgd>Wmb;xa~$zD2TtZ;eJ9 zT`9TIpR$UZaXdqZN7Igq5s^!a3Kj~lCj;(!JkeM~M1#cqv_}Ts%8;Hh zH12(EWcaYY~)7fzL!mxZ`r)XYE+ zt0PLtbgAx?I7Pm7M1JY^N97k^h`WTX8fIm;KgP;mi1REbqDk8un00no0QaC}BysLa zx3F|qR+-lT;-vs4*|IY6gBc`0&i*HwK019KPci|*!?%>)e^1Fn^I|@ak*BfZi{;nY zyPtP_#j9P|C%d zIzDS(x!~yqYn5Ecf2Jh9=^Lm*>{(AS!%FC^F4wi_dSGSZB6y*CRQIgzW!*cvk942n z8zGA2hoCFA71%OBmJ$;}uWT`($E@x(gc!ZDg-~`0;6^B1i7*L+hrI!1y{AYTqa2d@@6zTCo1Q!H`o@u428IC!p?{x+;^E?Y0l5?UBS4;X7dxD;~Fnwu*TU^wrhboN7w;8N~lBoLGfs-|Qr^6m6 z2+l;l%xXx>v088$i^-UZMLaqhS4nhP%WM4Bgv6RlriFS|_PQ@RG{wp~{yIG%EZUUo zugVZZ>+5|x4?i${#-&@97wLlyF}@Rnc9YvxVpFd7iqUC_a7yKjN)&H{44Es<7~^)Q zj`cVli3wAjPDi+ket?a>MUOv_72z=D&!M?0i14E< znc=Akr;1+YFkp|BV2duyO}yg#tJ$WZ$8Pq0S2##myV-&$Vlc3FA#2Kmc5Q-#L0 z5dz+Ga;S1VUEFbVF#@!6v5 zh!ce$wCeIJWPazJe&>?M~T7=80Km%%z<$p*1`g0SAVL7MV*HckBHJs zx(s}m8rCDeNedfv-)7sjuu&Jww`gIL&drZ#VT&%8Kcj{1y2*k7-b6p-jkmzhX%}o^ zbi&7&51O0JIJbx(G##NnXf$m>H~1emZ8;TqtN9^B958d9Djx*_BnRC2c=rLL}j zV9Q`vN9VAwzIkKBH@&&9ZHq5ZToNwy)%5iElvhK(!N^c#aATwm85+=@KD43+_=!sE z2Spn}bbsG)&8Emue=i;uBBlfKE3@Y{^Evd%Nyq}q^SR(#-++v4WW;ybv|7X-&TfSF~Z~hqFWjn z9O~-t^92jb3X7GG{Lcz+#D_%iDb#h;r4bw)Q78J)4gJcsQ+e}ELq&O7k#4+U?Z~0# zRP)d?btjcIh&tMkzE|nCZp1Ysmg2jxAdDb1UP>Qw(Nil@5796-_C%V8A{eLk$e?ey z-#6SD@tqmkp-Ag6eRz96UgAwV2Fo`**xVNBZ656QH4hIDcD0NsN&5PSyILbd+CUGY z76PVohI(+=cY3V92^Mu{U`eNd>@YyM5+r&NdQSb`=CjHyRK85tIXpZ7y&h^_vkFUv zUH$(}2}KwwwO9I-(JDgbZz{8>2Orrt6v2Ci#-ZE4`p2Kc8wN^9z$xJ#-EN#QU9GzY zwu1KRu406);cgXD1+m@36aLx@U1YH&13UfBU`{0vPIbGEn!R9GPWFkVOFwLY&BcM z*0Lt-|C(6~@Y!cN8*624EW+AZ2kT^AY(47+^Q{;9l>KagZGa7wAvO$?up8MXcq8A! zwzBiEF}?ueliS!RyNF%PwzEs%c5o-#1xb?2pt`z;UCypxSF)?v)$AI!mtD*DvHk1- z`xcC{UC(Y{H^N8IL0ITM%#N^|*|*s(>{fOgyPe$uPgi%byV*VLUUnb*4!fUymp#B9 zWDl{2+4tBZ>{0d@+^s&ro@C!=PqC-j57<#y<9wDq$9~9u#GYp_uou~n*-Pvv@Id`C zdxgCUBf39hud|=CH`tr(E%r8hhy8-R%id$ZWWQqXvtP4g>;rb3eaJpyzkxN?-@$Xy z$LtU6kL*wE6ZR?ljD61j%)VfMVSix4=7)jl*ytck(D6&0XBhW4MQVc`T3P@jQVi@+1y^3#>Y)@-&{#GdL_q z@GPFqb9gS#c`5L~KH}Q46nYZv( z-o_)m9ZCR% zG2hNF;XC+FzKdVVFXOxU9)3B$f?vt6;#WgcbuYh`@8kRV0sbw19lsuQ|Bd`6evlvH zhxrkHGygWfh2P3=F#jHZgg?q3=tm{3-r4{{cVBpW)B)=lBo#kNETa1^y!cF@K5wg#VPk%wOTJ^4Iv!`0M=V{0;sl ze~Z7(-{HUD@ACKfFZr+d`~27Z82^AD=O6Nq_;2`c`S1Ae`N#YZ{Ez%k{1g5u|BQdm z|IEMOf8l@Sf8&4W|KR`RU-GZ`34W48H>a)ewVPskSv z1n}a7VxdF`2&F<07AV6)nNTiN2$jMlVX`nqs1l|M)k2L>E7S?~!Ze{lm@do^W(u=} z*}@!Qt}suSFEk1ZgoVN)VX?48SSlMn~gl3^dXcgLoh|n%{ z2%SQguwLjEdW2q~Pv{p0gbl)=FeD5MBf>^uldxIXB5W1T6V4YdfD*|zVN|$CxLDXO zTq5icb_%a^VW$O5rNuYT+7TuW+rfPuMRU5WXc`CtNSwAlxY2BpehD z35SIv!p*|Bg2=@!$6&}#-lRA2uhlZryk)f_u z{ZOQNu(i_|>Dw6T=^uzlop>G=hlZO6&2(vs^bQPf5l29^i0xfHy~g3rCQu+95kA~$ zpm5jFFz@fy4@P?XH%1Iw`}=#Fy84XDy?8^<5?BLfsCb@jFMZ?+8dG;e8Y?HX+DiJ;Db zNb|4(OEsvfP9rr%DX^!%wOefOY3?xNW7-Bf`}-n8=8gS5BfXI(w8x?asREN09vRSY z7;Notix^ta9k>g_%^f0sLt;yRf47k?w8BdRgI#^Y`qt*&$Y8Tb%PZdZwCTHso3RjD zh9jGYn>r&z1)7!crmnW(PBY$h^fmQF+J~)b5KHE8WYD5MD3qa14X+;=8t!V}BGR{5 zy87CXPR*xW!>{q|sHvXV|f@z>l%BMx zL8TQ&H9Rt4Rs#w|C|yKwgysx&ZH+XwkM#6dweV1Hb5D;mvbnXVxwrXrv&4?B_F)l( zV>{-^V8j^N0zkuPm?+TN(?1lkqQCmO`Z|=hOX$zOh_SV~C(_r}Jg6VUR-wPw(AwYI zi}BX?Hh1(zhRx&sH8OCzAE|u+_u);E$gmBcJ}^Ku?5h8&g&CfB0W8p zR_fMvbnI}%+=*dqQlVQ3(tI~4p^*WTa;FZ7Qh~GS3`9ns6{8g3I4f#o;OtCP3~+dV zOGLkE5Ocm$8g3ry9?}D&qR&h%gI$sKR%~L-1i9)wkvazZM+Sga`nn|mS5 z$Z!*VDdq_UF-g?`b*n`UDt(1{1I*qxBo6ft0@QF(vKf>RCeQfFMj(PULWMOE?d}J_ zbO8R_uq3tgV~i~tI8#dNIB3%Y;rL;|>o9hC14cmlAjZBK7!f$n4BXxcq&d>lVgz2m zICn(sN*625pry;IKB|yvpry2_x6OjQ!=3#@==_LrXrybHM$AY+MK$VMu~0=KSYi5s zm1(6^mJ|AfmXWR=%$5!#G7r$YV`}b2?ah6y5q)o@t-EX3(oRi6E$bs_dIal0r_%3Y zdvSXts;z$n1J#6f;!2$veO8PLe`iGj{?2-)Q8Ay%Z&8CvMxz=gjH;ARNeyk0p>8Z2 z`kv+ix+#D%Z0+rDq3=>=qg8`<1>VdXM*4@ z*#IiVra)PRWx~p085+Ti#PsbN09cQ-s39aPFSQPgY~4zI*A;1vU;(89iOR8`2@;{B zAL{Ii^t9Q>7aFxSQM5!g0lfl-M!JSN(W8Svb`e^5Hn+9`L20YDf&ml&IV(m5kh7u) zK~2o0AgIpa-ky-yIy6+O2W$dmnpLby9jRc^A*_xrzrj<OOZWXSXNDEchhc(j6pqt1Gw_b9G3NSBax3s%#S zmWaBvX%FIN46}(YO7!V8)R~4hzzv9MpmY#`n|t-`plQ1Yh32+CvAv|M z#NN_1+ycZ7Y^)9gFk#Q2Wmvf>QI4K|RCI=zvQ2m%8JPH%;L17Stvbawfz0jSG-SXu z9qjLFlQ1zxHlvwcEwr`_b#EEKqSik$IJ98|ivq|2fJ(o<9cZ~HBGQEx@ZqijVQ7Sg zHXJt4=B8_7L}(f5;2XQ8O_8paerz22@P`Ct0lV_;m<}rDrnq2?`T^r>aF0rY)2pz( ztsnG&vi;CHzpUK45u`Y%Ql(8uRbFgUS2iW0sh^?(bSb3^ja7MwE@8Tq(WRU&6^4<% zu7;ADV)S)$31TWJQ$;B~Ql<*ZR6&_4C{qPxs;Cf~g2hUX778Ipuo%?@i-T%uwJ0c9 zj7-5|WC|7|Q?Qsal@!y3-j-0N63SG9YJw%GCRjo_N+?GOI4p?)>g>sZ?&8yc6tS?auu2)h})>5rX_)S#0r9Q0P zsqi3`5u{p!RBMoG4Jt1vYf#HNjVcaN#UUy-M43XADMXnfL=X`ohzJoxgo-PqjS=8d1PLTUR91*UB19k&B9I6XNQ4L^ zLIe__5~?IXl>{gU0Yiv@Aw<9sB47v+FoXygLIeyU0)`L)Lx_MOM8FUtU#BTP9k=(tdha0PlBIdGvI7<7av2Mv0N z20es9$AxmxpoeJCLp10i8uSnidWZ%+M1vlpK@ZWOhiK44H0U83^biethz31GgC3$m z4`I-8p&Wz>LWBuIzy$4qvWPN20_EzA3Q$d98u~B|eOSW>fpT>^1*pC-0YI1lAWSGB zOt2KD@ekAZhiUx7H2z^4|1gbzn8rU$;~%E+57YREY5c=9{$U#bFpYnh#y?EsAExmS z)A)x2>a+~hXf3Q!=X{_hptiiGRJ*GaE>NR2wML!!ftoVyeYtiYFRw;>uGQ{!+Pz-8 zPgC!;TD`Sey|r4swOYNkTD`Sey|r4swOYNkTD`Sey|r4swOYNkTD`Sey|r4s8qy5Z zY4z4=_10?v$(?k d0mRO}xo^G_%I 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 SdSQM28?+M|D(2r_;{O0|uQ74} diff --git a/docs/v3/v1/assets/fonts/specimen/FontAwesome.woff2 b/docs/v3/v1/assets/fonts/specimen/FontAwesome.woff2 deleted file mode 100644 index 4d13fc60404b91e398a37200c4a77b645cfd9586..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77160 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 diff --git a/docs/v3/v1/assets/fonts/specimen/MaterialIcons-Regular.ttf b/docs/v3/v1/assets/fonts/specimen/MaterialIcons-Regular.ttf deleted file mode 100644 index 7015564ad166a3e9d88c82f17829f0cc01ebe29a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128180 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 ohyj3!`#ee%L;DTjx@8!5k5~VH0QmdE^#A|> diff --git a/docs/v3/v1/assets/fonts/specimen/MaterialIcons-Regular.woff b/docs/v3/v1/assets/fonts/specimen/MaterialIcons-Regular.woff deleted file mode 100644 index b648a3eea2d16b6ce783906d6b7d5f251b9eb56c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57620 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

wF7DI|Kx|V1sBQ9FYs>m5C)C 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 BJ$(QG diff --git a/docs/v3/v1/assets/fonts/specimen/MaterialIcons-Regular.woff2 b/docs/v3/v1/assets/fonts/specimen/MaterialIcons-Regular.woff2 deleted file mode 100644 index 9fa211252080046a23b2449dbdced6abc2b0bb34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44300 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 QW8}wPUeo^@jR61v08@RD2LJ#7 diff --git a/docs/v3/v1/assets/images/favicon.png b/docs/v3/v1/assets/images/favicon.png deleted file mode 100644 index 76d17f57ad903c3ea2f1b564cafb95bf9af84ee3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 521 zcmV+k0`~ohP)kdg0005dNkl2WptjAn6@db&Pvy?U$ zv>P|<&rCZfZF0jmq0opf8)91(A<*iIVPPJJT((+JiF~>9KAA3%heFdnI;SaK+~|aU zQ~!x`%y{jX1<~SK2RxN7Db8`yWBbf6p7&07{VXfaam*cUs&eu*Zu(xaIL8rP){;a< zS~$}^Td32Rw+W1TqTd|L{#~jJet4!qwKsb5hq%YXiiUV!yH=ltu0>s|FLsT+Iy7K~ z!6*Z0a@vQ;AiZo!=s{{fqR+ct6YQPzbk+j}*qe7vtu39I7 zrOtZqU}=NnLchJxsU9iY+}3TYDl|BvPsX%E@dlyLgdV%q$UP|Y?DfcGb`}K&$;drd z+hL;zy7UTccUYU+h`ONIU|d=%`(0$=KW4%tVWXj~AE - - diff --git a/docs/v3/v1/assets/images/icons/github.f0b8504a.svg b/docs/v3/v1/assets/images/icons/github.f0b8504a.svg deleted file mode 100644 index c009420a7..000000000 --- a/docs/v3/v1/assets/images/icons/github.f0b8504a.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/docs/v3/v1/assets/images/icons/gitlab.6dd19c00.svg b/docs/v3/v1/assets/images/icons/gitlab.6dd19c00.svg deleted file mode 100644 index 9e3d6f05b..000000000 --- a/docs/v3/v1/assets/images/icons/gitlab.6dd19c00.svg +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/v3/v1/assets/javascripts/application.583bbe55.js b/docs/v3/v1/assets/javascripts/application.583bbe55.js deleted file mode 100644 index 7cc20810e..000000000 --- a/docs/v3/v1/assets/javascripts/application.583bbe55.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){for(var n in t)e[n]=t[n]}(window,function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=6)}([function(e,t,n){"use strict";t.__esModule=!0,t.default={createElement:function(e,t){var n=document.createElement(e);t&&Array.prototype.forEach.call(Object.keys(t),function(e){n.setAttribute(e,t[e])});for(var r=arguments.length,i=Array(r>2?r-2:0),o=2;o pre, pre > code");Array.prototype.forEach.call(n,function(t,n){var r="__code_"+n,i=e.createElement("button",{class:"md-clipboard",title:h("clipboard.copy"),"data-clipboard-target":"#"+r+" pre, #"+r+" code"},e.createElement("span",{class:"md-clipboard__message"})),o=t.parentNode;o.id=r,o.insertBefore(i,t)});new c.default(".md-clipboard").on("success",function(e){var t=e.trigger.querySelector(".md-clipboard__message");if(!(t instanceof HTMLElement))throw new ReferenceError;e.clearSelection(),t.dataset.mdTimer&&clearTimeout(parseInt(t.dataset.mdTimer,10)),t.classList.add("md-clipboard__message--active"),t.innerHTML=h("clipboard.copied"),t.dataset.mdTimer=setTimeout(function(){t.classList.remove("md-clipboard__message--active"),t.dataset.mdTimer=""},2e3).toString()})}if(!Modernizr.details){var r=document.querySelectorAll("details > summary");Array.prototype.forEach.call(r,function(e){e.addEventListener("click",function(e){var t=e.target.parentNode;t.hasAttribute("open")?t.removeAttribute("open"):t.setAttribute("open","")})})}var i=function(){if(document.location.hash){var e=document.getElementById(document.location.hash.substring(1));if(!e)return;for(var t=e.parentNode;t&&!(t instanceof HTMLDetailsElement);)t=t.parentNode;if(t&&!t.open){t.open=!0;var n=location.hash;location.hash=" ",location.hash=n}}};if(window.addEventListener("hashchange",i),i(),Modernizr.ios){var o=document.querySelectorAll("[data-md-scrollfix]");Array.prototype.forEach.call(o,function(e){e.addEventListener("touchstart",function(){var t=e.scrollTop;0===t?e.scrollTop=1:t+e.offsetHeight===e.scrollHeight&&(e.scrollTop=t-1)})})}}).listen(),new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Header.Shadow("[data-md-component=container]","[data-md-component=header]")).listen(),new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Header.Title("[data-md-component=title]",".md-typeset h1")).listen(),document.querySelector("[data-md-component=hero]")&&new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Tabs.Toggle("[data-md-component=hero]")).listen(),document.querySelector("[data-md-component=tabs]")&&new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Tabs.Toggle("[data-md-component=tabs]")).listen(),new f.default.Event.MatchMedia("(min-width: 1220px)",new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Sidebar.Position("[data-md-component=navigation]","[data-md-component=header]"))),document.querySelector("[data-md-component=toc]")&&new f.default.Event.MatchMedia("(min-width: 960px)",new f.default.Event.Listener(window,["scroll","resize","orientationchange"],new f.default.Sidebar.Position("[data-md-component=toc]","[data-md-component=header]"))),new f.default.Event.MatchMedia("(min-width: 960px)",new f.default.Event.Listener(window,"scroll",new f.default.Nav.Blur("[data-md-component=toc] [href]")));var n=document.querySelectorAll("[data-md-component=collapsible]");Array.prototype.forEach.call(n,function(e){new f.default.Event.MatchMedia("(min-width: 1220px)",new f.default.Event.Listener(e.previousElementSibling,"click",new f.default.Nav.Collapse(e)))}),new f.default.Event.MatchMedia("(max-width: 1219px)",new f.default.Event.Listener("[data-md-component=navigation] [data-md-toggle]","change",new f.default.Nav.Scrolling("[data-md-component=navigation] nav"))),document.querySelector("[data-md-component=search]")&&(new f.default.Event.MatchMedia("(max-width: 959px)",new f.default.Event.Listener("[data-md-toggle=search]","change",new f.default.Search.Lock("[data-md-toggle=search]"))),new f.default.Event.Listener("[data-md-component=query]",["focus","keyup","change"],new f.default.Search.Result("[data-md-component=result]",function(){return fetch(t.url.base+"/"+(t.version<"0.17"?"mkdocs":"search")+"/search_index.json",{credentials:"same-origin"}).then(function(e){return e.json()}).then(function(e){return e.docs.map(function(e){return e.location=t.url.base+"/"+e.location,e})})})).listen(),new f.default.Event.Listener("[data-md-component=reset]","click",function(){setTimeout(function(){var e=document.querySelector("[data-md-component=query]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.focus()},10)}).listen(),new f.default.Event.Listener("[data-md-toggle=search]","change",function(e){setTimeout(function(e){if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=document.querySelector("[data-md-component=query]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t.focus()}},400,e.target)}).listen(),new f.default.Event.MatchMedia("(min-width: 960px)",new f.default.Event.Listener("[data-md-component=query]","focus",function(){var e=document.querySelector("[data-md-toggle=search]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.checked||(e.checked=!0,e.dispatchEvent(new CustomEvent("change")))})),new f.default.Event.Listener(window,"keydown",function(e){var t=document.querySelector("[data-md-toggle=search]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;var n=document.querySelector("[data-md-component=query]");if(!(n instanceof HTMLInputElement))throw new ReferenceError;if(!e.metaKey&&!e.ctrlKey)if(t.checked){if(13===e.keyCode){if(n===document.activeElement){e.preventDefault();var r=document.querySelector("[data-md-component=search] [href][data-md-state=active]");r instanceof HTMLLinkElement&&(window.location=r.getAttribute("href"),t.checked=!1,t.dispatchEvent(new CustomEvent("change")),n.blur())}}else if(9===e.keyCode||27===e.keyCode)t.checked=!1,t.dispatchEvent(new CustomEvent("change")),n.blur();else if(-1!==[8,37,39].indexOf(e.keyCode))n!==document.activeElement&&n.focus();else if(-1!==[38,40].indexOf(e.keyCode)){var i=e.keyCode,o=Array.prototype.slice.call(document.querySelectorAll("[data-md-component=query], [data-md-component=search] [href]")),a=o.find(function(e){if(!(e instanceof HTMLElement))throw new ReferenceError;return"active"===e.dataset.mdState});a&&(a.dataset.mdState="");var s=Math.max(0,(o.indexOf(a)+o.length+(38===i?-1:1))%o.length);return o[s]&&(o[s].dataset.mdState="active",o[s].focus()),e.preventDefault(),e.stopPropagation(),!1}}else document.activeElement&&!document.activeElement.form&&(70!==e.keyCode&&83!==e.keyCode||(n.focus(),e.preventDefault()))}).listen(),new f.default.Event.Listener(window,"keypress",function(){var e=document.querySelector("[data-md-toggle=search]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=document.querySelector("[data-md-component=query]");if(!(t instanceof HTMLInputElement))throw new ReferenceError;t!==document.activeElement&&t.focus()}}).listen()),new f.default.Event.Listener(document.body,"keydown",function(e){if(9===e.keyCode){var t=document.querySelectorAll("[data-md-component=navigation] .md-nav__link[for]:not([tabindex])");Array.prototype.forEach.call(t,function(e){e.offsetHeight&&(e.tabIndex=0)})}}).listen(),new f.default.Event.Listener(document.body,"mousedown",function(){var e=document.querySelectorAll("[data-md-component=navigation] .md-nav__link[tabindex]");Array.prototype.forEach.call(e,function(e){e.removeAttribute("tabIndex")})}).listen(),document.body.addEventListener("click",function(){"tabbing"===document.body.dataset.mdState&&(document.body.dataset.mdState="")}),new f.default.Event.MatchMedia("(max-width: 959px)",new f.default.Event.Listener("[data-md-component=navigation] [href^='#']","click",function(){var e=document.querySelector("[data-md-toggle=drawer]");if(!(e instanceof HTMLInputElement))throw new ReferenceError;e.checked&&(e.checked=!1,e.dispatchEvent(new CustomEvent("change")))})),function(){var e=document.querySelector("[data-md-source]");if(!e)return a.default.resolve([]);if(!(e instanceof HTMLAnchorElement))throw new ReferenceError;switch(e.dataset.mdSource){case"github":return new f.default.Source.Adapter.GitHub(e).fetch();default:return a.default.resolve([])}}().then(function(e){var t=document.querySelectorAll("[data-md-source]");Array.prototype.forEach.call(t,function(t){new f.default.Source.Repository(t).initialize(e)})})}t.__esModule=!0,t.app=void 0,n(7),n(8),n(9),n(10),n(11),n(12),n(13);var o=n(14),a=r(o),s=n(19),c=r(s),u=n(20),l=r(u),d=n(21),f=r(d);window.Promise=window.Promise||a.default;var h=function(e){var t=document.getElementsByName("lang:"+e)[0];if(!(t instanceof HTMLMetaElement))throw new ReferenceError;return t.content},p={initialize:i};t.app=p}).call(t,n(0))},function(e,t,n){e.exports=n.p+"assets/images/icons/bitbucket.1b09e088.svg"},function(e,t,n){e.exports=n.p+"assets/images/icons/github.f0b8504a.svg"},function(e,t,n){e.exports=n.p+"assets/images/icons/gitlab.6dd19c00.svg"},function(e,t){},function(e,t){},function(e,t){!function(){if("undefined"!=typeof window)try{var e=new window.CustomEvent("test",{cancelable:!0});if(e.preventDefault(),!0!==e.defaultPrevented)throw new Error("Could not prevent default")}catch(e){var t=function(e,t){var n,r;return t=t||{bubbles:!1,cancelable:!1,detail:void 0},n=document.createEvent("CustomEvent"),n.initCustomEvent(e,t.bubbles,t.cancelable,t.detail),r=n.preventDefault,n.preventDefault=function(){r.call(this);try{Object.defineProperty(this,"defaultPrevented",{get:function(){return!0}})}catch(e){this.defaultPrevented=!0}},n};t.prototype=window.Event.prototype,window.CustomEvent=t}}()},function(e,t,n){window.fetch||(window.fetch=n(2).default||n(2))},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),function(e){function r(){}function i(e,t){return function(){e.apply(t,arguments)}}function o(e){if(!(this instanceof o))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],d(e,this)}function a(e,t){for(;3===e._state;)e=e._value;if(0===e._state)return void e._deferreds.push(t);e._handled=!0,o._immediateFn(function(){var n=1===e._state?t.onFulfilled:t.onRejected;if(null===n)return void(1===e._state?s:c)(t.promise,e._value);var r;try{r=n(e._value)}catch(e){return void c(t.promise,e)}s(t.promise,r)})}function s(e,t){try{if(t===e)throw new TypeError("A promise cannot be resolved with itself.");if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if(t instanceof o)return e._state=3,e._value=t,void u(e);if("function"==typeof n)return void d(i(n,t),e)}e._state=1,e._value=t,u(e)}catch(t){c(e,t)}}function c(e,t){e._state=2,e._value=t,u(e)}function u(e){2===e._state&&0===e._deferreds.length&&o._immediateFn(function(){e._handled||o._unhandledRejectionFn(e._value)});for(var t=0,n=e._deferreds.length;t=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n(16),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(t,n(1))},function(e,t,n){(function(e,t){!function(e,n){"use strict";function r(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n1)for(var n=1;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=e.action,this.container=e.container,this.emitter=e.emitter,this.target=e.target,this.text=e.text,this.trigger=e.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var e=this,t="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return e.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[t?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,r.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,r.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var e=void 0;try{e=document.execCommand(this.action)}catch(t){e=!1}this.handleResult(e)}},{key:"handleResult",value:function(e){this.emitter.emit(e?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=e,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(e){if(void 0!==e){if(!e||"object"!==(void 0===e?"undefined":i(e))||1!==e.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&e.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(e.hasAttribute("readonly")||e.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=e}},get:function(){return this._target}}]),e}();e.exports=a})},function(e,t,n){function r(e,t,n){if(!e&&!t&&!n)throw new Error("Missing required arguments");if(!s.string(t))throw new TypeError("Second argument must be a String");if(!s.fn(n))throw new TypeError("Third argument must be a Function");if(s.node(e))return i(e,t,n);if(s.nodeList(e))return o(e,t,n);if(s.string(e))return a(e,t,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function i(e,t,n){return e.addEventListener(t,n),{destroy:function(){e.removeEventListener(t,n)}}}function o(e,t,n){return Array.prototype.forEach.call(e,function(e){e.addEventListener(t,n)}),{destroy:function(){Array.prototype.forEach.call(e,function(e){e.removeEventListener(t,n)})}}}function a(e,t,n){return c(document.body,e,t,n)}var s=n(6),c=n(5);e.exports=r},function(e,t){function n(){}n.prototype={on:function(e,t,n){var r=this.e||(this.e={});return(r[e]||(r[e]=[])).push({fn:t,ctx:n}),this},once:function(e,t,n){function r(){i.off(e,r),t.apply(n,arguments)}var i=this;return r._=t,this.on(e,r,n)},emit:function(e){var t=[].slice.call(arguments,1),n=((this.e||(this.e={}))[e]||[]).slice(),r=0,i=n.length;for(r;r0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof e.action?e.action:this.defaultAction,this.target="function"==typeof e.target?e.target:this.defaultTarget,this.text="function"==typeof e.text?e.text:this.defaultText,this.container="object"===f(e.container)?e.container:document.body}},{key:"listenClick",value:function(e){var t=this;this.listener=(0,d.default)(e,"click",function(e){return t.onClick(e)})}},{key:"onClick",value:function(e){var t=e.delegateTarget||e.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new u.default({action:this.action(t),target:this.target(t),text:this.text(t),container:this.container,trigger:t,emitter:this})}},{key:"defaultAction",value:function(e){return c("action",e)}},{key:"defaultTarget",value:function(e){var t=c("target",e);if(t)return document.querySelector(t)}},{key:"defaultText",value:function(e){return c("text",e)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],t="string"==typeof e?[e]:e,n=!!document.queryCommandSupported;return t.forEach(function(e){n=n&&!!document.queryCommandSupported(e)}),n}}]),t}(l.default);e.exports=p})},function(e,t){function n(e,t){for(;e&&e.nodeType!==r;){if("function"==typeof e.matches&&e.matches(t))return e;e=e.parentNode}}var r=9;if("undefined"!=typeof Element&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}e.exports=n},function(e,t,n){function r(e,t,n,r,i){var a=o.apply(this,arguments);return e.addEventListener(n,a,i),{destroy:function(){e.removeEventListener(n,a,i)}}}function i(e,t,n,i,o){return"function"==typeof e.addEventListener?r.apply(null,arguments):"function"==typeof n?r.bind(null,document).apply(null,arguments):("string"==typeof e&&(e=document.querySelectorAll(e)),Array.prototype.map.call(e,function(e){return r(e,t,n,i,o)}))}function o(e,t,n,r){return function(n){n.delegateTarget=a(n.target,t),n.delegateTarget&&r.call(e,n)}}var a=n(4);e.exports=i},function(e,t){t.node=function(e){return void 0!==e&&e instanceof HTMLElement&&1===e.nodeType},t.nodeList=function(e){var n=Object.prototype.toString.call(e);return void 0!==e&&("[object NodeList]"===n||"[object HTMLCollection]"===n)&&"length"in e&&(0===e.length||t.node(e[0]))},t.string=function(e){return"string"==typeof e||e instanceof String},t.fn=function(e){return"[object Function]"===Object.prototype.toString.call(e)}},function(e,t){function n(e){var t;if("SELECT"===e.nodeName)e.focus(),t=e.value;else if("INPUT"===e.nodeName||"TEXTAREA"===e.nodeName){var n=e.hasAttribute("readonly");n||e.setAttribute("readonly",""),e.select(),e.setSelectionRange(0,e.value.length),n||e.removeAttribute("readonly"),t=e.value}else{e.hasAttribute("contenteditable")&&e.focus();var r=window.getSelection(),i=document.createRange();i.selectNodeContents(e),r.removeAllRanges(),r.addRange(i),t=r.toString()}return t}e.exports=n}])})},function(e,t,n){var r;!function(){"use strict";function i(e,t){var n;if(t=t||{},this.trackingClick=!1,this.trackingClickStart=0,this.targetElement=null,this.touchStartX=0,this.touchStartY=0,this.lastTouchIdentifier=0,this.touchBoundary=t.touchBoundary||10,this.layer=e,this.tapDelay=t.tapDelay||200,this.tapTimeout=t.tapTimeout||700,!i.notNeeded(e)){for(var r=["onMouse","onClick","onTouchStart","onTouchMove","onTouchEnd","onTouchCancel"],o=this,s=0,c=r.length;s=0,a=navigator.userAgent.indexOf("Android")>0&&!o,s=/iP(ad|hone|od)/.test(navigator.userAgent)&&!o,c=s&&/OS 4_\d(_\d)?/.test(navigator.userAgent),u=s&&/OS [6-7]_\d/.test(navigator.userAgent),l=navigator.userAgent.indexOf("BB10")>0;i.prototype.needsClick=function(e){switch(e.nodeName.toLowerCase()){case"button":case"select":case"textarea":if(e.disabled)return!0;break;case"input":if(s&&"file"===e.type||e.disabled)return!0;break;case"label":case"iframe":case"video":return!0}return/\bneedsclick\b/.test(e.className)},i.prototype.needsFocus=function(e){switch(e.nodeName.toLowerCase()){case"textarea":return!0;case"select":return!a;case"input":switch(e.type){case"button":case"checkbox":case"file":case"image":case"radio":case"submit":return!1}return!e.disabled&&!e.readOnly;default:return/\bneedsfocus\b/.test(e.className)}},i.prototype.sendClick=function(e,t){var n,r;document.activeElement&&document.activeElement!==e&&document.activeElement.blur(),r=t.changedTouches[0],n=document.createEvent("MouseEvents"),n.initMouseEvent(this.determineEventType(e),!0,!0,window,1,r.screenX,r.screenY,r.clientX,r.clientY,!1,!1,!1,!1,0,null),n.forwardedTouchEvent=!0,e.dispatchEvent(n)},i.prototype.determineEventType=function(e){return a&&"select"===e.tagName.toLowerCase()?"mousedown":"click"},i.prototype.focus=function(e){var t;s&&e.setSelectionRange&&0!==e.type.indexOf("date")&&"time"!==e.type&&"month"!==e.type?(t=e.value.length,e.setSelectionRange(t,t)):e.focus()},i.prototype.updateScrollParent=function(e){var t,n;if(!(t=e.fastClickScrollParent)||!t.contains(e)){n=e;do{if(n.scrollHeight>n.offsetHeight){t=n,e.fastClickScrollParent=n;break}n=n.parentElement}while(n)}t&&(t.fastClickLastScrollTop=t.scrollTop)},i.prototype.getTargetElementFromEventTarget=function(e){return e.nodeType===Node.TEXT_NODE?e.parentNode:e},i.prototype.onTouchStart=function(e){var t,n,r;if(e.targetTouches.length>1)return!0;if(t=this.getTargetElementFromEventTarget(e.target),n=e.targetTouches[0],s){if(r=window.getSelection(),r.rangeCount&&!r.isCollapsed)return!0;if(!c){if(n.identifier&&n.identifier===this.lastTouchIdentifier)return e.preventDefault(),!1;this.lastTouchIdentifier=n.identifier,this.updateScrollParent(t)}}return this.trackingClick=!0,this.trackingClickStart=e.timeStamp,this.targetElement=t,this.touchStartX=n.pageX,this.touchStartY=n.pageY,e.timeStamp-this.lastClickTimen||Math.abs(t.pageY-this.touchStartY)>n},i.prototype.onTouchMove=function(e){return!this.trackingClick||((this.targetElement!==this.getTargetElementFromEventTarget(e.target)||this.touchHasMoved(e))&&(this.trackingClick=!1,this.targetElement=null),!0)},i.prototype.findControl=function(e){return void 0!==e.control?e.control:e.htmlFor?document.getElementById(e.htmlFor):e.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea")},i.prototype.onTouchEnd=function(e){var t,n,r,i,o,l=this.targetElement;if(!this.trackingClick)return!0;if(e.timeStamp-this.lastClickTimethis.tapTimeout)return!0;if(this.cancelNextClick=!1,this.lastClickTime=e.timeStamp,n=this.trackingClickStart,this.trackingClick=!1,this.trackingClickStart=0,u&&(o=e.changedTouches[0],l=document.elementFromPoint(o.pageX-window.pageXOffset,o.pageY-window.pageYOffset)||l,l.fastClickScrollParent=this.targetElement.fastClickScrollParent),"label"===(r=l.tagName.toLowerCase())){if(t=this.findControl(l)){if(this.focus(l),a)return!1;l=t}}else if(this.needsFocus(l))return e.timeStamp-n>100||s&&window.top!==window&&"input"===r?(this.targetElement=null,!1):(this.focus(l),this.sendClick(l,e),s&&"select"===r||(this.targetElement=null,e.preventDefault()),!1);return!(!s||c||!(i=l.fastClickScrollParent)||i.fastClickLastScrollTop===i.scrollTop)||(this.needsClick(l)||(e.preventDefault(),this.sendClick(l,e)),!1)},i.prototype.onTouchCancel=function(){this.trackingClick=!1,this.targetElement=null},i.prototype.onMouse=function(e){return!this.targetElement||(!!e.forwardedTouchEvent||(!e.cancelable||(!(!this.needsClick(this.targetElement)||this.cancelNextClick)||(e.stopImmediatePropagation?e.stopImmediatePropagation():e.propagationStopped=!0,e.stopPropagation(),e.preventDefault(),!1))))},i.prototype.onClick=function(e){var t;return this.trackingClick?(this.targetElement=null,this.trackingClick=!1,!0):"submit"===e.target.type&&0===e.detail||(t=this.onMouse(e),t||(this.targetElement=null),t)},i.prototype.destroy=function(){var e=this.layer;a&&(e.removeEventListener("mouseover",this.onMouse,!0),e.removeEventListener("mousedown",this.onMouse,!0),e.removeEventListener("mouseup",this.onMouse,!0)),e.removeEventListener("click",this.onClick,!0),e.removeEventListener("touchstart",this.onTouchStart,!1),e.removeEventListener("touchmove",this.onTouchMove,!1),e.removeEventListener("touchend",this.onTouchEnd,!1),e.removeEventListener("touchcancel",this.onTouchCancel,!1)},i.notNeeded=function(e){var t,n,r;if(void 0===window.ontouchstart)return!0;if(n=+(/Chrome\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1]){if(!a)return!0;if(t=document.querySelector("meta[name=viewport]")){if(-1!==t.content.indexOf("user-scalable=no"))return!0;if(n>31&&document.documentElement.scrollWidth<=window.outerWidth)return!0}}if(l&&(r=navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/),r[1]>=10&&r[2]>=3&&(t=document.querySelector("meta[name=viewport]")))){if(-1!==t.content.indexOf("user-scalable=no"))return!0;if(document.documentElement.scrollWidth<=window.outerWidth)return!0}return"none"===e.style.msTouchAction||"manipulation"===e.style.touchAction||(!!(+(/Firefox\/([0-9]+)/.exec(navigator.userAgent)||[,0])[1]>=27&&(t=document.querySelector("meta[name=viewport]"))&&(-1!==t.content.indexOf("user-scalable=no")||document.documentElement.scrollWidth<=window.outerWidth))||("none"===e.style.touchAction||"manipulation"===e.style.touchAction))},i.attach=function(e,t){return new i(e,t)},void 0!==(r=function(){return i}.call(t,n,t,e))&&(e.exports=r)}()},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(22),o=r(i),a=n(24),s=r(a),c=n(27),u=r(c),l=n(31),d=r(l),f=n(37),h=r(f),p=n(39),m=r(p),v=n(45),y=r(v);t.default={Event:o.default,Header:s.default,Nav:u.default,Search:d.default,Sidebar:h.default,Source:m.default,Tabs:y.default}},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(3),o=r(i),a=n(23),s=r(a);t.default={Listener:o.default,MatchMedia:s.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=n(3),o=(function(e){e&&e.__esModule}(i),function e(t,n){r(this,e),this.handler_=function(e){e.matches?n.listen():n.unlisten()};var i=window.matchMedia(t);i.addListener(this.handler_),this.handler_(i)});t.default=o},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(25),o=r(i),a=n(26),s=r(a);t.default={Shadow:o.default,Title:s.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t,n){r(this,e);var i="string"==typeof t?document.querySelector(t):t;if(!(i instanceof HTMLElement&&i.parentNode instanceof HTMLElement))throw new ReferenceError;if(this.el_=i.parentNode,!((i="string"==typeof n?document.querySelector(n):n)instanceof HTMLElement))throw new ReferenceError;this.header_=i,this.height_=0,this.active_=!1}return e.prototype.setup=function(){for(var e=this.el_;e=e.previousElementSibling;){if(!(e instanceof HTMLElement))throw new ReferenceError;this.height_+=e.offsetHeight}this.update()},e.prototype.update=function(e){if(!e||"resize"!==e.type&&"orientationchange"!==e.type){var t=window.pageYOffset>=this.height_;t!==this.active_&&(this.header_.dataset.mdState=(this.active_=t)?"shadow":"")}else this.height_=0,this.setup()},e.prototype.reset=function(){this.header_.dataset.mdState="",this.height_=0,this.active_=!1},e}();t.default=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t,n){r(this,e);var i="string"==typeof t?document.querySelector(t):t;if(!(i instanceof HTMLElement))throw new ReferenceError;if(this.el_=i,!((i="string"==typeof n?document.querySelector(n):n)instanceof HTMLHeadingElement))throw new ReferenceError;this.header_=i,this.active_=!1}return e.prototype.setup=function(){var e=this;Array.prototype.forEach.call(this.el_.children,function(t){t.style.width=e.el_.offsetWidth-20+"px"})},e.prototype.update=function(e){var t=this,n=window.pageYOffset>=this.header_.offsetTop;n!==this.active_&&(this.el_.dataset.mdState=(this.active_=n)?"active":""),"resize"!==e.type&&"orientationchange"!==e.type||Array.prototype.forEach.call(this.el_.children,function(e){e.style.width=t.el_.offsetWidth-20+"px"})},e.prototype.reset=function(){this.el_.dataset.mdState="",this.el_.style.width="",this.active_=!1},e}();t.default=i},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(28),o=r(i),a=n(29),s=r(a),c=n(30),u=r(c);t.default={Blur:o.default,Collapse:s.default,Scrolling:u.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t){r(this,e),this.els_="string"==typeof t?document.querySelectorAll(t):t,this.index_=0,this.offset_=window.pageYOffset,this.dir_=!1,this.anchors_=[].reduce.call(this.els_,function(e,t){return e.concat(document.getElementById(t.hash.substring(1))||[])},[])}return e.prototype.setup=function(){this.update()},e.prototype.update=function(){var e=window.pageYOffset,t=this.offset_-e<0;if(this.dir_!==t&&(this.index_=this.index_=t?0:this.els_.length-1),0!==this.anchors_.length){if(this.offset_<=e)for(var n=this.index_+1;n0&&(this.els_[n-1].dataset.mdState="blur"),this.index_=n;else for(var r=this.index_;r>=0;r--){if(!(this.anchors_[r].offsetTop-80>e)){this.index_=r;break}r>0&&(this.els_[r-1].dataset.mdState="")}this.offset_=e,this.dir_=t}},e.prototype.reset=function(){Array.prototype.forEach.call(this.els_,function(e){e.dataset.mdState=""}),this.index_=0,this.offset_=window.pageYOffset},e}();t.default=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t){r(this,e);var n="string"==typeof t?document.querySelector(t):t;if(!(n instanceof HTMLElement))throw new ReferenceError;this.el_=n}return e.prototype.setup=function(){var e=this.el_.getBoundingClientRect().height;this.el_.style.display=e?"block":"none",this.el_.style.overflow=e?"visible":"hidden"},e.prototype.update=function(){var e=this,t=this.el_.getBoundingClientRect().height;if(this.el_.style.display="block",this.el_.style.overflow="",t)this.el_.style.maxHeight=t+"px",requestAnimationFrame(function(){e.el_.setAttribute("data-md-state","animate"),e.el_.style.maxHeight="0px"});else{this.el_.setAttribute("data-md-state","expand"),this.el_.style.maxHeight="";var n=this.el_.getBoundingClientRect().height;this.el_.removeAttribute("data-md-state"),this.el_.style.maxHeight="0px",requestAnimationFrame(function(){e.el_.setAttribute("data-md-state","animate"),e.el_.style.maxHeight=n+"px"})}var r=function e(n){var r=n.target;if(!(r instanceof HTMLElement))throw new ReferenceError;r.removeAttribute("data-md-state"),r.style.maxHeight="",r.style.display=t?"none":"block",r.style.overflow=t?"hidden":"visible",r.removeEventListener("transitionend",e)};this.el_.addEventListener("transitionend",r,!1)},e.prototype.reset=function(){this.el_.dataset.mdState="",this.el_.style.maxHeight="",this.el_.style.display="",this.el_.style.overflow=""},e}();t.default=i},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t){r(this,e);var n="string"==typeof t?document.querySelector(t):t;if(!(n instanceof HTMLElement))throw new ReferenceError;this.el_=n}return e.prototype.setup=function(){this.el_.children[this.el_.children.length-1].style.webkitOverflowScrolling="touch";var e=this.el_.querySelectorAll("[data-md-toggle]");Array.prototype.forEach.call(e,function(e){if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=e.nextElementSibling;if(!(t instanceof HTMLElement))throw new ReferenceError;for(;"NAV"!==t.tagName&&t.nextElementSibling;)t=t.nextElementSibling;if(!(e.parentNode instanceof HTMLElement&&e.parentNode.parentNode instanceof HTMLElement))throw new ReferenceError;var n=e.parentNode.parentNode,r=t.children[t.children.length-1];n.style.webkitOverflowScrolling="",r.style.webkitOverflowScrolling="touch"}})},e.prototype.update=function(e){var t=e.target;if(!(t instanceof HTMLElement))throw new ReferenceError;var n=t.nextElementSibling;if(!(n instanceof HTMLElement))throw new ReferenceError;for(;"NAV"!==n.tagName&&n.nextElementSibling;)n=n.nextElementSibling;if(!(t.parentNode instanceof HTMLElement&&t.parentNode.parentNode instanceof HTMLElement))throw new ReferenceError;var r=t.parentNode.parentNode,i=n.children[n.children.length-1];if(r.style.webkitOverflowScrolling="",i.style.webkitOverflowScrolling="",!t.checked){var o=function e(){n instanceof HTMLElement&&(r.style.webkitOverflowScrolling="touch",n.removeEventListener("transitionend",e))};n.addEventListener("transitionend",o,!1)}if(t.checked){var a=function e(){n instanceof HTMLElement&&(i.style.webkitOverflowScrolling="touch",n.removeEventListener("transitionend",e))};n.addEventListener("transitionend",a,!1)}},e.prototype.reset=function(){this.el_.children[1].style.webkitOverflowScrolling="";var e=this.el_.querySelectorAll("[data-md-toggle]");Array.prototype.forEach.call(e,function(e){if(!(e instanceof HTMLInputElement))throw new ReferenceError;if(e.checked){var t=e.nextElementSibling;if(!(t instanceof HTMLElement))throw new ReferenceError;for(;"NAV"!==t.tagName&&t.nextElementSibling;)t=t.nextElementSibling;if(!(e.parentNode instanceof HTMLElement&&e.parentNode.parentNode instanceof HTMLElement))throw new ReferenceError;var n=e.parentNode.parentNode,r=t.children[t.children.length-1];n.style.webkitOverflowScrolling="",r.style.webkitOverflowScrolling=""}})},e}();t.default=i},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(32),o=r(i),a=n(33),s=r(a);t.default={Lock:o.default,Result:s.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t){r(this,e);var n="string"==typeof t?document.querySelector(t):t;if(!(n instanceof HTMLInputElement))throw new ReferenceError;if(this.el_=n,!document.body)throw new ReferenceError;this.lock_=document.body}return e.prototype.setup=function(){this.update()},e.prototype.update=function(){var e=this;this.el_.checked?(this.offset_=window.pageYOffset,setTimeout(function(){window.scrollTo(0,0),e.el_.checked&&(e.lock_.dataset.mdState="lock")},400)):(this.lock_.dataset.mdState="",setTimeout(function(){void 0!==e.offset_&&window.scrollTo(0,e.offset_)},100))},e.prototype.reset=function(){"lock"===this.lock_.dataset.mdState&&window.scrollTo(0,this.offset_),this.lock_.dataset.mdState=""},e}();t.default=i},function(e,t,n){"use strict";(function(e){function r(e){return e&&e.__esModule?e:{default:e}}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var o=n(34),a=r(o),s=n(35),c=r(s),u=function(e,t){var n=t;if(e.length>n){for(;" "!==e[n]&&--n>0;);return e.substring(0,n)+"..."}return e},l=function(e){var t=document.getElementsByName("lang:"+e)[0];if(!(t instanceof HTMLMetaElement))throw new ReferenceError;return t.content},d=function(){function t(e,n){i(this,t);var r="string"==typeof e?document.querySelector(e):e;if(!(r instanceof HTMLElement))throw new ReferenceError;this.el_=r;var o=Array.prototype.slice.call(this.el_.children),a=o[0],s=o[1];this.data_=n,this.meta_=a,this.list_=s,this.message_={placeholder:this.meta_.textContent,none:l("search.result.none"),one:l("search.result.one"),other:l("search.result.other")};var u=l("search.tokenizer");u.length&&(c.default.tokenizer.separator=u),this.lang_=l("search.language").split(",").filter(Boolean).map(function(e){return e.trim()})}return t.prototype.update=function(t){var n=this;if("focus"!==t.type||this.index_){if("focus"===t.type||"keyup"===t.type){var r=t.target;if(!(r instanceof HTMLInputElement))throw new ReferenceError;if(!this.index_||r.value===this.value_)return;for(;this.list_.firstChild;)this.list_.removeChild(this.list_.firstChild);if(this.value_=r.value,0===this.value_.length)return void(this.meta_.textContent=this.message_.placeholder);var i=this.index_.query(function(e){n.value_.toLowerCase().split(" ").filter(Boolean).forEach(function(t){e.term(t,{wildcard:c.default.Query.wildcard.TRAILING})})}).reduce(function(e,t){var r=n.docs_.get(t.ref);if(r.parent){var i=r.parent.location;e.set(i,(e.get(i)||[]).concat(t))}else{var o=r.location;e.set(o,e.get(o)||[])}return e},new Map),o=(0,a.default)(this.value_.trim()).replace(new RegExp(c.default.tokenizer.separator,"img"),"|"),s=new RegExp("(^|"+c.default.tokenizer.separator+")("+o+")","img"),d=function(e,t,n){return t+""+n+""};this.stack_=[],i.forEach(function(t,r){var i,o=n.docs_.get(r),a=e.createElement("li",{class:"md-search-result__item"},e.createElement("a",{href:o.location,title:o.title,class:"md-search-result__link",tabindex:"-1"},e.createElement("article",{class:"md-search-result__article md-search-result__article--document"},e.createElement("h1",{class:"md-search-result__title"},{__html:o.title.replace(s,d)}),o.text.length?e.createElement("p",{class:"md-search-result__teaser"},{__html:o.text.replace(s,d)}):{}))),c=t.map(function(t){return function(){var r=n.docs_.get(t.ref);a.appendChild(e.createElement("a",{href:r.location,title:r.title,class:"md-search-result__link","data-md-rel":"anchor",tabindex:"-1"},e.createElement("article",{class:"md-search-result__article"},e.createElement("h1",{class:"md-search-result__title"},{__html:r.title.replace(s,d)}),r.text.length?e.createElement("p",{class:"md-search-result__teaser"},{__html:u(r.text.replace(s,d),400)}):{})))}});(i=n.stack_).push.apply(i,[function(){return n.list_.appendChild(a)}].concat(c))});var f=this.el_.parentNode;if(!(f instanceof HTMLElement))throw new ReferenceError;for(;this.stack_.length&&f.offsetHeight>=f.scrollHeight-16;)this.stack_.shift()();var h=this.list_.querySelectorAll("[data-md-rel=anchor]");switch(Array.prototype.forEach.call(h,function(e){["click","keydown"].forEach(function(t){e.addEventListener(t,function(n){if("keydown"!==t||13===n.keyCode){var r=document.querySelector("[data-md-toggle=search]");if(!(r instanceof HTMLInputElement))throw new ReferenceError;r.checked&&(r.checked=!1,r.dispatchEvent(new CustomEvent("change"))),n.preventDefault(),setTimeout(function(){document.location.href=e.href},100)}})})}),i.size){case 0:this.meta_.textContent=this.message_.none;break;case 1:this.meta_.textContent=this.message_.one;break;default:this.meta_.textContent=this.message_.other.replace("#",i.size)}}}else{var p=function(e){n.docs_=e.reduce(function(e,t){var n=t.location.split("#"),r=n[0];return n[1]&&(t.parent=e.get(r),t.parent&&!t.parent.done&&(t.parent.title=t.title,t.parent.text=t.text,t.parent.done=!0)),t.text=t.text.replace(/\n/g," ").replace(/\s+/g," ").replace(/\s+([,.:;!?])/g,function(e,t){return t}),t.parent&&t.parent.title===t.title||e.set(t.location,t),e},new Map);var t=n.docs_,r=n.lang_;n.stack_=[],n.index_=(0,c.default)(function(){var e,n=this,i={"search.pipeline.trimmer":c.default.trimmer,"search.pipeline.stopwords":c.default.stopWordFilter},o=Object.keys(i).reduce(function(e,t){return l(t).match(/^false$/i)||e.push(i[t]),e},[]);this.pipeline.reset(),o&&(e=this.pipeline).add.apply(e,o),1===r.length&&"en"!==r[0]&&c.default[r[0]]?this.use(c.default[r[0]]):r.length>1&&this.use(c.default.multiLanguage.apply(c.default,r)),this.field("title",{boost:10}),this.field("text"),this.ref("location"),t.forEach(function(e){return n.add(e)})});var i=n.el_.parentNode;if(!(i instanceof HTMLElement))throw new ReferenceError;i.addEventListener("scroll",function(){for(;n.stack_.length&&i.scrollTop+i.offsetHeight>=i.scrollHeight-16;)n.stack_.splice(0,10).forEach(function(e){return e()})})};setTimeout(function(){return"function"==typeof n.data_?n.data_().then(p):p(n.data_)},250)}},t}();t.default=d}).call(t,n(0))},function(e,t,n){"use strict";var r=/[|\\{}()[\]^$+*?.]/g;e.exports=function(e){if("string"!=typeof e)throw new TypeError("Expected a string");return e.replace(r,"\\$&")}},function(e,t,n){(function(t){e.exports=t.lunr=n(36)}).call(t,n(1))},function(e,t,n){var r,i;!function(){var o=function(e){var t=new o.Builder;return t.pipeline.add(o.trimmer,o.stopWordFilter,o.stemmer),t.searchPipeline.add(o.stemmer),e.call(t,t),t.build()};o.version="2.1.5",o.utils={},o.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),o.utils.asString=function(e){return void 0===e||null===e?"":e.toString()},o.FieldRef=function(e,t,n){this.docRef=e,this.fieldName=t,this._stringValue=n},o.FieldRef.joiner="/",o.FieldRef.fromString=function(e){var t=e.indexOf(o.FieldRef.joiner);if(-1===t)throw"malformed field ref string";var n=e.slice(0,t),r=e.slice(t+1);return new o.FieldRef(r,n,e)},o.FieldRef.prototype.toString=function(){return void 0==this._stringValue&&(this._stringValue=this.fieldName+o.FieldRef.joiner+this.docRef),this._stringValue},o.idf=function(e,t){var n=0;for(var r in e)"_index"!=r&&(n+=Object.keys(e[r]).length);var i=(t-n+.5)/(n+.5);return Math.log(1+Math.abs(i))},o.Token=function(e,t){this.str=e||"",this.metadata=t||{}},o.Token.prototype.toString=function(){return this.str},o.Token.prototype.update=function(e){return this.str=e(this.str,this.metadata),this},o.Token.prototype.clone=function(e){return e=e||function(e){return e},new o.Token(e(this.str,this.metadata),this.metadata)},o.tokenizer=function(e){if(null==e||void 0==e)return[];if(Array.isArray(e))return e.map(function(e){return new o.Token(o.utils.asString(e).toLowerCase())});for(var t=e.toString().trim().toLowerCase(),n=t.length,r=[],i=0,a=0;i<=n;i++){var s=t.charAt(i),c=i-a;(s.match(o.tokenizer.separator)||i==n)&&(c>0&&r.push(new o.Token(t.slice(a,i),{position:[a,c],index:r.length})),a=i+1)}return r},o.tokenizer.separator=/[\s\-]+/,o.Pipeline=function(){this._stack=[]},o.Pipeline.registeredFunctions=Object.create(null),o.Pipeline.registerFunction=function(e,t){t in this.registeredFunctions&&o.utils.warn("Overwriting existing registered function: "+t),e.label=t,o.Pipeline.registeredFunctions[e.label]=e},o.Pipeline.warnIfFunctionNotRegistered=function(e){e.label&&e.label in this.registeredFunctions||o.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},o.Pipeline.load=function(e){var t=new o.Pipeline;return e.forEach(function(e){var n=o.Pipeline.registeredFunctions[e];if(!n)throw new Error("Cannot load unregistered function: "+e);t.add(n)}),t},o.Pipeline.prototype.add=function(){Array.prototype.slice.call(arguments).forEach(function(e){o.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},o.Pipeline.prototype.after=function(e,t){o.Pipeline.warnIfFunctionNotRegistered(t);var n=this._stack.indexOf(e);if(-1==n)throw new Error("Cannot find existingFn");n+=1,this._stack.splice(n,0,t)},o.Pipeline.prototype.before=function(e,t){o.Pipeline.warnIfFunctionNotRegistered(t);var n=this._stack.indexOf(e);if(-1==n)throw new Error("Cannot find existingFn");this._stack.splice(n,0,t)},o.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);-1!=t&&this._stack.splice(t,1)},o.Pipeline.prototype.run=function(e){for(var t=this._stack.length,n=0;n1&&(oe&&(n=i),o!=e);)r=n-t,i=t+Math.floor(r/2),o=this.elements[2*i];return o==e?2*i:o>e?2*i:os?u+=2:a==s&&(t+=n[c+1]*r[u+1],c+=2,u+=2);return t},o.Vector.prototype.similarity=function(e){return this.dot(e)/(this.magnitude()*e.magnitude())},o.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,n=0;t0){var a,s=i.str.charAt(0);s in i.node.edges?a=i.node.edges[s]:(a=new o.TokenSet,i.node.edges[s]=a),1==i.str.length?a.final=!0:r.push({node:a,editsRemaining:i.editsRemaining,str:i.str.slice(1)})}if(i.editsRemaining>0&&i.str.length>1){var c,s=i.str.charAt(1);s in i.node.edges?c=i.node.edges[s]:(c=new o.TokenSet,i.node.edges[s]=c),i.str.length<=2?c.final=!0:r.push({node:c,editsRemaining:i.editsRemaining-1,str:i.str.slice(2)})}if(i.editsRemaining>0&&1==i.str.length&&(i.node.final=!0),i.editsRemaining>0&&i.str.length>=1){if("*"in i.node.edges)var u=i.node.edges["*"];else{var u=new o.TokenSet;i.node.edges["*"]=u}1==i.str.length?u.final=!0:r.push({node:u,editsRemaining:i.editsRemaining-1,str:i.str.slice(1)})}if(i.editsRemaining>0){if("*"in i.node.edges)var l=i.node.edges["*"];else{var l=new o.TokenSet;i.node.edges["*"]=l}0==i.str.length?l.final=!0:r.push({node:l,editsRemaining:i.editsRemaining-1,str:i.str})}if(i.editsRemaining>0&&i.str.length>1){var d,f=i.str.charAt(0),h=i.str.charAt(1);h in i.node.edges?d=i.node.edges[h]:(d=new o.TokenSet,i.node.edges[h]=d),1==i.str.length?d.final=!0:r.push({node:d,editsRemaining:i.editsRemaining-1,str:f+i.str.slice(2)})}}return n},o.TokenSet.fromString=function(e){for(var t=new o.TokenSet,n=t,r=!1,i=0,a=e.length;i=e;t--){var n=this.uncheckedNodes[t],r=n.child.toString();r in this.minimizedNodes?n.parent.edges[n.char]=this.minimizedNodes[r]:(n.child._str=r,this.minimizedNodes[r]=n.child),this.uncheckedNodes.pop()}},o.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},o.Index.prototype.search=function(e){return this.query(function(t){new o.QueryParser(e,t).parse()})},o.Index.prototype.query=function(e){var t=new o.Query(this.fields),n=Object.create(null),r=Object.create(null),i=Object.create(null);e.call(t,t);for(var a=0;a1?1:e},o.Builder.prototype.k1=function(e){this._k1=e},o.Builder.prototype.add=function(e){var t=e[this._ref];this.documentCount+=1;for(var n=0;n=this.length)return o.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},o.QueryLexer.prototype.width=function(){return this.pos-this.start},o.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},o.QueryLexer.prototype.backup=function(){this.pos-=1},o.QueryLexer.prototype.acceptDigitRun=function(){var e,t;do{e=this.next(),t=e.charCodeAt(0)}while(t>47&&t<58);e!=o.QueryLexer.EOS&&this.backup()},o.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(o.QueryLexer.TERM)),e.ignore(),e.more())return o.QueryLexer.lexText},o.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(o.QueryLexer.EDIT_DISTANCE),o.QueryLexer.lexText},o.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(o.QueryLexer.BOOST),o.QueryLexer.lexText},o.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(o.QueryLexer.TERM)},o.QueryLexer.termSeparator=o.tokenizer.separator,o.QueryLexer.lexText=function(e){for(;;){var t=e.next();if(t==o.QueryLexer.EOS)return o.QueryLexer.lexEOS;if(92!=t.charCodeAt(0)){if(":"==t)return o.QueryLexer.lexField;if("~"==t)return e.backup(),e.width()>0&&e.emit(o.QueryLexer.TERM),o.QueryLexer.lexEditDistance;if("^"==t)return e.backup(),e.width()>0&&e.emit(o.QueryLexer.TERM),o.QueryLexer.lexBoost;if(t.match(o.QueryLexer.termSeparator))return o.QueryLexer.lexTerm}else e.escapeCharacter()}},o.QueryParser=function(e,t){this.lexer=new o.QueryLexer(e),this.query=t,this.currentClause={},this.lexemeIdx=0},o.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=o.QueryParser.parseFieldOrTerm;e;)e=e(this);return this.query},o.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},o.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},o.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},o.QueryParser.parseFieldOrTerm=function(e){var t=e.peekLexeme();if(void 0!=t)switch(t.type){case o.QueryLexer.FIELD:return o.QueryParser.parseField;case o.QueryLexer.TERM:return o.QueryParser.parseTerm;default:var n="expected either a field or a term, found "+t.type;throw t.str.length>=1&&(n+=" with value '"+t.str+"'"),new o.QueryParseError(n,t.start,t.end)}},o.QueryParser.parseField=function(e){var t=e.consumeLexeme();if(void 0!=t){if(-1==e.query.allFields.indexOf(t.str)){var n=e.query.allFields.map(function(e){return"'"+e+"'"}).join(", "),r="unrecognised field '"+t.str+"', possible fields: "+n;throw new o.QueryParseError(r,t.start,t.end)}e.currentClause.fields=[t.str];var i=e.peekLexeme();if(void 0==i){var r="expecting term, found nothing";throw new o.QueryParseError(r,t.start,t.end)}switch(i.type){case o.QueryLexer.TERM:return o.QueryParser.parseTerm;default:var r="expecting term, found '"+i.type+"'";throw new o.QueryParseError(r,i.start,i.end)}}},o.QueryParser.parseTerm=function(e){var t=e.consumeLexeme();if(void 0!=t){e.currentClause.term=t.str.toLowerCase(),-1!=t.str.indexOf("*")&&(e.currentClause.usePipeline=!1);var n=e.peekLexeme();if(void 0==n)return void e.nextClause();switch(n.type){case o.QueryLexer.TERM:return e.nextClause(),o.QueryParser.parseTerm;case o.QueryLexer.FIELD:return e.nextClause(),o.QueryParser.parseField;case o.QueryLexer.EDIT_DISTANCE:return o.QueryParser.parseEditDistance;case o.QueryLexer.BOOST:return o.QueryParser.parseBoost;default:var r="Unexpected lexeme type '"+n.type+"'";throw new o.QueryParseError(r,n.start,n.end)}}},o.QueryParser.parseEditDistance=function(e){var t=e.consumeLexeme();if(void 0!=t){var n=parseInt(t.str,10);if(isNaN(n)){var r="edit distance must be numeric";throw new o.QueryParseError(r,t.start,t.end)}e.currentClause.editDistance=n;var i=e.peekLexeme();if(void 0==i)return void e.nextClause();switch(i.type){case o.QueryLexer.TERM:return e.nextClause(),o.QueryParser.parseTerm;case o.QueryLexer.FIELD:return e.nextClause(),o.QueryParser.parseField;case o.QueryLexer.EDIT_DISTANCE:return o.QueryParser.parseEditDistance;case o.QueryLexer.BOOST:return o.QueryParser.parseBoost;default:var r="Unexpected lexeme type '"+i.type+"'";throw new o.QueryParseError(r,i.start,i.end)}}},o.QueryParser.parseBoost=function(e){var t=e.consumeLexeme();if(void 0!=t){var n=parseInt(t.str,10);if(isNaN(n)){var r="boost must be numeric";throw new o.QueryParseError(r,t.start,t.end)}e.currentClause.boost=n;var i=e.peekLexeme();if(void 0==i)return void e.nextClause();switch(i.type){case o.QueryLexer.TERM:return e.nextClause(),o.QueryParser.parseTerm;case o.QueryLexer.FIELD:return e.nextClause(),o.QueryParser.parseField;case o.QueryLexer.EDIT_DISTANCE:return o.QueryParser.parseEditDistance;case o.QueryLexer.BOOST:return o.QueryParser.parseBoost;default:var r="Unexpected lexeme type '"+i.type+"'";throw new o.QueryParseError(r,i.start,i.end)}}},function(o,a){r=a,void 0!==(i="function"==typeof r?r.call(t,n,t,e):r)&&(e.exports=i)}(0,function(){return o})}()},function(e,t,n){"use strict";t.__esModule=!0;var r=n(38),i=function(e){return e&&e.__esModule?e:{default:e}}(r);t.default={Position:i.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=function(){function e(t,n){r(this,e);var i="string"==typeof t?document.querySelector(t):t;if(!(i instanceof HTMLElement&&i.parentNode instanceof HTMLElement))throw new ReferenceError;if(this.el_=i,this.parent_=i.parentNode,!((i="string"==typeof n?document.querySelector(n):n)instanceof HTMLElement))throw new ReferenceError;this.header_=i,this.height_=0,this.pad_="fixed"===window.getComputedStyle(this.header_).position}return e.prototype.setup=function(){var e=Array.prototype.reduce.call(this.parent_.children,function(e,t){return Math.max(e,t.offsetTop)},0);this.offset_=e-(this.pad_?this.header_.offsetHeight:0),this.update()},e.prototype.update=function(e){var t=window.pageYOffset,n=window.innerHeight;e&&"resize"===e.type&&this.setup();var r={top:this.pad_?this.header_.offsetHeight:0,bottom:this.parent_.offsetTop+this.parent_.offsetHeight},i=n-r.top-Math.max(0,this.offset_-t)-Math.max(0,t+n-r.bottom);i!==this.height_&&(this.el_.style.height=(this.height_=i)+"px"),t>=this.offset_?"lock"!==this.el_.dataset.mdState&&(this.el_.dataset.mdState="lock"):"lock"===this.el_.dataset.mdState&&(this.el_.dataset.mdState="")},e.prototype.reset=function(){this.el_.dataset.mdState="",this.el_.style.height="",this.height_=0},e}();t.default=i},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var i=n(40),o=r(i),a=n(44),s=r(a);t.default={Adapter:o.default,Repository:s.default}},function(e,t,n){"use strict";t.__esModule=!0;var r=n(41),i=function(e){return e&&e.__esModule?e:{default:e}}(r);t.default={GitHub:i.default}},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function o(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}t.__esModule=!0;var a=n(42),s=function(e){return e&&e.__esModule?e:{default:e}}(a),c=function(e){function t(n){r(this,t);var o=i(this,e.call(this,n)),a=/^.+github\.com\/([^\/]+)\/?([^\/]+)?.*$/.exec(o.base_);if(a&&3===a.length){var s=a[1],c=a[2];o.base_="https://api.github.com/users/"+s+"/repos",o.name_=c}return o}return o(t,e),t.prototype.fetch_=function(){var e=this;return function t(){var n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0;return fetch(e.base_+"?per_page=30&page="+n).then(function(e){return e.json()}).then(function(r){if(!(r instanceof Array))throw new TypeError;if(e.name_){var i=r.find(function(t){return t.name===e.name_});return i||30!==r.length?i?[e.format_(i.stargazers_count)+" Stars",e.format_(i.forks_count)+" Forks"]:[]:t(n+1)}return[r.length+" Repositories"]})}()},t}(s.default);t.default=c},function(e,t,n){"use strict";function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var i=n(43),o=function(e){return e&&e.__esModule?e:{default:e}}(i),a=function(){function e(t){r(this,e);var n="string"==typeof t?document.querySelector(t):t;if(!(n instanceof HTMLAnchorElement))throw new ReferenceError;this.el_=n,this.base_=this.el_.href,this.salt_=this.hash_(this.base_)}return e.prototype.fetch=function(){var e=this;return new Promise(function(t){var n=o.default.getJSON(e.salt_+".cache-source");void 0!==n?t(n):e.fetch_().then(function(n){o.default.set(e.salt_+".cache-source",n,{expires:1/96}),t(n)})})},e.prototype.fetch_=function(){throw new Error("fetch_(): Not implemented")},e.prototype.format_=function(e){return e>1e4?(e/1e3).toFixed(0)+"k":e>1e3?(e/1e3).toFixed(1)+"k":""+e},e.prototype.hash_=function(e){var t=0;if(0===e.length)return t;for(var n=0,r=e.length;n1){if(o=e({path:"/"},r.defaults,o),"number"==typeof o.expires){var s=new Date;s.setMilliseconds(s.getMilliseconds()+864e5*o.expires),o.expires=s}o.expires=o.expires?o.expires.toUTCString():"";try{a=JSON.stringify(i),/^[\{\[]/.test(a)&&(i=a)}catch(e){}i=n.write?n.write(i,t):encodeURIComponent(String(i)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),t=encodeURIComponent(String(t)),t=t.replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent),t=t.replace(/[\(\)]/g,escape);var c="";for(var u in o)o[u]&&(c+="; "+u,!0!==o[u]&&(c+="="+o[u]));return document.cookie=t+"="+i+c}t||(a={});for(var l=document.cookie?document.cookie.split("; "):[],d=/(%[0-9A-Z]{2})+/g,f=0;f=this.el_.children[0].offsetTop+-43;e!==this.active_&&(this.el_.dataset.mdState=(this.active_=e)?"hidden":"")},e.prototype.reset=function(){this.el_.dataset.mdState="",this.active_=!1},e}();t.default=i}])); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.da.js b/docs/v3/v1/assets/javascripts/lunr/lunr.da.js deleted file mode 100644 index 3b07b2c19..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.da.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,i,n;e.da=function(){this.pipeline.reset(),this.pipeline.add(e.da.trimmer,e.da.stopWordFilter,e.da.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.da.stemmer))},e.da.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.da.trimmer=e.trimmerSupport.generateTrimmer(e.da.wordCharacters),e.Pipeline.registerFunction(e.da.trimmer,"trimmer-da"),e.da.stemmer=(r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,t,s=[new r("hed",-1,1),new r("ethed",0,1),new r("ered",-1,1),new r("e",-1,1),new r("erede",3,1),new r("ende",3,1),new r("erende",5,1),new r("ene",3,1),new r("erne",3,1),new r("ere",3,1),new r("en",-1,1),new r("heden",10,1),new r("eren",10,1),new r("er",-1,1),new r("heder",13,1),new r("erer",13,1),new r("s",-1,2),new r("heds",16,1),new r("es",16,1),new r("endes",18,1),new r("erendes",19,1),new r("enes",18,1),new r("ernes",18,1),new r("eres",18,1),new r("ens",16,1),new r("hedens",24,1),new r("erens",24,1),new r("ers",16,1),new r("ets",16,1),new r("erets",28,1),new r("et",-1,1),new r("eret",30,1)],o=[new r("gd",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1)],a=[new r("ig",-1,1),new r("lig",0,1),new r("elig",1,1),new r("els",-1,1),new r("løst",-1,2)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],u=[239,254,42,3,0,0,0,0,0,0,0,0,0,0,0,0,16],c=new i;function l(){var e,r=c.limit-c.cursor;c.cursor>=n&&(e=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,c.find_among_b(o,4)?(c.bra=c.cursor,c.limit_backward=e,c.cursor=c.limit-r,c.cursor>c.limit_backward&&(c.cursor--,c.bra=c.cursor,c.slice_del())):c.limit_backward=e)}this.setCurrent=function(e){c.setCurrent(e)},this.getCurrent=function(){return c.getCurrent()},this.stem=function(){var r,i=c.cursor;return function(){var r,i=c.cursor+3;if(n=c.limit,0<=i&&i<=c.limit){for(e=i;;){if(r=c.cursor,c.in_grouping(d,97,248)){c.cursor=r;break}if(c.cursor=r,r>=c.limit)return;c.cursor++}for(;!c.out_grouping(d,97,248);){if(c.cursor>=c.limit)return;c.cursor++}(n=c.cursor)=n&&(r=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,e=c.find_among_b(s,32),c.limit_backward=r,e))switch(c.bra=c.cursor,e){case 1:c.slice_del();break;case 2:c.in_grouping_b(u,97,229)&&c.slice_del()}}(),c.cursor=c.limit,l(),c.cursor=c.limit,function(){var e,r,i,t=c.limit-c.cursor;if(c.ket=c.cursor,c.eq_s_b(2,"st")&&(c.bra=c.cursor,c.eq_s_b(2,"ig")&&c.slice_del()),c.cursor=c.limit-t,c.cursor>=n&&(r=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,e=c.find_among_b(a,5),c.limit_backward=r,e))switch(c.bra=c.cursor,e){case 1:c.slice_del(),i=c.limit-c.cursor,l(),c.cursor=c.limit-i;break;case 2:c.slice_from("løs")}}(),c.cursor=c.limit,c.cursor>=n&&(r=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,c.out_grouping_b(d,97,248)?(c.bra=c.cursor,t=c.slice_to(t),c.limit_backward=r,c.eq_v_b(t)&&c.slice_del()):c.limit_backward=r),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.da.stemmer,"stemmer-da"),e.da.stopWordFilter=e.generateStopWordFilter("ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været".split(" ")),e.Pipeline.registerFunction(e.da.stopWordFilter,"stopWordFilter-da")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.de.js b/docs/v3/v1/assets/javascripts/lunr/lunr.de.js deleted file mode 100644 index ebd78f281..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.de.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,n,i;e.de=function(){this.pipeline.reset(),this.pipeline.add(e.de.trimmer,e.de.stopWordFilter,e.de.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.de.stemmer))},e.de.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.de.trimmer=e.trimmerSupport.generateTrimmer(e.de.wordCharacters),e.Pipeline.registerFunction(e.de.trimmer,"trimmer-de"),e.de.stemmer=(r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){var e,i,s,t=[new r("",-1,6),new r("U",0,2),new r("Y",0,1),new r("ä",0,3),new r("ö",0,4),new r("ü",0,5)],o=[new r("e",-1,2),new r("em",-1,1),new r("en",-1,2),new r("ern",-1,1),new r("er",-1,1),new r("s",-1,3),new r("es",5,2)],c=[new r("en",-1,1),new r("er",-1,1),new r("st",-1,2),new r("est",2,1)],u=[new r("ig",-1,1),new r("lich",-1,1)],a=[new r("end",-1,1),new r("ig",-1,2),new r("ung",-1,1),new r("lich",-1,3),new r("isch",-1,2),new r("ik",-1,2),new r("heit",-1,3),new r("keit",-1,4)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32,8],l=[117,30,5],m=[117,30,4],h=new n;function w(e,r,n){return!(!h.eq_s(1,e)||(h.ket=h.cursor,!h.in_grouping(d,97,252)))&&(h.slice_from(r),h.cursor=n,!0)}function f(){for(;!h.in_grouping(d,97,252);){if(h.cursor>=h.limit)return!0;h.cursor++}for(;!h.out_grouping(d,97,252);){if(h.cursor>=h.limit)return!0;h.cursor++}return!1}function b(){return s<=h.cursor}function _(){return i<=h.cursor}this.setCurrent=function(e){h.setCurrent(e)},this.getCurrent=function(){return h.getCurrent()},this.stem=function(){var r=h.cursor;return function(){for(var e,r,n,i,s=h.cursor;;)if(e=h.cursor,h.bra=e,h.eq_s(1,"ß"))h.ket=h.cursor,h.slice_from("ss");else{if(e>=h.limit)break;h.cursor=e+1}for(h.cursor=s;;)for(r=h.cursor;;){if(n=h.cursor,h.in_grouping(d,97,252)){if(i=h.cursor,h.bra=i,w("u","U",n))break;if(h.cursor=i,w("y","Y",n))break}if(n>=h.limit)return void(h.cursor=r);h.cursor=n+1}}(),h.cursor=r,function(){s=h.limit,i=s;var r=h.cursor+3;0<=r&&r<=h.limit&&(e=r,f()||((s=h.cursor)=h.limit)return;h.cursor++}}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.de.stemmer,"stemmer-de"),e.de.stopWordFilter=e.generateStopWordFilter("aber alle allem allen aller alles als also am an ander andere anderem anderen anderer anderes anderm andern anderr anders auch auf aus bei bin bis bist da damit dann das dasselbe dazu daß dein deine deinem deinen deiner deines dem demselben den denn denselben der derer derselbe derselben des desselben dessen dich die dies diese dieselbe dieselben diesem diesen dieser dieses dir doch dort du durch ein eine einem einen einer eines einig einige einigem einigen einiger einiges einmal er es etwas euch euer eure eurem euren eurer eures für gegen gewesen hab habe haben hat hatte hatten hier hin hinter ich ihm ihn ihnen ihr ihre ihrem ihren ihrer ihres im in indem ins ist jede jedem jeden jeder jedes jene jenem jenen jener jenes jetzt kann kein keine keinem keinen keiner keines können könnte machen man manche manchem manchen mancher manches mein meine meinem meinen meiner meines mich mir mit muss musste nach nicht nichts noch nun nur ob oder ohne sehr sein seine seinem seinen seiner seines selbst sich sie sind so solche solchem solchen solcher solches soll sollte sondern sonst um und uns unse unsem unsen unser unses unter viel vom von vor war waren warst was weg weil weiter welche welchem welchen welcher welches wenn werde werden wie wieder will wir wird wirst wo wollen wollte während würde würden zu zum zur zwar zwischen über".split(" ")),e.Pipeline.registerFunction(e.de.stopWordFilter,"stopWordFilter-de")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.du.js b/docs/v3/v1/assets/javascripts/lunr/lunr.du.js deleted file mode 100644 index 375c0e763..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.du.js +++ /dev/null @@ -1 +0,0 @@ -!function(r,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():e()(r.lunr)}(this,function(){return function(r){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var e,i,n;r.du=function(){this.pipeline.reset(),this.pipeline.add(r.du.trimmer,r.du.stopWordFilter,r.du.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(r.du.stemmer))},r.du.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",r.du.trimmer=r.trimmerSupport.generateTrimmer(r.du.wordCharacters),r.Pipeline.registerFunction(r.du.trimmer,"trimmer-du"),r.du.stemmer=(e=r.stemmerSupport.Among,i=r.stemmerSupport.SnowballProgram,n=new function(){var r,n,o,t=[new e("",-1,6),new e("á",0,1),new e("ä",0,1),new e("é",0,2),new e("ë",0,2),new e("í",0,3),new e("ï",0,3),new e("ó",0,4),new e("ö",0,4),new e("ú",0,5),new e("ü",0,5)],s=[new e("",-1,3),new e("I",0,2),new e("Y",0,1)],u=[new e("dd",-1,-1),new e("kk",-1,-1),new e("tt",-1,-1)],c=[new e("ene",-1,2),new e("se",-1,3),new e("en",-1,2),new e("heden",2,1),new e("s",-1,3)],a=[new e("end",-1,1),new e("ig",-1,2),new e("ing",-1,1),new e("lijk",-1,3),new e("baar",-1,4),new e("bar",-1,5)],l=[new e("aa",-1,-1),new e("ee",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1)],m=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],d=[1,0,0,17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],f=[17,67,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],_=new i;function w(r){return _.cursor=r,r>=_.limit||(_.cursor++,!1)}function b(){for(;!_.in_grouping(m,97,232);){if(_.cursor>=_.limit)return!0;_.cursor++}for(;!_.out_grouping(m,97,232);){if(_.cursor>=_.limit)return!0;_.cursor++}return!1}function p(){return n<=_.cursor}function g(){return r<=_.cursor}function h(){var r=_.limit-_.cursor;_.find_among_b(u,3)&&(_.cursor=_.limit-r,_.ket=_.cursor,_.cursor>_.limit_backward&&(_.cursor--,_.bra=_.cursor,_.slice_del()))}function k(){var r;o=!1,_.ket=_.cursor,_.eq_s_b(1,"e")&&(_.bra=_.cursor,p()&&(r=_.limit-_.cursor,_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-r,_.slice_del(),o=!0,h())))}function v(){var r;p()&&(r=_.limit-_.cursor,_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-r,_.eq_s_b(3,"gem")||(_.cursor=_.limit-r,_.slice_del(),h())))}this.setCurrent=function(r){_.setCurrent(r)},this.getCurrent=function(){return _.getCurrent()},this.stem=function(){var e=_.cursor;return function(){for(var r,e,i,n=_.cursor;;){if(_.bra=_.cursor,r=_.find_among(t,11))switch(_.ket=_.cursor,r){case 1:_.slice_from("a");continue;case 2:_.slice_from("e");continue;case 3:_.slice_from("i");continue;case 4:_.slice_from("o");continue;case 5:_.slice_from("u");continue;case 6:if(_.cursor>=_.limit)break;_.cursor++;continue}break}for(_.cursor=n,_.bra=n,_.eq_s(1,"y")?(_.ket=_.cursor,_.slice_from("Y")):_.cursor=n;;)if(e=_.cursor,_.in_grouping(m,97,232)){if(i=_.cursor,_.bra=i,_.eq_s(1,"i"))_.ket=_.cursor,_.in_grouping(m,97,232)&&(_.slice_from("I"),_.cursor=e);else if(_.cursor=i,_.eq_s(1,"y"))_.ket=_.cursor,_.slice_from("Y"),_.cursor=e;else if(w(e))break}else if(w(e))break}(),_.cursor=e,n=_.limit,r=n,b()||((n=_.cursor)<3&&(n=3),b()||(r=_.cursor)),_.limit_backward=e,_.cursor=_.limit,function(){var r,e,i,n,t,s,u=_.limit-_.cursor;if(_.ket=_.cursor,r=_.find_among_b(c,5))switch(_.bra=_.cursor,r){case 1:p()&&_.slice_from("heid");break;case 2:v();break;case 3:p()&&_.out_grouping_b(f,97,232)&&_.slice_del()}if(_.cursor=_.limit-u,k(),_.cursor=_.limit-u,_.ket=_.cursor,_.eq_s_b(4,"heid")&&(_.bra=_.cursor,g()&&(e=_.limit-_.cursor,_.eq_s_b(1,"c")||(_.cursor=_.limit-e,_.slice_del(),_.ket=_.cursor,_.eq_s_b(2,"en")&&(_.bra=_.cursor,v())))),_.cursor=_.limit-u,_.ket=_.cursor,r=_.find_among_b(a,6))switch(_.bra=_.cursor,r){case 1:if(g()){if(_.slice_del(),i=_.limit-_.cursor,_.ket=_.cursor,_.eq_s_b(2,"ig")&&(_.bra=_.cursor,g()&&(n=_.limit-_.cursor,!_.eq_s_b(1,"e")))){_.cursor=_.limit-n,_.slice_del();break}_.cursor=_.limit-i,h()}break;case 2:g()&&(t=_.limit-_.cursor,_.eq_s_b(1,"e")||(_.cursor=_.limit-t,_.slice_del()));break;case 3:g()&&(_.slice_del(),k());break;case 4:g()&&_.slice_del();break;case 5:g()&&o&&_.slice_del()}_.cursor=_.limit-u,_.out_grouping_b(d,73,232)&&(s=_.limit-_.cursor,_.find_among_b(l,4)&&_.out_grouping_b(m,97,232)&&(_.cursor=_.limit-s,_.ket=_.cursor,_.cursor>_.limit_backward&&(_.cursor--,_.bra=_.cursor,_.slice_del())))}(),_.cursor=_.limit_backward,function(){for(var r;;)if(_.bra=_.cursor,r=_.find_among(s,3))switch(_.ket=_.cursor,r){case 1:_.slice_from("y");break;case 2:_.slice_from("i");break;case 3:if(_.cursor>=_.limit)return;_.cursor++}}(),!0}},function(r){return"function"==typeof r.update?r.update(function(r){return n.setCurrent(r),n.stem(),n.getCurrent()}):(n.setCurrent(r),n.stem(),n.getCurrent())}),r.Pipeline.registerFunction(r.du.stemmer,"stemmer-du"),r.du.stopWordFilter=r.generateStopWordFilter(" aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou".split(" ")),r.Pipeline.registerFunction(r.du.stopWordFilter,"stopWordFilter-du")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.es.js b/docs/v3/v1/assets/javascripts/lunr/lunr.es.js deleted file mode 100644 index 4cb634f0a..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.es.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,s){"function"==typeof define&&define.amd?define(s):"object"==typeof exports?module.exports=s():s()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var s,r,n;e.es=function(){this.pipeline.reset(),this.pipeline.add(e.es.trimmer,e.es.stopWordFilter,e.es.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.es.stemmer))},e.es.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.es.trimmer=e.trimmerSupport.generateTrimmer(e.es.wordCharacters),e.Pipeline.registerFunction(e.es.trimmer,"trimmer-es"),e.es.stemmer=(s=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,i,a=[new s("",-1,6),new s("á",0,1),new s("é",0,2),new s("í",0,3),new s("ó",0,4),new s("ú",0,5)],t=[new s("la",-1,-1),new s("sela",0,-1),new s("le",-1,-1),new s("me",-1,-1),new s("se",-1,-1),new s("lo",-1,-1),new s("selo",5,-1),new s("las",-1,-1),new s("selas",7,-1),new s("les",-1,-1),new s("los",-1,-1),new s("selos",10,-1),new s("nos",-1,-1)],o=[new s("ando",-1,6),new s("iendo",-1,6),new s("yendo",-1,7),new s("ándo",-1,2),new s("iéndo",-1,1),new s("ar",-1,6),new s("er",-1,6),new s("ir",-1,6),new s("ár",-1,3),new s("ér",-1,4),new s("ír",-1,5)],u=[new s("ic",-1,-1),new s("ad",-1,-1),new s("os",-1,-1),new s("iv",-1,1)],w=[new s("able",-1,1),new s("ible",-1,1),new s("ante",-1,1)],c=[new s("ic",-1,1),new s("abil",-1,1),new s("iv",-1,1)],m=[new s("ica",-1,1),new s("ancia",-1,2),new s("encia",-1,5),new s("adora",-1,2),new s("osa",-1,1),new s("ista",-1,1),new s("iva",-1,9),new s("anza",-1,1),new s("logía",-1,3),new s("idad",-1,8),new s("able",-1,1),new s("ible",-1,1),new s("ante",-1,2),new s("mente",-1,7),new s("amente",13,6),new s("ación",-1,2),new s("ución",-1,4),new s("ico",-1,1),new s("ismo",-1,1),new s("oso",-1,1),new s("amiento",-1,1),new s("imiento",-1,1),new s("ivo",-1,9),new s("ador",-1,2),new s("icas",-1,1),new s("ancias",-1,2),new s("encias",-1,5),new s("adoras",-1,2),new s("osas",-1,1),new s("istas",-1,1),new s("ivas",-1,9),new s("anzas",-1,1),new s("logías",-1,3),new s("idades",-1,8),new s("ables",-1,1),new s("ibles",-1,1),new s("aciones",-1,2),new s("uciones",-1,4),new s("adores",-1,2),new s("antes",-1,2),new s("icos",-1,1),new s("ismos",-1,1),new s("osos",-1,1),new s("amientos",-1,1),new s("imientos",-1,1),new s("ivos",-1,9)],l=[new s("ya",-1,1),new s("ye",-1,1),new s("yan",-1,1),new s("yen",-1,1),new s("yeron",-1,1),new s("yendo",-1,1),new s("yo",-1,1),new s("yas",-1,1),new s("yes",-1,1),new s("yais",-1,1),new s("yamos",-1,1),new s("yó",-1,1)],d=[new s("aba",-1,2),new s("ada",-1,2),new s("ida",-1,2),new s("ara",-1,2),new s("iera",-1,2),new s("ía",-1,2),new s("aría",5,2),new s("ería",5,2),new s("iría",5,2),new s("ad",-1,2),new s("ed",-1,2),new s("id",-1,2),new s("ase",-1,2),new s("iese",-1,2),new s("aste",-1,2),new s("iste",-1,2),new s("an",-1,2),new s("aban",16,2),new s("aran",16,2),new s("ieran",16,2),new s("ían",16,2),new s("arían",20,2),new s("erían",20,2),new s("irían",20,2),new s("en",-1,1),new s("asen",24,2),new s("iesen",24,2),new s("aron",-1,2),new s("ieron",-1,2),new s("arán",-1,2),new s("erán",-1,2),new s("irán",-1,2),new s("ado",-1,2),new s("ido",-1,2),new s("ando",-1,2),new s("iendo",-1,2),new s("ar",-1,2),new s("er",-1,2),new s("ir",-1,2),new s("as",-1,2),new s("abas",39,2),new s("adas",39,2),new s("idas",39,2),new s("aras",39,2),new s("ieras",39,2),new s("ías",39,2),new s("arías",45,2),new s("erías",45,2),new s("irías",45,2),new s("es",-1,1),new s("ases",49,2),new s("ieses",49,2),new s("abais",-1,2),new s("arais",-1,2),new s("ierais",-1,2),new s("íais",-1,2),new s("aríais",55,2),new s("eríais",55,2),new s("iríais",55,2),new s("aseis",-1,2),new s("ieseis",-1,2),new s("asteis",-1,2),new s("isteis",-1,2),new s("áis",-1,2),new s("éis",-1,1),new s("aréis",64,2),new s("eréis",64,2),new s("iréis",64,2),new s("ados",-1,2),new s("idos",-1,2),new s("amos",-1,2),new s("ábamos",70,2),new s("áramos",70,2),new s("iéramos",70,2),new s("íamos",70,2),new s("aríamos",74,2),new s("eríamos",74,2),new s("iríamos",74,2),new s("emos",-1,1),new s("aremos",78,2),new s("eremos",78,2),new s("iremos",78,2),new s("ásemos",78,2),new s("iésemos",78,2),new s("imos",-1,2),new s("arás",-1,2),new s("erás",-1,2),new s("irás",-1,2),new s("ís",-1,2),new s("ará",-1,2),new s("erá",-1,2),new s("irá",-1,2),new s("aré",-1,2),new s("eré",-1,2),new s("iré",-1,2),new s("ió",-1,2)],b=[new s("a",-1,1),new s("e",-1,2),new s("o",-1,1),new s("os",-1,1),new s("á",-1,1),new s("é",-1,2),new s("í",-1,1),new s("ó",-1,1)],f=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,4,10],_=new r;function h(){if(_.out_grouping(f,97,252)){for(;!_.in_grouping(f,97,252);){if(_.cursor>=_.limit)return!0;_.cursor++}return!1}return!0}function v(){var e,s=_.cursor;if(function(){if(_.in_grouping(f,97,252)){var e=_.cursor;if(h()){if(_.cursor=e,!_.in_grouping(f,97,252))return!0;for(;!_.out_grouping(f,97,252);){if(_.cursor>=_.limit)return!0;_.cursor++}}return!1}return!0}()){if(_.cursor=s,!_.out_grouping(f,97,252))return;if(e=_.cursor,h()){if(_.cursor=e,!_.in_grouping(f,97,252)||_.cursor>=_.limit)return;_.cursor++}}i=_.cursor}function p(){for(;!_.in_grouping(f,97,252);){if(_.cursor>=_.limit)return!1;_.cursor++}for(;!_.out_grouping(f,97,252);){if(_.cursor>=_.limit)return!1;_.cursor++}return!0}function g(){return i<=_.cursor}function k(){return e<=_.cursor}function y(e,s){if(!k())return!0;_.slice_del(),_.ket=_.cursor;var r=_.find_among_b(e,s);return r&&(_.bra=_.cursor,1==r&&k()&&_.slice_del()),!1}function q(e){return!k()||(_.slice_del(),_.ket=_.cursor,_.eq_s_b(2,e)&&(_.bra=_.cursor,k()&&_.slice_del()),!1)}function C(){var e;if(_.ket=_.cursor,e=_.find_among_b(m,46)){switch(_.bra=_.cursor,e){case 1:if(!k())return!1;_.slice_del();break;case 2:if(q("ic"))return!1;break;case 3:if(!k())return!1;_.slice_from("log");break;case 4:if(!k())return!1;_.slice_from("u");break;case 5:if(!k())return!1;_.slice_from("ente");break;case 6:if(!(n<=_.cursor))return!1;_.slice_del(),_.ket=_.cursor,(e=_.find_among_b(u,4))&&(_.bra=_.cursor,k()&&(_.slice_del(),1==e&&(_.ket=_.cursor,_.eq_s_b(2,"at")&&(_.bra=_.cursor,k()&&_.slice_del()))));break;case 7:if(y(w,3))return!1;break;case 8:if(y(c,3))return!1;break;case 9:if(q("at"))return!1}return!0}return!1}this.setCurrent=function(e){_.setCurrent(e)},this.getCurrent=function(){return _.getCurrent()},this.stem=function(){var s,r=_.cursor;return s=_.cursor,i=_.limit,n=i,e=i,v(),_.cursor=s,p()&&(n=_.cursor,p()&&(e=_.cursor)),_.limit_backward=r,_.cursor=_.limit,function(){var e;if(_.ket=_.cursor,_.find_among_b(t,13)&&(_.bra=_.cursor,(e=_.find_among_b(o,11))&&g()))switch(e){case 1:_.bra=_.cursor,_.slice_from("iendo");break;case 2:_.bra=_.cursor,_.slice_from("ando");break;case 3:_.bra=_.cursor,_.slice_from("ar");break;case 4:_.bra=_.cursor,_.slice_from("er");break;case 5:_.bra=_.cursor,_.slice_from("ir");break;case 6:_.slice_del();break;case 7:_.eq_s_b(1,"u")&&_.slice_del()}}(),_.cursor=_.limit,C()||(_.cursor=_.limit,function(){var e,s;if(_.cursor>=i&&(s=_.limit_backward,_.limit_backward=i,_.ket=_.cursor,e=_.find_among_b(l,12),_.limit_backward=s,e)){if(_.bra=_.cursor,1==e){if(!_.eq_s_b(1,"u"))return!1;_.slice_del()}return!0}return!1}()||(_.cursor=_.limit,function(){var e,s,r,n;if(_.cursor>=i&&(s=_.limit_backward,_.limit_backward=i,_.ket=_.cursor,e=_.find_among_b(d,96),_.limit_backward=s,e))switch(_.bra=_.cursor,e){case 1:r=_.limit-_.cursor,_.eq_s_b(1,"u")?(n=_.limit-_.cursor,_.eq_s_b(1,"g")?_.cursor=_.limit-n:_.cursor=_.limit-r):_.cursor=_.limit-r,_.bra=_.cursor;case 2:_.slice_del()}}())),_.cursor=_.limit,function(){var e,s;if(_.ket=_.cursor,e=_.find_among_b(b,8))switch(_.bra=_.cursor,e){case 1:g()&&_.slice_del();break;case 2:g()&&(_.slice_del(),_.ket=_.cursor,_.eq_s_b(1,"u")&&(_.bra=_.cursor,s=_.limit-_.cursor,_.eq_s_b(1,"g")&&(_.cursor=_.limit-s,g()&&_.slice_del())))}}(),_.cursor=_.limit_backward,function(){for(var e;;){if(_.bra=_.cursor,e=_.find_among(a,6))switch(_.ket=_.cursor,e){case 1:_.slice_from("a");continue;case 2:_.slice_from("e");continue;case 3:_.slice_from("i");continue;case 4:_.slice_from("o");continue;case 5:_.slice_from("u");continue;case 6:if(_.cursor>=_.limit)break;_.cursor++;continue}break}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.es.stemmer,"stemmer-es"),e.es.stopWordFilter=e.generateStopWordFilter("a al algo algunas algunos ante antes como con contra cual cuando de del desde donde durante e el ella ellas ellos en entre era erais eran eras eres es esa esas ese eso esos esta estaba estabais estaban estabas estad estada estadas estado estados estamos estando estar estaremos estará estarán estarás estaré estaréis estaría estaríais estaríamos estarían estarías estas este estemos esto estos estoy estuve estuviera estuvierais estuvieran estuvieras estuvieron estuviese estuvieseis estuviesen estuvieses estuvimos estuviste estuvisteis estuviéramos estuviésemos estuvo está estábamos estáis están estás esté estéis estén estés fue fuera fuerais fueran fueras fueron fuese fueseis fuesen fueses fui fuimos fuiste fuisteis fuéramos fuésemos ha habida habidas habido habidos habiendo habremos habrá habrán habrás habré habréis habría habríais habríamos habrían habrías habéis había habíais habíamos habían habías han has hasta hay haya hayamos hayan hayas hayáis he hemos hube hubiera hubierais hubieran hubieras hubieron hubiese hubieseis hubiesen hubieses hubimos hubiste hubisteis hubiéramos hubiésemos hubo la las le les lo los me mi mis mucho muchos muy más mí mía mías mío míos nada ni no nos nosotras nosotros nuestra nuestras nuestro nuestros o os otra otras otro otros para pero poco por porque que quien quienes qué se sea seamos sean seas seremos será serán serás seré seréis sería seríais seríamos serían serías seáis sido siendo sin sobre sois somos son soy su sus suya suyas suyo suyos sí también tanto te tendremos tendrá tendrán tendrás tendré tendréis tendría tendríais tendríamos tendrían tendrías tened tenemos tenga tengamos tengan tengas tengo tengáis tenida tenidas tenido tenidos teniendo tenéis tenía teníais teníamos tenían tenías ti tiene tienen tienes todo todos tu tus tuve tuviera tuvierais tuvieran tuvieras tuvieron tuviese tuvieseis tuviesen tuvieses tuvimos tuviste tuvisteis tuviéramos tuviésemos tuvo tuya tuyas tuyo tuyos tú un una uno unos vosotras vosotros vuestra vuestras vuestro vuestros y ya yo él éramos".split(" ")),e.Pipeline.registerFunction(e.es.stopWordFilter,"stopWordFilter-es")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.fi.js b/docs/v3/v1/assets/javascripts/lunr/lunr.fi.js deleted file mode 100644 index 0200b1fcb..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.fi.js +++ /dev/null @@ -1 +0,0 @@ -!function(i,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():e()(i.lunr)}(this,function(){return function(i){if(void 0===i)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===i.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var e,r,n;i.fi=function(){this.pipeline.reset(),this.pipeline.add(i.fi.trimmer,i.fi.stopWordFilter,i.fi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(i.fi.stemmer))},i.fi.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",i.fi.trimmer=i.trimmerSupport.generateTrimmer(i.fi.wordCharacters),i.Pipeline.registerFunction(i.fi.trimmer,"trimmer-fi"),i.fi.stemmer=(e=i.stemmerSupport.Among,r=i.stemmerSupport.SnowballProgram,n=new function(){var i,n,t,s,l=[new e("pa",-1,1),new e("sti",-1,2),new e("kaan",-1,1),new e("han",-1,1),new e("kin",-1,1),new e("hän",-1,1),new e("kään",-1,1),new e("ko",-1,1),new e("pä",-1,1),new e("kö",-1,1)],o=[new e("lla",-1,-1),new e("na",-1,-1),new e("ssa",-1,-1),new e("ta",-1,-1),new e("lta",3,-1),new e("sta",3,-1)],a=[new e("llä",-1,-1),new e("nä",-1,-1),new e("ssä",-1,-1),new e("tä",-1,-1),new e("ltä",3,-1),new e("stä",3,-1)],u=[new e("lle",-1,-1),new e("ine",-1,-1)],c=[new e("nsa",-1,3),new e("mme",-1,3),new e("nne",-1,3),new e("ni",-1,2),new e("si",-1,1),new e("an",-1,4),new e("en",-1,6),new e("än",-1,5),new e("nsä",-1,3)],m=[new e("aa",-1,-1),new e("ee",-1,-1),new e("ii",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1),new e("ää",-1,-1),new e("öö",-1,-1)],w=[new e("a",-1,8),new e("lla",0,-1),new e("na",0,-1),new e("ssa",0,-1),new e("ta",0,-1),new e("lta",4,-1),new e("sta",4,-1),new e("tta",4,9),new e("lle",-1,-1),new e("ine",-1,-1),new e("ksi",-1,-1),new e("n",-1,7),new e("han",11,1),new e("den",11,-1,C),new e("seen",11,-1,v),new e("hen",11,2),new e("tten",11,-1,C),new e("hin",11,3),new e("siin",11,-1,C),new e("hon",11,4),new e("hän",11,5),new e("hön",11,6),new e("ä",-1,8),new e("llä",22,-1),new e("nä",22,-1),new e("ssä",22,-1),new e("tä",22,-1),new e("ltä",26,-1),new e("stä",26,-1),new e("ttä",26,9)],_=[new e("eja",-1,-1),new e("mma",-1,1),new e("imma",1,-1),new e("mpa",-1,1),new e("impa",3,-1),new e("mmi",-1,1),new e("immi",5,-1),new e("mpi",-1,1),new e("impi",7,-1),new e("ejä",-1,-1),new e("mmä",-1,1),new e("immä",10,-1),new e("mpä",-1,1),new e("impä",12,-1)],k=[new e("i",-1,-1),new e("j",-1,-1)],b=[new e("mma",-1,1),new e("imma",0,-1)],d=[17,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8],f=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],h=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],p=[17,97,24,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],g=new r;function j(){for(var i;i=g.cursor,!g.in_grouping(f,97,246);){if(g.cursor=i,i>=g.limit)return!0;g.cursor++}for(g.cursor=i;!g.out_grouping(f,97,246);){if(g.cursor>=g.limit)return!0;g.cursor++}return!1}function q(){var i,e;if(g.cursor>=s)if(e=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,i=g.find_among_b(l,10)){switch(g.bra=g.cursor,g.limit_backward=e,i){case 1:if(!g.in_grouping_b(p,97,246))return;break;case 2:if(!(t<=g.cursor))return}g.slice_del()}else g.limit_backward=e}function v(){return g.find_among_b(m,7)}function C(){return g.eq_s_b(1,"i")&&g.in_grouping_b(h,97,246)}this.setCurrent=function(i){g.setCurrent(i)},this.getCurrent=function(){return g.getCurrent()},this.stem=function(){var e,r=g.cursor;return s=g.limit,t=s,j()||(s=g.cursor,j()||(t=g.cursor)),i=!1,g.limit_backward=r,g.cursor=g.limit,q(),g.cursor=g.limit,function(){var i,e,r;if(g.cursor>=s)if(e=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,i=g.find_among_b(c,9))switch(g.bra=g.cursor,g.limit_backward=e,i){case 1:r=g.limit-g.cursor,g.eq_s_b(1,"k")||(g.cursor=g.limit-r,g.slice_del());break;case 2:g.slice_del(),g.ket=g.cursor,g.eq_s_b(3,"kse")&&(g.bra=g.cursor,g.slice_from("ksi"));break;case 3:g.slice_del();break;case 4:g.find_among_b(o,6)&&g.slice_del();break;case 5:g.find_among_b(a,6)&&g.slice_del();break;case 6:g.find_among_b(u,2)&&g.slice_del()}else g.limit_backward=e}(),g.cursor=g.limit,function(){var e,r,n;if(g.cursor>=s)if(r=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,e=g.find_among_b(w,30)){switch(g.bra=g.cursor,g.limit_backward=r,e){case 1:if(!g.eq_s_b(1,"a"))return;break;case 2:case 9:if(!g.eq_s_b(1,"e"))return;break;case 3:if(!g.eq_s_b(1,"i"))return;break;case 4:if(!g.eq_s_b(1,"o"))return;break;case 5:if(!g.eq_s_b(1,"ä"))return;break;case 6:if(!g.eq_s_b(1,"ö"))return;break;case 7:if(n=g.limit-g.cursor,!v()&&(g.cursor=g.limit-n,!g.eq_s_b(2,"ie"))){g.cursor=g.limit-n;break}if(g.cursor=g.limit-n,g.cursor<=g.limit_backward){g.cursor=g.limit-n;break}g.cursor--,g.bra=g.cursor;break;case 8:if(!g.in_grouping_b(f,97,246)||!g.out_grouping_b(f,97,246))return}g.slice_del(),i=!0}else g.limit_backward=r}(),g.cursor=g.limit,function(){var i,e,r;if(g.cursor>=t)if(e=g.limit_backward,g.limit_backward=t,g.ket=g.cursor,i=g.find_among_b(_,14)){if(g.bra=g.cursor,g.limit_backward=e,1==i){if(r=g.limit-g.cursor,g.eq_s_b(2,"po"))return;g.cursor=g.limit-r}g.slice_del()}else g.limit_backward=e}(),g.cursor=g.limit,i?(g.cursor>=s&&(e=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,g.find_among_b(k,2)?(g.bra=g.cursor,g.limit_backward=e,g.slice_del()):g.limit_backward=e),g.cursor=g.limit):(g.cursor=g.limit,function(){var i,e,r,n,l,o;if(g.cursor>=s){if(e=g.limit_backward,g.limit_backward=s,g.ket=g.cursor,g.eq_s_b(1,"t")&&(g.bra=g.cursor,r=g.limit-g.cursor,g.in_grouping_b(f,97,246)&&(g.cursor=g.limit-r,g.slice_del(),g.limit_backward=e,n=g.limit-g.cursor,g.cursor>=t&&(g.cursor=t,l=g.limit_backward,g.limit_backward=g.cursor,g.cursor=g.limit-n,g.ket=g.cursor,i=g.find_among_b(b,2))))){if(g.bra=g.cursor,g.limit_backward=l,1==i){if(o=g.limit-g.cursor,g.eq_s_b(2,"po"))return;g.cursor=g.limit-o}return void g.slice_del()}g.limit_backward=e}}(),g.cursor=g.limit),function(){var i,e,r,t;if(g.cursor>=s){for(i=g.limit_backward,g.limit_backward=s,e=g.limit-g.cursor,v()&&(g.cursor=g.limit-e,g.ket=g.cursor,g.cursor>g.limit_backward&&(g.cursor--,g.bra=g.cursor,g.slice_del())),g.cursor=g.limit-e,g.ket=g.cursor,g.in_grouping_b(d,97,228)&&(g.bra=g.cursor,g.out_grouping_b(f,97,246)&&g.slice_del()),g.cursor=g.limit-e,g.ket=g.cursor,g.eq_s_b(1,"j")&&(g.bra=g.cursor,r=g.limit-g.cursor,g.eq_s_b(1,"o")?g.slice_del():(g.cursor=g.limit-r,g.eq_s_b(1,"u")&&g.slice_del())),g.cursor=g.limit-e,g.ket=g.cursor,g.eq_s_b(1,"o")&&(g.bra=g.cursor,g.eq_s_b(1,"j")&&g.slice_del()),g.cursor=g.limit-e,g.limit_backward=i;;){if(t=g.limit-g.cursor,g.out_grouping_b(f,97,246)){g.cursor=g.limit-t;break}if(g.cursor=g.limit-t,g.cursor<=g.limit_backward)return;g.cursor--}g.ket=g.cursor,g.cursor>g.limit_backward&&(g.cursor--,g.bra=g.cursor,n=g.slice_to(),g.eq_v_b(n)&&g.slice_del())}}(),!0}},function(i){return"function"==typeof i.update?i.update(function(i){return n.setCurrent(i),n.stem(),n.getCurrent()}):(n.setCurrent(i),n.stem(),n.getCurrent())}),i.Pipeline.registerFunction(i.fi.stemmer,"stemmer-fi"),i.fi.stopWordFilter=i.generateStopWordFilter("ei eivät emme en et ette että he heidän heidät heihin heille heillä heiltä heissä heistä heitä hän häneen hänelle hänellä häneltä hänen hänessä hänestä hänet häntä itse ja johon joiden joihin joiksi joilla joille joilta joina joissa joista joita joka joksi jolla jolle jolta jona jonka jos jossa josta jota jotka kanssa keiden keihin keiksi keille keillä keiltä keinä keissä keistä keitä keneen keneksi kenelle kenellä keneltä kenen kenenä kenessä kenestä kenet ketkä ketkä ketä koska kuin kuka kun me meidän meidät meihin meille meillä meiltä meissä meistä meitä mihin miksi mikä mille millä miltä minkä minkä minua minulla minulle minulta minun minussa minusta minut minuun minä minä missä mistä mitkä mitä mukaan mutta ne niiden niihin niiksi niille niillä niiltä niin niin niinä niissä niistä niitä noiden noihin noiksi noilla noille noilta noin noina noissa noista noita nuo nyt näiden näihin näiksi näille näillä näiltä näinä näissä näistä näitä nämä ole olemme olen olet olette oli olimme olin olisi olisimme olisin olisit olisitte olisivat olit olitte olivat olla olleet ollut on ovat poikki se sekä sen siihen siinä siitä siksi sille sillä sillä siltä sinua sinulla sinulle sinulta sinun sinussa sinusta sinut sinuun sinä sinä sitä tai te teidän teidät teihin teille teillä teiltä teissä teistä teitä tuo tuohon tuoksi tuolla tuolle tuolta tuon tuona tuossa tuosta tuota tähän täksi tälle tällä tältä tämä tämän tänä tässä tästä tätä vaan vai vaikka yli".split(" ")),i.Pipeline.registerFunction(i.fi.stopWordFilter,"stopWordFilter-fi")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.fr.js b/docs/v3/v1/assets/javascripts/lunr/lunr.fr.js deleted file mode 100644 index 3a9b9b177..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.fr.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,s,i;e.fr=function(){this.pipeline.reset(),this.pipeline.add(e.fr.trimmer,e.fr.stopWordFilter,e.fr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.fr.stemmer))},e.fr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.fr.trimmer=e.trimmerSupport.generateTrimmer(e.fr.wordCharacters),e.Pipeline.registerFunction(e.fr.trimmer,"trimmer-fr"),e.fr.stemmer=(r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,i=new function(){var e,i,n,t=[new r("col",-1,-1),new r("par",-1,-1),new r("tap",-1,-1)],u=[new r("",-1,4),new r("I",0,1),new r("U",0,2),new r("Y",0,3)],o=[new r("iqU",-1,3),new r("abl",-1,3),new r("Ièr",-1,4),new r("ièr",-1,4),new r("eus",-1,2),new r("iv",-1,1)],c=[new r("ic",-1,2),new r("abil",-1,1),new r("iv",-1,3)],a=[new r("iqUe",-1,1),new r("atrice",-1,2),new r("ance",-1,1),new r("ence",-1,5),new r("logie",-1,3),new r("able",-1,1),new r("isme",-1,1),new r("euse",-1,11),new r("iste",-1,1),new r("ive",-1,8),new r("if",-1,8),new r("usion",-1,4),new r("ation",-1,2),new r("ution",-1,4),new r("ateur",-1,2),new r("iqUes",-1,1),new r("atrices",-1,2),new r("ances",-1,1),new r("ences",-1,5),new r("logies",-1,3),new r("ables",-1,1),new r("ismes",-1,1),new r("euses",-1,11),new r("istes",-1,1),new r("ives",-1,8),new r("ifs",-1,8),new r("usions",-1,4),new r("ations",-1,2),new r("utions",-1,4),new r("ateurs",-1,2),new r("ments",-1,15),new r("ements",30,6),new r("issements",31,12),new r("ités",-1,7),new r("ment",-1,15),new r("ement",34,6),new r("issement",35,12),new r("amment",34,13),new r("emment",34,14),new r("aux",-1,10),new r("eaux",39,9),new r("eux",-1,1),new r("ité",-1,7)],l=[new r("ira",-1,1),new r("ie",-1,1),new r("isse",-1,1),new r("issante",-1,1),new r("i",-1,1),new r("irai",4,1),new r("ir",-1,1),new r("iras",-1,1),new r("ies",-1,1),new r("îmes",-1,1),new r("isses",-1,1),new r("issantes",-1,1),new r("îtes",-1,1),new r("is",-1,1),new r("irais",13,1),new r("issais",13,1),new r("irions",-1,1),new r("issions",-1,1),new r("irons",-1,1),new r("issons",-1,1),new r("issants",-1,1),new r("it",-1,1),new r("irait",21,1),new r("issait",21,1),new r("issant",-1,1),new r("iraIent",-1,1),new r("issaIent",-1,1),new r("irent",-1,1),new r("issent",-1,1),new r("iront",-1,1),new r("ît",-1,1),new r("iriez",-1,1),new r("issiez",-1,1),new r("irez",-1,1),new r("issez",-1,1)],w=[new r("a",-1,3),new r("era",0,2),new r("asse",-1,3),new r("ante",-1,3),new r("ée",-1,2),new r("ai",-1,3),new r("erai",5,2),new r("er",-1,2),new r("as",-1,3),new r("eras",8,2),new r("âmes",-1,3),new r("asses",-1,3),new r("antes",-1,3),new r("âtes",-1,3),new r("ées",-1,2),new r("ais",-1,3),new r("erais",15,2),new r("ions",-1,1),new r("erions",17,2),new r("assions",17,3),new r("erons",-1,2),new r("ants",-1,3),new r("és",-1,2),new r("ait",-1,3),new r("erait",23,2),new r("ant",-1,3),new r("aIent",-1,3),new r("eraIent",26,2),new r("èrent",-1,2),new r("assent",-1,3),new r("eront",-1,2),new r("ât",-1,3),new r("ez",-1,2),new r("iez",32,2),new r("eriez",33,2),new r("assiez",33,3),new r("erez",32,2),new r("é",-1,2)],f=[new r("e",-1,3),new r("Ière",0,2),new r("ière",0,2),new r("ion",-1,1),new r("Ier",-1,2),new r("ier",-1,2),new r("ë",-1,4)],m=[new r("ell",-1,-1),new r("eill",-1,-1),new r("enn",-1,-1),new r("onn",-1,-1),new r("ett",-1,-1)],_=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,128,130,103,8,5],b=[1,65,20,0,0,0,0,0,0,0,0,0,0,0,0,0,128],d=new s;function k(e,r,s){return!(!d.eq_s(1,e)||(d.ket=d.cursor,!d.in_grouping(_,97,251)))&&(d.slice_from(r),d.cursor=s,!0)}function p(e,r,s){return!!d.eq_s(1,e)&&(d.ket=d.cursor,d.slice_from(r),d.cursor=s,!0)}function g(){for(;!d.in_grouping(_,97,251);){if(d.cursor>=d.limit)return!0;d.cursor++}for(;!d.out_grouping(_,97,251);){if(d.cursor>=d.limit)return!0;d.cursor++}return!1}function q(){return n<=d.cursor}function v(){return i<=d.cursor}function h(){return e<=d.cursor}function z(){if(!function(){var e,r;if(d.ket=d.cursor,e=d.find_among_b(a,43)){switch(d.bra=d.cursor,e){case 1:if(!h())return!1;d.slice_del();break;case 2:if(!h())return!1;d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")&&(d.bra=d.cursor,h()?d.slice_del():d.slice_from("iqU"));break;case 3:if(!h())return!1;d.slice_from("log");break;case 4:if(!h())return!1;d.slice_from("u");break;case 5:if(!h())return!1;d.slice_from("ent");break;case 6:if(!q())return!1;if(d.slice_del(),d.ket=d.cursor,e=d.find_among_b(o,6))switch(d.bra=d.cursor,e){case 1:h()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,h()&&d.slice_del()));break;case 2:h()?d.slice_del():v()&&d.slice_from("eux");break;case 3:h()&&d.slice_del();break;case 4:q()&&d.slice_from("i")}break;case 7:if(!h())return!1;if(d.slice_del(),d.ket=d.cursor,e=d.find_among_b(c,3))switch(d.bra=d.cursor,e){case 1:h()?d.slice_del():d.slice_from("abl");break;case 2:h()?d.slice_del():d.slice_from("iqU");break;case 3:h()&&d.slice_del()}break;case 8:if(!h())return!1;if(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,h()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")))){d.bra=d.cursor,h()?d.slice_del():d.slice_from("iqU");break}break;case 9:d.slice_from("eau");break;case 10:if(!v())return!1;d.slice_from("al");break;case 11:if(h())d.slice_del();else{if(!v())return!1;d.slice_from("eux")}break;case 12:if(!v()||!d.out_grouping_b(_,97,251))return!1;d.slice_del();break;case 13:return q()&&d.slice_from("ant"),!1;case 14:return q()&&d.slice_from("ent"),!1;case 15:return r=d.limit-d.cursor,d.in_grouping_b(_,97,251)&&q()&&(d.cursor=d.limit-r,d.slice_del()),!1}return!0}return!1}()&&(d.cursor=d.limit,!function(){var e,r;if(d.cursor=n){if(s=d.limit_backward,d.limit_backward=n,d.ket=d.cursor,e=d.find_among_b(f,7))switch(d.bra=d.cursor,e){case 1:if(h()){if(i=d.limit-d.cursor,!d.eq_s_b(1,"s")&&(d.cursor=d.limit-i,!d.eq_s_b(1,"t")))break;d.slice_del()}break;case 2:d.slice_from("i");break;case 3:d.slice_del();break;case 4:d.eq_s_b(2,"gu")&&d.slice_del()}d.limit_backward=s}}();d.cursor=d.limit,d.ket=d.cursor,d.eq_s_b(1,"Y")?(d.bra=d.cursor,d.slice_from("i")):(d.cursor=d.limit,d.eq_s_b(1,"ç")&&(d.bra=d.cursor,d.slice_from("c")))}this.setCurrent=function(e){d.setCurrent(e)},this.getCurrent=function(){return d.getCurrent()},this.stem=function(){var r,s=d.cursor;return function(){for(var e,r;;){if(e=d.cursor,d.in_grouping(_,97,251)){if(d.bra=d.cursor,r=d.cursor,k("u","U",e))continue;if(d.cursor=r,k("i","I",e))continue;if(d.cursor=r,p("y","Y",e))continue}if(d.cursor=e,d.bra=e,!k("y","Y",e)){if(d.cursor=e,d.eq_s(1,"q")&&(d.bra=d.cursor,p("u","U",e)))continue;if(d.cursor=e,e>=d.limit)return;d.cursor++}}}(),d.cursor=s,function(){var r=d.cursor;if(n=d.limit,i=n,e=n,d.in_grouping(_,97,251)&&d.in_grouping(_,97,251)&&d.cursor=d.limit){d.cursor=n;break}d.cursor++}while(!d.in_grouping(_,97,251))}n=d.cursor,d.cursor=r,g()||(i=d.cursor,g()||(e=d.cursor))}(),d.limit_backward=s,d.cursor=d.limit,z(),d.cursor=d.limit,r=d.limit-d.cursor,d.find_among_b(m,5)&&(d.cursor=d.limit-r,d.ket=d.cursor,d.cursor>d.limit_backward&&(d.cursor--,d.bra=d.cursor,d.slice_del())),d.cursor=d.limit,function(){for(var e,r=1;d.out_grouping_b(_,97,251);)r--;if(r<=0){if(d.ket=d.cursor,e=d.limit-d.cursor,!d.eq_s_b(1,"é")&&(d.cursor=d.limit-e,!d.eq_s_b(1,"è")))return;d.bra=d.cursor,d.slice_from("e")}}(),d.cursor=d.limit_backward,function(){for(var e,r;r=d.cursor,d.bra=r,e=d.find_among(u,4);)switch(d.ket=d.cursor,e){case 1:d.slice_from("i");break;case 2:d.slice_from("u");break;case 3:d.slice_from("y");break;case 4:if(d.cursor>=d.limit)return;d.cursor++}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.fr.stemmer,"stemmer-fr"),e.fr.stopWordFilter=e.generateStopWordFilter("ai aie aient aies ait as au aura aurai auraient aurais aurait auras aurez auriez aurions aurons auront aux avaient avais avait avec avez aviez avions avons ayant ayez ayons c ce ceci celà ces cet cette d dans de des du elle en es est et eu eue eues eurent eus eusse eussent eusses eussiez eussions eut eux eûmes eût eûtes furent fus fusse fussent fusses fussiez fussions fut fûmes fût fûtes ici il ils j je l la le les leur leurs lui m ma mais me mes moi mon même n ne nos notre nous on ont ou par pas pour qu que quel quelle quelles quels qui s sa sans se sera serai seraient serais serait seras serez seriez serions serons seront ses soi soient sois soit sommes son sont soyez soyons suis sur t ta te tes toi ton tu un une vos votre vous y à étaient étais était étant étiez étions été étée étées étés êtes".split(" ")),e.Pipeline.registerFunction(e.fr.stopWordFilter,"stopWordFilter-fr")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.hu.js b/docs/v3/v1/assets/javascripts/lunr/lunr.hu.js deleted file mode 100644 index fa704a69c..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.hu.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var n,r,i;e.hu=function(){this.pipeline.reset(),this.pipeline.add(e.hu.trimmer,e.hu.stopWordFilter,e.hu.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hu.stemmer))},e.hu.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.hu.trimmer=e.trimmerSupport.generateTrimmer(e.hu.wordCharacters),e.Pipeline.registerFunction(e.hu.trimmer,"trimmer-hu"),e.hu.stemmer=(n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,i=new function(){var e,i=[new n("cs",-1,-1),new n("dzs",-1,-1),new n("gy",-1,-1),new n("ly",-1,-1),new n("ny",-1,-1),new n("sz",-1,-1),new n("ty",-1,-1),new n("zs",-1,-1)],a=[new n("á",-1,1),new n("é",-1,2)],t=[new n("bb",-1,-1),new n("cc",-1,-1),new n("dd",-1,-1),new n("ff",-1,-1),new n("gg",-1,-1),new n("jj",-1,-1),new n("kk",-1,-1),new n("ll",-1,-1),new n("mm",-1,-1),new n("nn",-1,-1),new n("pp",-1,-1),new n("rr",-1,-1),new n("ccs",-1,-1),new n("ss",-1,-1),new n("zzs",-1,-1),new n("tt",-1,-1),new n("vv",-1,-1),new n("ggy",-1,-1),new n("lly",-1,-1),new n("nny",-1,-1),new n("tty",-1,-1),new n("ssz",-1,-1),new n("zz",-1,-1)],s=[new n("al",-1,1),new n("el",-1,2)],c=[new n("ba",-1,-1),new n("ra",-1,-1),new n("be",-1,-1),new n("re",-1,-1),new n("ig",-1,-1),new n("nak",-1,-1),new n("nek",-1,-1),new n("val",-1,-1),new n("vel",-1,-1),new n("ul",-1,-1),new n("nál",-1,-1),new n("nél",-1,-1),new n("ból",-1,-1),new n("ról",-1,-1),new n("tól",-1,-1),new n("bõl",-1,-1),new n("rõl",-1,-1),new n("tõl",-1,-1),new n("ül",-1,-1),new n("n",-1,-1),new n("an",19,-1),new n("ban",20,-1),new n("en",19,-1),new n("ben",22,-1),new n("képpen",22,-1),new n("on",19,-1),new n("ön",19,-1),new n("képp",-1,-1),new n("kor",-1,-1),new n("t",-1,-1),new n("at",29,-1),new n("et",29,-1),new n("ként",29,-1),new n("anként",32,-1),new n("enként",32,-1),new n("onként",32,-1),new n("ot",29,-1),new n("ért",29,-1),new n("öt",29,-1),new n("hez",-1,-1),new n("hoz",-1,-1),new n("höz",-1,-1),new n("vá",-1,-1),new n("vé",-1,-1)],w=[new n("án",-1,2),new n("én",-1,1),new n("ánként",-1,3)],o=[new n("stul",-1,2),new n("astul",0,1),new n("ástul",0,3),new n("stül",-1,2),new n("estül",3,1),new n("éstül",3,4)],l=[new n("á",-1,1),new n("é",-1,2)],u=[new n("k",-1,7),new n("ak",0,4),new n("ek",0,6),new n("ok",0,5),new n("ák",0,1),new n("ék",0,2),new n("ök",0,3)],m=[new n("éi",-1,7),new n("áéi",0,6),new n("ééi",0,5),new n("é",-1,9),new n("ké",3,4),new n("aké",4,1),new n("eké",4,1),new n("oké",4,1),new n("áké",4,3),new n("éké",4,2),new n("öké",4,1),new n("éé",3,8)],k=[new n("a",-1,18),new n("ja",0,17),new n("d",-1,16),new n("ad",2,13),new n("ed",2,13),new n("od",2,13),new n("ád",2,14),new n("éd",2,15),new n("öd",2,13),new n("e",-1,18),new n("je",9,17),new n("nk",-1,4),new n("unk",11,1),new n("ánk",11,2),new n("énk",11,3),new n("ünk",11,1),new n("uk",-1,8),new n("juk",16,7),new n("ájuk",17,5),new n("ük",-1,8),new n("jük",19,7),new n("éjük",20,6),new n("m",-1,12),new n("am",22,9),new n("em",22,9),new n("om",22,9),new n("ám",22,10),new n("ém",22,11),new n("o",-1,18),new n("á",-1,19),new n("é",-1,20)],f=[new n("id",-1,10),new n("aid",0,9),new n("jaid",1,6),new n("eid",0,9),new n("jeid",3,6),new n("áid",0,7),new n("éid",0,8),new n("i",-1,15),new n("ai",7,14),new n("jai",8,11),new n("ei",7,14),new n("jei",10,11),new n("ái",7,12),new n("éi",7,13),new n("itek",-1,24),new n("eitek",14,21),new n("jeitek",15,20),new n("éitek",14,23),new n("ik",-1,29),new n("aik",18,26),new n("jaik",19,25),new n("eik",18,26),new n("jeik",21,25),new n("áik",18,27),new n("éik",18,28),new n("ink",-1,20),new n("aink",25,17),new n("jaink",26,16),new n("eink",25,17),new n("jeink",28,16),new n("áink",25,18),new n("éink",25,19),new n("aitok",-1,21),new n("jaitok",32,20),new n("áitok",-1,22),new n("im",-1,5),new n("aim",35,4),new n("jaim",36,1),new n("eim",35,4),new n("jeim",38,1),new n("áim",35,2),new n("éim",35,3)],b=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,52,14],d=new r;function g(){return e<=d.cursor}function h(){var e=d.limit-d.cursor;return!!d.find_among_b(t,23)&&(d.cursor=d.limit-e,!0)}function p(){if(d.cursor>d.limit_backward){d.cursor--,d.ket=d.cursor;var e=d.cursor-1;d.limit_backward<=e&&e<=d.limit&&(d.cursor=e,d.bra=e,d.slice_del())}}function _(){d.ket=d.cursor,d.find_among_b(c,44)&&(d.bra=d.cursor,g()&&(d.slice_del(),function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(a,2))&&(d.bra=d.cursor,g()))switch(e){case 1:d.slice_from("a");break;case 2:d.slice_from("e")}}()))}this.setCurrent=function(e){d.setCurrent(e)},this.getCurrent=function(){return d.getCurrent()},this.stem=function(){var n=d.cursor;return function(){var n,r=d.cursor;if(e=d.limit,d.in_grouping(b,97,252))for(;;){if(n=d.cursor,d.out_grouping(b,97,252))return d.cursor=n,d.find_among(i,8)||(d.cursor=n,n=d.limit)return void(e=n);d.cursor++}if(d.cursor=r,d.out_grouping(b,97,252)){for(;!d.in_grouping(b,97,252);){if(d.cursor>=d.limit)return;d.cursor++}e=d.cursor}}(),d.limit_backward=n,d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(s,2))&&(d.bra=d.cursor,g())){if((1==e||2==e)&&!h())return;d.slice_del(),p()}}(),d.cursor=d.limit,_(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(w,3))&&(d.bra=d.cursor,g()))switch(e){case 1:d.slice_from("e");break;case 2:case 3:d.slice_from("a")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(o,6))&&(d.bra=d.cursor,g()))switch(e){case 1:case 2:d.slice_del();break;case 3:d.slice_from("a");break;case 4:d.slice_from("e")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(l,2))&&(d.bra=d.cursor,g())){if((1==e||2==e)&&!h())return;d.slice_del(),p()}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(m,12))&&(d.bra=d.cursor,g()))switch(e){case 1:case 4:case 7:case 9:d.slice_del();break;case 2:case 5:case 8:d.slice_from("e");break;case 3:case 6:d.slice_from("a")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(k,31))&&(d.bra=d.cursor,g()))switch(e){case 1:case 4:case 7:case 8:case 9:case 12:case 13:case 16:case 17:case 18:d.slice_del();break;case 2:case 5:case 10:case 14:case 19:d.slice_from("a");break;case 3:case 6:case 11:case 15:case 20:d.slice_from("e")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(f,42))&&(d.bra=d.cursor,g()))switch(e){case 1:case 4:case 5:case 6:case 9:case 10:case 11:case 14:case 15:case 16:case 17:case 20:case 21:case 24:case 25:case 26:case 29:d.slice_del();break;case 2:case 7:case 12:case 18:case 22:case 27:d.slice_from("a");break;case 3:case 8:case 13:case 19:case 23:case 28:d.slice_from("e")}}(),d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,(e=d.find_among_b(u,7))&&(d.bra=d.cursor,g()))switch(e){case 1:d.slice_from("a");break;case 2:d.slice_from("e");break;case 3:case 4:case 5:case 6:case 7:d.slice_del()}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.hu.stemmer,"stemmer-hu"),e.hu.stopWordFilter=e.generateStopWordFilter("a abban ahhoz ahogy ahol aki akik akkor alatt amely amelyek amelyekben amelyeket amelyet amelynek ami amikor amit amolyan amíg annak arra arról az azok azon azonban azt aztán azután azzal azért be belül benne bár cikk cikkek cikkeket csak de e ebben eddig egy egyes egyetlen egyik egyre egyéb egész ehhez ekkor el ellen elsõ elég elõ elõször elõtt emilyen ennek erre ez ezek ezen ezt ezzel ezért fel felé hanem hiszen hogy hogyan igen ill ill. illetve ilyen ilyenkor ismét ison itt jobban jó jól kell kellett keressünk keresztül ki kívül között közül legalább legyen lehet lehetett lenne lenni lesz lett maga magát majd majd meg mellett mely melyek mert mi mikor milyen minden mindenki mindent mindig mint mintha mit mivel miért most már más másik még míg nagy nagyobb nagyon ne nekem neki nem nincs néha néhány nélkül olyan ott pedig persze rá s saját sem semmi sok sokat sokkal szemben szerint szinte számára talán tehát teljes tovább továbbá több ugyanis utolsó után utána vagy vagyis vagyok valaki valami valamint való van vannak vele vissza viszont volna volt voltak voltam voltunk által általában át én éppen és így õ õk õket össze úgy új újabb újra".split(" ")),e.Pipeline.registerFunction(e.hu.stopWordFilter,"stopWordFilter-hu")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.it.js b/docs/v3/v1/assets/javascripts/lunr/lunr.it.js deleted file mode 100644 index 293073389..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.it.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,i,n;e.it=function(){this.pipeline.reset(),this.pipeline.add(e.it.trimmer,e.it.stopWordFilter,e.it.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.it.stemmer))},e.it.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.it.trimmer=e.trimmerSupport.generateTrimmer(e.it.wordCharacters),e.Pipeline.registerFunction(e.it.trimmer,"trimmer-it"),e.it.stemmer=(r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,o,t=[new r("",-1,7),new r("qu",0,6),new r("á",0,1),new r("é",0,2),new r("í",0,3),new r("ó",0,4),new r("ú",0,5)],s=[new r("",-1,3),new r("I",0,1),new r("U",0,2)],a=[new r("la",-1,-1),new r("cela",0,-1),new r("gliela",0,-1),new r("mela",0,-1),new r("tela",0,-1),new r("vela",0,-1),new r("le",-1,-1),new r("cele",6,-1),new r("gliele",6,-1),new r("mele",6,-1),new r("tele",6,-1),new r("vele",6,-1),new r("ne",-1,-1),new r("cene",12,-1),new r("gliene",12,-1),new r("mene",12,-1),new r("sene",12,-1),new r("tene",12,-1),new r("vene",12,-1),new r("ci",-1,-1),new r("li",-1,-1),new r("celi",20,-1),new r("glieli",20,-1),new r("meli",20,-1),new r("teli",20,-1),new r("veli",20,-1),new r("gli",20,-1),new r("mi",-1,-1),new r("si",-1,-1),new r("ti",-1,-1),new r("vi",-1,-1),new r("lo",-1,-1),new r("celo",31,-1),new r("glielo",31,-1),new r("melo",31,-1),new r("telo",31,-1),new r("velo",31,-1)],u=[new r("ando",-1,1),new r("endo",-1,1),new r("ar",-1,2),new r("er",-1,2),new r("ir",-1,2)],c=[new r("ic",-1,-1),new r("abil",-1,-1),new r("os",-1,-1),new r("iv",-1,1)],w=[new r("ic",-1,1),new r("abil",-1,1),new r("iv",-1,1)],l=[new r("ica",-1,1),new r("logia",-1,3),new r("osa",-1,1),new r("ista",-1,1),new r("iva",-1,9),new r("anza",-1,1),new r("enza",-1,5),new r("ice",-1,1),new r("atrice",7,1),new r("iche",-1,1),new r("logie",-1,3),new r("abile",-1,1),new r("ibile",-1,1),new r("usione",-1,4),new r("azione",-1,2),new r("uzione",-1,4),new r("atore",-1,2),new r("ose",-1,1),new r("ante",-1,1),new r("mente",-1,1),new r("amente",19,7),new r("iste",-1,1),new r("ive",-1,9),new r("anze",-1,1),new r("enze",-1,5),new r("ici",-1,1),new r("atrici",25,1),new r("ichi",-1,1),new r("abili",-1,1),new r("ibili",-1,1),new r("ismi",-1,1),new r("usioni",-1,4),new r("azioni",-1,2),new r("uzioni",-1,4),new r("atori",-1,2),new r("osi",-1,1),new r("anti",-1,1),new r("amenti",-1,6),new r("imenti",-1,6),new r("isti",-1,1),new r("ivi",-1,9),new r("ico",-1,1),new r("ismo",-1,1),new r("oso",-1,1),new r("amento",-1,6),new r("imento",-1,6),new r("ivo",-1,9),new r("ità",-1,8),new r("istà",-1,1),new r("istè",-1,1),new r("istì",-1,1)],m=[new r("isca",-1,1),new r("enda",-1,1),new r("ata",-1,1),new r("ita",-1,1),new r("uta",-1,1),new r("ava",-1,1),new r("eva",-1,1),new r("iva",-1,1),new r("erebbe",-1,1),new r("irebbe",-1,1),new r("isce",-1,1),new r("ende",-1,1),new r("are",-1,1),new r("ere",-1,1),new r("ire",-1,1),new r("asse",-1,1),new r("ate",-1,1),new r("avate",16,1),new r("evate",16,1),new r("ivate",16,1),new r("ete",-1,1),new r("erete",20,1),new r("irete",20,1),new r("ite",-1,1),new r("ereste",-1,1),new r("ireste",-1,1),new r("ute",-1,1),new r("erai",-1,1),new r("irai",-1,1),new r("isci",-1,1),new r("endi",-1,1),new r("erei",-1,1),new r("irei",-1,1),new r("assi",-1,1),new r("ati",-1,1),new r("iti",-1,1),new r("eresti",-1,1),new r("iresti",-1,1),new r("uti",-1,1),new r("avi",-1,1),new r("evi",-1,1),new r("ivi",-1,1),new r("isco",-1,1),new r("ando",-1,1),new r("endo",-1,1),new r("Yamo",-1,1),new r("iamo",-1,1),new r("avamo",-1,1),new r("evamo",-1,1),new r("ivamo",-1,1),new r("eremo",-1,1),new r("iremo",-1,1),new r("assimo",-1,1),new r("ammo",-1,1),new r("emmo",-1,1),new r("eremmo",54,1),new r("iremmo",54,1),new r("immo",-1,1),new r("ano",-1,1),new r("iscano",58,1),new r("avano",58,1),new r("evano",58,1),new r("ivano",58,1),new r("eranno",-1,1),new r("iranno",-1,1),new r("ono",-1,1),new r("iscono",65,1),new r("arono",65,1),new r("erono",65,1),new r("irono",65,1),new r("erebbero",-1,1),new r("irebbero",-1,1),new r("assero",-1,1),new r("essero",-1,1),new r("issero",-1,1),new r("ato",-1,1),new r("ito",-1,1),new r("uto",-1,1),new r("avo",-1,1),new r("evo",-1,1),new r("ivo",-1,1),new r("ar",-1,1),new r("ir",-1,1),new r("erà",-1,1),new r("irà",-1,1),new r("erò",-1,1),new r("irò",-1,1)],f=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2,1],v=[17,65,0,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2],b=[17],d=new i;function _(e,r,i){return!(!d.eq_s(1,e)||(d.ket=d.cursor,!d.in_grouping(f,97,249)))&&(d.slice_from(r),d.cursor=i,!0)}function g(e){if(d.cursor=e,!d.in_grouping(f,97,249))return!1;for(;!d.out_grouping(f,97,249);){if(d.cursor>=d.limit)return!1;d.cursor++}return!0}function p(){var e,r=d.cursor;if(!function(){if(d.in_grouping(f,97,249)){var e=d.cursor;if(d.out_grouping(f,97,249)){for(;!d.in_grouping(f,97,249);){if(d.cursor>=d.limit)return g(e);d.cursor++}return!0}return g(e)}return!1}()){if(d.cursor=r,!d.out_grouping(f,97,249))return;if(e=d.cursor,d.out_grouping(f,97,249)){for(;!d.in_grouping(f,97,249);){if(d.cursor>=d.limit)return d.cursor=e,void(d.in_grouping(f,97,249)&&d.cursor=d.limit)return;d.cursor++}o=d.cursor}function k(){for(;!d.in_grouping(f,97,249);){if(d.cursor>=d.limit)return!1;d.cursor++}for(;!d.out_grouping(f,97,249);){if(d.cursor>=d.limit)return!1;d.cursor++}return!0}function h(){return o<=d.cursor}function q(){return e<=d.cursor}function C(){var e;if(d.ket=d.cursor,!(e=d.find_among_b(l,51)))return!1;switch(d.bra=d.cursor,e){case 1:if(!q())return!1;d.slice_del();break;case 2:if(!q())return!1;d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")&&(d.bra=d.cursor,q()&&d.slice_del());break;case 3:if(!q())return!1;d.slice_from("log");break;case 4:if(!q())return!1;d.slice_from("u");break;case 5:if(!q())return!1;d.slice_from("ente");break;case 6:if(!h())return!1;d.slice_del();break;case 7:if(!(n<=d.cursor))return!1;d.slice_del(),d.ket=d.cursor,(e=d.find_among_b(c,4))&&(d.bra=d.cursor,q()&&(d.slice_del(),1==e&&(d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,q()&&d.slice_del()))));break;case 8:if(!q())return!1;d.slice_del(),d.ket=d.cursor,(e=d.find_among_b(w,3))&&(d.bra=d.cursor,1==e&&q()&&d.slice_del());break;case 9:if(!q())return!1;d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"at")&&(d.bra=d.cursor,q()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(2,"ic")&&(d.bra=d.cursor,q()&&d.slice_del())))}return!0}function z(){var e;e=d.limit-d.cursor,d.ket=d.cursor,d.in_grouping_b(v,97,242)&&(d.bra=d.cursor,h()&&(d.slice_del(),d.ket=d.cursor,d.eq_s_b(1,"i")&&(d.bra=d.cursor,h())))?d.slice_del():d.cursor=d.limit-e,d.ket=d.cursor,d.eq_s_b(1,"h")&&(d.bra=d.cursor,d.in_grouping_b(b,99,103)&&h()&&d.slice_del())}this.setCurrent=function(e){d.setCurrent(e)},this.getCurrent=function(){return d.getCurrent()},this.stem=function(){var r,i,c,w=d.cursor;return function(){for(var e,r,i,n,o=d.cursor;;){if(d.bra=d.cursor,e=d.find_among(t,7))switch(d.ket=d.cursor,e){case 1:d.slice_from("à");continue;case 2:d.slice_from("è");continue;case 3:d.slice_from("ì");continue;case 4:d.slice_from("ò");continue;case 5:d.slice_from("ù");continue;case 6:d.slice_from("qU");continue;case 7:if(d.cursor>=d.limit)break;d.cursor++;continue}break}for(d.cursor=o;;)for(r=d.cursor;;){if(i=d.cursor,d.in_grouping(f,97,249)){if(d.bra=d.cursor,n=d.cursor,_("u","U",i))break;if(d.cursor=n,_("i","I",i))break}if(d.cursor=i,d.cursor>=d.limit)return void(d.cursor=r);d.cursor++}}(),d.cursor=w,r=d.cursor,o=d.limit,n=o,e=o,p(),d.cursor=r,k()&&(n=d.cursor,k()&&(e=d.cursor)),d.limit_backward=w,d.cursor=d.limit,function(){var e;if(d.ket=d.cursor,d.find_among_b(a,37)&&(d.bra=d.cursor,(e=d.find_among_b(u,5))&&h()))switch(e){case 1:d.slice_del();break;case 2:d.slice_from("e")}}(),d.cursor=d.limit,C()||(d.cursor=d.limit,d.cursor>=o&&(c=d.limit_backward,d.limit_backward=o,d.ket=d.cursor,(i=d.find_among_b(m,87))&&(d.bra=d.cursor,1==i&&d.slice_del()),d.limit_backward=c)),d.cursor=d.limit,z(),d.cursor=d.limit_backward,function(){for(var e;d.bra=d.cursor,e=d.find_among(s,3);)switch(d.ket=d.cursor,e){case 1:d.slice_from("i");break;case 2:d.slice_from("u");break;case 3:if(d.cursor>=d.limit)return;d.cursor++}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.it.stemmer,"stemmer-it"),e.it.stopWordFilter=e.generateStopWordFilter("a abbia abbiamo abbiano abbiate ad agl agli ai al all alla alle allo anche avemmo avendo avesse avessero avessi avessimo aveste avesti avete aveva avevamo avevano avevate avevi avevo avrai avranno avrebbe avrebbero avrei avremmo avremo avreste avresti avrete avrà avrò avuta avute avuti avuto c che chi ci coi col come con contro cui da dagl dagli dai dal dall dalla dalle dallo degl degli dei del dell della delle dello di dov dove e ebbe ebbero ebbi ed era erano eravamo eravate eri ero essendo faccia facciamo facciano facciate faccio facemmo facendo facesse facessero facessi facessimo faceste facesti faceva facevamo facevano facevate facevi facevo fai fanno farai faranno farebbe farebbero farei faremmo faremo fareste faresti farete farà farò fece fecero feci fosse fossero fossi fossimo foste fosti fu fui fummo furono gli ha hai hanno ho i il in io l la le lei li lo loro lui ma mi mia mie miei mio ne negl negli nei nel nell nella nelle nello noi non nostra nostre nostri nostro o per perché più quale quanta quante quanti quanto quella quelle quelli quello questa queste questi questo sarai saranno sarebbe sarebbero sarei saremmo saremo sareste saresti sarete sarà sarò se sei si sia siamo siano siate siete sono sta stai stando stanno starai staranno starebbe starebbero starei staremmo staremo stareste staresti starete starà starò stava stavamo stavano stavate stavi stavo stemmo stesse stessero stessi stessimo steste stesti stette stettero stetti stia stiamo stiano stiate sto su sua sue sugl sugli sui sul sull sulla sulle sullo suo suoi ti tra tu tua tue tuo tuoi tutti tutto un una uno vi voi vostra vostre vostri vostro è".split(" ")),e.Pipeline.registerFunction(e.it.stopWordFilter,"stopWordFilter-it")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.jp.js b/docs/v3/v1/assets/javascripts/lunr/lunr.jp.js deleted file mode 100644 index a33c3c71c..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.jp.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.jp=function(){this.pipeline.reset(),this.pipeline.add(e.jp.stopWordFilter,e.jp.stemmer),r?this.tokenizer=e.jp.tokenizer:(e.tokenizer&&(e.tokenizer=e.jp.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.jp.tokenizer))};var t=new e.TinySegmenter;e.jp.tokenizer=function(n){if(!arguments.length||null==n||null==n)return[];if(Array.isArray(n))return n.map(function(t){return r?new e.Token(t.toLowerCase()):t.toLowerCase()});for(var i=n.toString().toLowerCase().replace(/^\s+/,""),o=i.length-1;o>=0;o--)if(/\S/.test(i.charAt(o))){i=i.substring(0,o+1);break}return t.segment(i).filter(function(e){return!!e}).map(function(t){return r?new e.Token(t):t})},e.jp.stemmer=function(e){return e},e.Pipeline.registerFunction(e.jp.stemmer,"stemmer-jp"),e.jp.wordCharacters="一二三四五六七八九十百千万億兆一-龠々〆ヵヶぁ-んァ-ヴーア-ン゙a-zA-Za-zA-Z0-90-9",e.jp.stopWordFilter=function(t){if(-1===e.jp.stopWordFilter.stopWords.indexOf(r?t.toString():t))return t},e.jp.stopWordFilter=e.generateStopWordFilter("これ それ あれ この その あの ここ そこ あそこ こちら どこ だれ なに なん 何 私 貴方 貴方方 我々 私達 あの人 あのかた 彼女 彼 です あります おります います は が の に を で え から まで より も どの と し それで しかし".split(" ")),e.Pipeline.registerFunction(e.jp.stopWordFilter,"stopWordFilter-jp")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.multi.js b/docs/v3/v1/assets/javascripts/lunr/lunr.multi.js deleted file mode 100644 index d3dbc860c..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.multi.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(e.lunr)}(this,function(){return function(e){e.multiLanguage=function(){for(var i=Array.prototype.slice.call(arguments),t=i.join("-"),r="",n=[],s=[],p=0;p=l.limit)return;l.cursor=r+1}for(;!l.out_grouping(a,97,248);){if(l.cursor>=l.limit)return;l.cursor++}(i=l.cursor)=i&&(r=l.limit_backward,l.limit_backward=i,l.ket=l.cursor,e=l.find_among_b(t,29),l.limit_backward=r,e))switch(l.bra=l.cursor,e){case 1:l.slice_del();break;case 2:n=l.limit-l.cursor,l.in_grouping_b(m,98,122)?l.slice_del():(l.cursor=l.limit-n,l.eq_s_b(1,"k")&&l.out_grouping_b(a,97,248)&&l.slice_del());break;case 3:l.slice_from("er")}}(),l.cursor=l.limit,n=l.limit-l.cursor,l.cursor>=i&&(r=l.limit_backward,l.limit_backward=i,l.ket=l.cursor,l.find_among_b(o,2)?(l.bra=l.cursor,l.limit_backward=r,l.cursor=l.limit-n,l.cursor>l.limit_backward&&(l.cursor--,l.bra=l.cursor,l.slice_del())):l.limit_backward=r),l.cursor=l.limit,l.cursor>=i&&(d=l.limit_backward,l.limit_backward=i,l.ket=l.cursor,(u=l.find_among_b(s,11))?(l.bra=l.cursor,l.limit_backward=d,1==u&&l.slice_del()):l.limit_backward=d),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.no.stemmer,"stemmer-no"),e.no.stopWordFilter=e.generateStopWordFilter("alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å".split(" ")),e.Pipeline.registerFunction(e.no.stopWordFilter,"stopWordFilter-no")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.pt.js b/docs/v3/v1/assets/javascripts/lunr/lunr.pt.js deleted file mode 100644 index 51035c969..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.pt.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,s,n;e.pt=function(){this.pipeline.reset(),this.pipeline.add(e.pt.trimmer,e.pt.stopWordFilter,e.pt.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.pt.stemmer))},e.pt.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.pt.trimmer=e.trimmerSupport.generateTrimmer(e.pt.wordCharacters),e.Pipeline.registerFunction(e.pt.trimmer,"trimmer-pt"),e.pt.stemmer=(r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,i,o=[new r("",-1,3),new r("ã",0,1),new r("õ",0,2)],a=[new r("",-1,3),new r("a~",0,1),new r("o~",0,2)],t=[new r("ic",-1,-1),new r("ad",-1,-1),new r("os",-1,-1),new r("iv",-1,1)],u=[new r("ante",-1,1),new r("avel",-1,1),new r("ível",-1,1)],w=[new r("ic",-1,1),new r("abil",-1,1),new r("iv",-1,1)],m=[new r("ica",-1,1),new r("ância",-1,1),new r("ência",-1,4),new r("ira",-1,9),new r("adora",-1,1),new r("osa",-1,1),new r("ista",-1,1),new r("iva",-1,8),new r("eza",-1,1),new r("logía",-1,2),new r("idade",-1,7),new r("ante",-1,1),new r("mente",-1,6),new r("amente",12,5),new r("ável",-1,1),new r("ível",-1,1),new r("ución",-1,3),new r("ico",-1,1),new r("ismo",-1,1),new r("oso",-1,1),new r("amento",-1,1),new r("imento",-1,1),new r("ivo",-1,8),new r("aça~o",-1,1),new r("ador",-1,1),new r("icas",-1,1),new r("ências",-1,4),new r("iras",-1,9),new r("adoras",-1,1),new r("osas",-1,1),new r("istas",-1,1),new r("ivas",-1,8),new r("ezas",-1,1),new r("logías",-1,2),new r("idades",-1,7),new r("uciones",-1,3),new r("adores",-1,1),new r("antes",-1,1),new r("aço~es",-1,1),new r("icos",-1,1),new r("ismos",-1,1),new r("osos",-1,1),new r("amentos",-1,1),new r("imentos",-1,1),new r("ivos",-1,8)],c=[new r("ada",-1,1),new r("ida",-1,1),new r("ia",-1,1),new r("aria",2,1),new r("eria",2,1),new r("iria",2,1),new r("ara",-1,1),new r("era",-1,1),new r("ira",-1,1),new r("ava",-1,1),new r("asse",-1,1),new r("esse",-1,1),new r("isse",-1,1),new r("aste",-1,1),new r("este",-1,1),new r("iste",-1,1),new r("ei",-1,1),new r("arei",16,1),new r("erei",16,1),new r("irei",16,1),new r("am",-1,1),new r("iam",20,1),new r("ariam",21,1),new r("eriam",21,1),new r("iriam",21,1),new r("aram",20,1),new r("eram",20,1),new r("iram",20,1),new r("avam",20,1),new r("em",-1,1),new r("arem",29,1),new r("erem",29,1),new r("irem",29,1),new r("assem",29,1),new r("essem",29,1),new r("issem",29,1),new r("ado",-1,1),new r("ido",-1,1),new r("ando",-1,1),new r("endo",-1,1),new r("indo",-1,1),new r("ara~o",-1,1),new r("era~o",-1,1),new r("ira~o",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("ir",-1,1),new r("as",-1,1),new r("adas",47,1),new r("idas",47,1),new r("ias",47,1),new r("arias",50,1),new r("erias",50,1),new r("irias",50,1),new r("aras",47,1),new r("eras",47,1),new r("iras",47,1),new r("avas",47,1),new r("es",-1,1),new r("ardes",58,1),new r("erdes",58,1),new r("irdes",58,1),new r("ares",58,1),new r("eres",58,1),new r("ires",58,1),new r("asses",58,1),new r("esses",58,1),new r("isses",58,1),new r("astes",58,1),new r("estes",58,1),new r("istes",58,1),new r("is",-1,1),new r("ais",71,1),new r("eis",71,1),new r("areis",73,1),new r("ereis",73,1),new r("ireis",73,1),new r("áreis",73,1),new r("éreis",73,1),new r("íreis",73,1),new r("ásseis",73,1),new r("ésseis",73,1),new r("ísseis",73,1),new r("áveis",73,1),new r("íeis",73,1),new r("aríeis",84,1),new r("eríeis",84,1),new r("iríeis",84,1),new r("ados",-1,1),new r("idos",-1,1),new r("amos",-1,1),new r("áramos",90,1),new r("éramos",90,1),new r("íramos",90,1),new r("ávamos",90,1),new r("íamos",90,1),new r("aríamos",95,1),new r("eríamos",95,1),new r("iríamos",95,1),new r("emos",-1,1),new r("aremos",99,1),new r("eremos",99,1),new r("iremos",99,1),new r("ássemos",99,1),new r("êssemos",99,1),new r("íssemos",99,1),new r("imos",-1,1),new r("armos",-1,1),new r("ermos",-1,1),new r("irmos",-1,1),new r("ámos",-1,1),new r("arás",-1,1),new r("erás",-1,1),new r("irás",-1,1),new r("eu",-1,1),new r("iu",-1,1),new r("ou",-1,1),new r("ará",-1,1),new r("erá",-1,1),new r("irá",-1,1)],l=[new r("a",-1,1),new r("i",-1,1),new r("o",-1,1),new r("os",-1,1),new r("á",-1,1),new r("í",-1,1),new r("ó",-1,1)],f=[new r("e",-1,1),new r("ç",-1,2),new r("é",-1,1),new r("ê",-1,1)],d=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,3,19,12,2],v=new s;function p(){if(v.out_grouping(d,97,250)){for(;!v.in_grouping(d,97,250);){if(v.cursor>=v.limit)return!0;v.cursor++}return!1}return!0}function _(){var e,r,s=v.cursor;if(v.in_grouping(d,97,250))if(e=v.cursor,p()){if(v.cursor=e,function(){if(v.in_grouping(d,97,250))for(;!v.out_grouping(d,97,250);){if(v.cursor>=v.limit)return!1;v.cursor++}return i=v.cursor,!0}())return}else i=v.cursor;if(v.cursor=s,v.out_grouping(d,97,250)){if(r=v.cursor,p()){if(v.cursor=r,!v.in_grouping(d,97,250)||v.cursor>=v.limit)return;v.cursor++}i=v.cursor}}function h(){for(;!v.in_grouping(d,97,250);){if(v.cursor>=v.limit)return!1;v.cursor++}for(;!v.out_grouping(d,97,250);){if(v.cursor>=v.limit)return!1;v.cursor++}return!0}function b(){return i<=v.cursor}function g(){return e<=v.cursor}function k(){var e;if(v.ket=v.cursor,!(e=v.find_among_b(m,45)))return!1;switch(v.bra=v.cursor,e){case 1:if(!g())return!1;v.slice_del();break;case 2:if(!g())return!1;v.slice_from("log");break;case 3:if(!g())return!1;v.slice_from("u");break;case 4:if(!g())return!1;v.slice_from("ente");break;case 5:if(!(n<=v.cursor))return!1;v.slice_del(),v.ket=v.cursor,(e=v.find_among_b(t,4))&&(v.bra=v.cursor,g()&&(v.slice_del(),1==e&&(v.ket=v.cursor,v.eq_s_b(2,"at")&&(v.bra=v.cursor,g()&&v.slice_del()))));break;case 6:if(!g())return!1;v.slice_del(),v.ket=v.cursor,(e=v.find_among_b(u,3))&&(v.bra=v.cursor,1==e&&g()&&v.slice_del());break;case 7:if(!g())return!1;v.slice_del(),v.ket=v.cursor,(e=v.find_among_b(w,3))&&(v.bra=v.cursor,1==e&&g()&&v.slice_del());break;case 8:if(!g())return!1;v.slice_del(),v.ket=v.cursor,v.eq_s_b(2,"at")&&(v.bra=v.cursor,g()&&v.slice_del());break;case 9:if(!b()||!v.eq_s_b(1,"e"))return!1;v.slice_from("ir")}return!0}function q(e,r){if(v.eq_s_b(1,e)){v.bra=v.cursor;var s=v.limit-v.cursor;if(v.eq_s_b(1,r))return v.cursor=v.limit-s,b()&&v.slice_del(),!1}return!0}function j(){if(!k()&&(v.cursor=v.limit,!function(){var e,r;if(v.cursor>=i){if(r=v.limit_backward,v.limit_backward=i,v.ket=v.cursor,e=v.find_among_b(c,120))return v.bra=v.cursor,1==e&&v.slice_del(),v.limit_backward=r,!0;v.limit_backward=r}return!1}()))return v.cursor=v.limit,v.ket=v.cursor,void((e=v.find_among_b(l,7))&&(v.bra=v.cursor,1==e&&b()&&v.slice_del()));var e;v.cursor=v.limit,v.ket=v.cursor,v.eq_s_b(1,"i")&&(v.bra=v.cursor,v.eq_s_b(1,"c")&&(v.cursor=v.limit,b()&&v.slice_del()))}this.setCurrent=function(e){v.setCurrent(e)},this.getCurrent=function(){return v.getCurrent()},this.stem=function(){var r,s=v.cursor;return function(){for(var e;;){if(v.bra=v.cursor,e=v.find_among(o,3))switch(v.ket=v.cursor,e){case 1:v.slice_from("a~");continue;case 2:v.slice_from("o~");continue;case 3:if(v.cursor>=v.limit)break;v.cursor++;continue}break}}(),v.cursor=s,r=v.cursor,i=v.limit,n=i,e=i,_(),v.cursor=r,h()&&(n=v.cursor,h()&&(e=v.cursor)),v.limit_backward=s,v.cursor=v.limit,j(),v.cursor=v.limit,function(){var e;if(v.ket=v.cursor,e=v.find_among_b(f,4))switch(v.bra=v.cursor,e){case 1:b()&&(v.slice_del(),v.ket=v.cursor,v.limit,v.cursor,q("u","g")&&q("i","c"));break;case 2:v.slice_from("c")}}(),v.cursor=v.limit_backward,function(){for(var e;;){if(v.bra=v.cursor,e=v.find_among(a,3))switch(v.ket=v.cursor,e){case 1:v.slice_from("ã");continue;case 2:v.slice_from("õ");continue;case 3:if(v.cursor>=v.limit)break;v.cursor++;continue}break}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.pt.stemmer,"stemmer-pt"),e.pt.stopWordFilter=e.generateStopWordFilter("a ao aos aquela aquelas aquele aqueles aquilo as até com como da das de dela delas dele deles depois do dos e ela elas ele eles em entre era eram essa essas esse esses esta estamos estas estava estavam este esteja estejam estejamos estes esteve estive estivemos estiver estivera estiveram estiverem estivermos estivesse estivessem estivéramos estivéssemos estou está estávamos estão eu foi fomos for fora foram forem formos fosse fossem fui fôramos fôssemos haja hajam hajamos havemos hei houve houvemos houver houvera houveram houverei houverem houveremos houveria houveriam houvermos houverá houverão houveríamos houvesse houvessem houvéramos houvéssemos há hão isso isto já lhe lhes mais mas me mesmo meu meus minha minhas muito na nas nem no nos nossa nossas nosso nossos num numa não nós o os ou para pela pelas pelo pelos por qual quando que quem se seja sejam sejamos sem serei seremos seria seriam será serão seríamos seu seus somos sou sua suas são só também te tem temos tenha tenham tenhamos tenho terei teremos teria teriam terá terão teríamos teu teus teve tinha tinham tive tivemos tiver tivera tiveram tiverem tivermos tivesse tivessem tivéramos tivéssemos tu tua tuas tém tínhamos um uma você vocês vos à às éramos".split(" ")),e.Pipeline.registerFunction(e.pt.stopWordFilter,"stopWordFilter-pt")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.ro.js b/docs/v3/v1/assets/javascripts/lunr/lunr.ro.js deleted file mode 100644 index 155cb5621..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.ro.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var i,r,n;e.ro=function(){this.pipeline.reset(),this.pipeline.add(e.ro.trimmer,e.ro.stopWordFilter,e.ro.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ro.stemmer))},e.ro.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.ro.trimmer=e.trimmerSupport.generateTrimmer(e.ro.wordCharacters),e.Pipeline.registerFunction(e.ro.trimmer,"trimmer-ro"),e.ro.stemmer=(i=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,t,a,o=[new i("",-1,3),new i("I",0,1),new i("U",0,2)],s=[new i("ea",-1,3),new i("aţia",-1,7),new i("aua",-1,2),new i("iua",-1,4),new i("aţie",-1,7),new i("ele",-1,3),new i("ile",-1,5),new i("iile",6,4),new i("iei",-1,4),new i("atei",-1,6),new i("ii",-1,4),new i("ului",-1,1),new i("ul",-1,1),new i("elor",-1,3),new i("ilor",-1,4),new i("iilor",14,4)],c=[new i("icala",-1,4),new i("iciva",-1,4),new i("ativa",-1,5),new i("itiva",-1,6),new i("icale",-1,4),new i("aţiune",-1,5),new i("iţiune",-1,6),new i("atoare",-1,5),new i("itoare",-1,6),new i("ătoare",-1,5),new i("icitate",-1,4),new i("abilitate",-1,1),new i("ibilitate",-1,2),new i("ivitate",-1,3),new i("icive",-1,4),new i("ative",-1,5),new i("itive",-1,6),new i("icali",-1,4),new i("atori",-1,5),new i("icatori",18,4),new i("itori",-1,6),new i("ători",-1,5),new i("icitati",-1,4),new i("abilitati",-1,1),new i("ivitati",-1,3),new i("icivi",-1,4),new i("ativi",-1,5),new i("itivi",-1,6),new i("icităi",-1,4),new i("abilităi",-1,1),new i("ivităi",-1,3),new i("icităţi",-1,4),new i("abilităţi",-1,1),new i("ivităţi",-1,3),new i("ical",-1,4),new i("ator",-1,5),new i("icator",35,4),new i("itor",-1,6),new i("ător",-1,5),new i("iciv",-1,4),new i("ativ",-1,5),new i("itiv",-1,6),new i("icală",-1,4),new i("icivă",-1,4),new i("ativă",-1,5),new i("itivă",-1,6)],u=[new i("ica",-1,1),new i("abila",-1,1),new i("ibila",-1,1),new i("oasa",-1,1),new i("ata",-1,1),new i("ita",-1,1),new i("anta",-1,1),new i("ista",-1,3),new i("uta",-1,1),new i("iva",-1,1),new i("ic",-1,1),new i("ice",-1,1),new i("abile",-1,1),new i("ibile",-1,1),new i("isme",-1,3),new i("iune",-1,2),new i("oase",-1,1),new i("ate",-1,1),new i("itate",17,1),new i("ite",-1,1),new i("ante",-1,1),new i("iste",-1,3),new i("ute",-1,1),new i("ive",-1,1),new i("ici",-1,1),new i("abili",-1,1),new i("ibili",-1,1),new i("iuni",-1,2),new i("atori",-1,1),new i("osi",-1,1),new i("ati",-1,1),new i("itati",30,1),new i("iti",-1,1),new i("anti",-1,1),new i("isti",-1,3),new i("uti",-1,1),new i("işti",-1,3),new i("ivi",-1,1),new i("ităi",-1,1),new i("oşi",-1,1),new i("ităţi",-1,1),new i("abil",-1,1),new i("ibil",-1,1),new i("ism",-1,3),new i("ator",-1,1),new i("os",-1,1),new i("at",-1,1),new i("it",-1,1),new i("ant",-1,1),new i("ist",-1,3),new i("ut",-1,1),new i("iv",-1,1),new i("ică",-1,1),new i("abilă",-1,1),new i("ibilă",-1,1),new i("oasă",-1,1),new i("ată",-1,1),new i("ită",-1,1),new i("antă",-1,1),new i("istă",-1,3),new i("ută",-1,1),new i("ivă",-1,1)],w=[new i("ea",-1,1),new i("ia",-1,1),new i("esc",-1,1),new i("ăsc",-1,1),new i("ind",-1,1),new i("ând",-1,1),new i("are",-1,1),new i("ere",-1,1),new i("ire",-1,1),new i("âre",-1,1),new i("se",-1,2),new i("ase",10,1),new i("sese",10,2),new i("ise",10,1),new i("use",10,1),new i("âse",10,1),new i("eşte",-1,1),new i("ăşte",-1,1),new i("eze",-1,1),new i("ai",-1,1),new i("eai",19,1),new i("iai",19,1),new i("sei",-1,2),new i("eşti",-1,1),new i("ăşti",-1,1),new i("ui",-1,1),new i("ezi",-1,1),new i("âi",-1,1),new i("aşi",-1,1),new i("seşi",-1,2),new i("aseşi",29,1),new i("seseşi",29,2),new i("iseşi",29,1),new i("useşi",29,1),new i("âseşi",29,1),new i("işi",-1,1),new i("uşi",-1,1),new i("âşi",-1,1),new i("aţi",-1,2),new i("eaţi",38,1),new i("iaţi",38,1),new i("eţi",-1,2),new i("iţi",-1,2),new i("âţi",-1,2),new i("arăţi",-1,1),new i("serăţi",-1,2),new i("aserăţi",45,1),new i("seserăţi",45,2),new i("iserăţi",45,1),new i("userăţi",45,1),new i("âserăţi",45,1),new i("irăţi",-1,1),new i("urăţi",-1,1),new i("ârăţi",-1,1),new i("am",-1,1),new i("eam",54,1),new i("iam",54,1),new i("em",-1,2),new i("asem",57,1),new i("sesem",57,2),new i("isem",57,1),new i("usem",57,1),new i("âsem",57,1),new i("im",-1,2),new i("âm",-1,2),new i("ăm",-1,2),new i("arăm",65,1),new i("serăm",65,2),new i("aserăm",67,1),new i("seserăm",67,2),new i("iserăm",67,1),new i("userăm",67,1),new i("âserăm",67,1),new i("irăm",65,1),new i("urăm",65,1),new i("ârăm",65,1),new i("au",-1,1),new i("eau",76,1),new i("iau",76,1),new i("indu",-1,1),new i("ându",-1,1),new i("ez",-1,1),new i("ească",-1,1),new i("ară",-1,1),new i("seră",-1,2),new i("aseră",84,1),new i("seseră",84,2),new i("iseră",84,1),new i("useră",84,1),new i("âseră",84,1),new i("iră",-1,1),new i("ură",-1,1),new i("âră",-1,1),new i("ează",-1,1)],m=[new i("a",-1,1),new i("e",-1,1),new i("ie",1,1),new i("i",-1,1),new i("ă",-1,1)],l=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,2,32,0,0,4],f=new r;function p(e,i){f.eq_s(1,e)&&(f.ket=f.cursor,f.in_grouping(l,97,259)&&f.slice_from(i))}function d(){if(f.out_grouping(l,97,259)){for(;!f.in_grouping(l,97,259);){if(f.cursor>=f.limit)return!0;f.cursor++}return!1}return!0}function b(){var e,i,r=f.cursor;if(f.in_grouping(l,97,259)){if(e=f.cursor,!d())return void(a=f.cursor);if(f.cursor=e,!function(){if(f.in_grouping(l,97,259))for(;!f.out_grouping(l,97,259);){if(f.cursor>=f.limit)return!0;f.cursor++}return!1}())return void(a=f.cursor)}f.cursor=r,f.out_grouping(l,97,259)&&(i=f.cursor,d()&&(f.cursor=i,f.in_grouping(l,97,259)&&f.cursor=f.limit)return!1;f.cursor++}for(;!f.out_grouping(l,97,259);){if(f.cursor>=f.limit)return!1;f.cursor++}return!0}function _(){return t<=f.cursor}function g(){var i,r=f.limit-f.cursor;if(f.ket=f.cursor,(i=f.find_among_b(c,46))&&(f.bra=f.cursor,_())){switch(i){case 1:f.slice_from("abil");break;case 2:f.slice_from("ibil");break;case 3:f.slice_from("iv");break;case 4:f.slice_from("ic");break;case 5:f.slice_from("at");break;case 6:f.slice_from("it")}return e=!0,f.cursor=f.limit-r,!0}return!1}function k(){var i,r;for(e=!1;;)if(r=f.limit-f.cursor,!g()){f.cursor=f.limit-r;break}if(f.ket=f.cursor,(i=f.find_among_b(u,62))&&(f.bra=f.cursor,n<=f.cursor)){switch(i){case 1:f.slice_del();break;case 2:f.eq_s_b(1,"ţ")&&(f.bra=f.cursor,f.slice_from("t"));break;case 3:f.slice_from("ist")}e=!0}}function h(){var e;f.ket=f.cursor,(e=f.find_among_b(m,5))&&(f.bra=f.cursor,a<=f.cursor&&1==e&&f.slice_del())}this.setCurrent=function(e){f.setCurrent(e)},this.getCurrent=function(){return f.getCurrent()},this.stem=function(){var i,r=f.cursor;return function(){for(var e,i;e=f.cursor,f.in_grouping(l,97,259)&&(i=f.cursor,f.bra=i,p("u","U"),f.cursor=i,p("i","I")),f.cursor=e,!(f.cursor>=f.limit);)f.cursor++}(),f.cursor=r,i=f.cursor,a=f.limit,t=a,n=a,b(),f.cursor=i,v()&&(t=f.cursor,v()&&(n=f.cursor)),f.limit_backward=r,f.cursor=f.limit,function(){var e,i;if(f.ket=f.cursor,(e=f.find_among_b(s,16))&&(f.bra=f.cursor,_()))switch(e){case 1:f.slice_del();break;case 2:f.slice_from("a");break;case 3:f.slice_from("e");break;case 4:f.slice_from("i");break;case 5:i=f.limit-f.cursor,f.eq_s_b(2,"ab")||(f.cursor=f.limit-i,f.slice_from("i"));break;case 6:f.slice_from("at");break;case 7:f.slice_from("aţi")}}(),f.cursor=f.limit,k(),f.cursor=f.limit,e||(f.cursor=f.limit,function(){var e,i,r;if(f.cursor>=a){if(i=f.limit_backward,f.limit_backward=a,f.ket=f.cursor,e=f.find_among_b(w,94))switch(f.bra=f.cursor,e){case 1:if(r=f.limit-f.cursor,!f.out_grouping_b(l,97,259)&&(f.cursor=f.limit-r,!f.eq_s_b(1,"u")))break;case 2:f.slice_del()}f.limit_backward=i}}(),f.cursor=f.limit),h(),f.cursor=f.limit_backward,function(){for(var e;;){if(f.bra=f.cursor,e=f.find_among(o,3))switch(f.ket=f.cursor,e){case 1:f.slice_from("i");continue;case 2:f.slice_from("u");continue;case 3:if(f.cursor>=f.limit)break;f.cursor++;continue}break}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.ro.stemmer,"stemmer-ro"),e.ro.stopWordFilter=e.generateStopWordFilter("acea aceasta această aceea acei aceia acel acela acele acelea acest acesta aceste acestea aceşti aceştia acolo acord acum ai aia aibă aici al ale alea altceva altcineva am ar are asemenea asta astea astăzi asupra au avea avem aveţi azi aş aşadar aţi bine bucur bună ca care caut ce cel ceva chiar cinci cine cineva contra cu cum cumva curând curînd când cât câte câtva câţi cînd cît cîte cîtva cîţi că căci cărei căror cărui către da dacă dar datorită dată dau de deci deja deoarece departe deşi din dinaintea dintr- dintre doi doilea două drept după dă ea ei el ele eram este eu eşti face fata fi fie fiecare fii fim fiu fiţi frumos fără graţie halbă iar ieri la le li lor lui lângă lîngă mai mea mei mele mereu meu mi mie mine mult multă mulţi mulţumesc mâine mîine mă ne nevoie nici nicăieri nimeni nimeri nimic nişte noastre noastră noi noroc nostru nouă noştri nu opt ori oricare orice oricine oricum oricând oricât oricînd oricît oriunde patra patru patrulea pe pentru peste pic poate pot prea prima primul prin puţin puţina puţină până pînă rog sa sale sau se spate spre sub sunt suntem sunteţi sută sînt sîntem sînteţi să săi său ta tale te timp tine toate toată tot totuşi toţi trei treia treilea tu tăi tău un una unde undeva unei uneia unele uneori unii unor unora unu unui unuia unul vi voastre voastră voi vostru vouă voştri vreme vreo vreun vă zece zero zi zice îi îl îmi împotriva în înainte înaintea încotro încât încît între întrucât întrucît îţi ăla ălea ăsta ăstea ăştia şapte şase şi ştiu ţi ţie".split(" ")),e.Pipeline.registerFunction(e.ro.stopWordFilter,"stopWordFilter-ro")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.ru.js b/docs/v3/v1/assets/javascripts/lunr/lunr.ru.js deleted file mode 100644 index 078609ad8..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.ru.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var n,r,t;e.ru=function(){this.pipeline.reset(),this.pipeline.add(e.ru.trimmer,e.ru.stopWordFilter,e.ru.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ru.stemmer))},e.ru.wordCharacters="Ѐ-҄҇-ԯᴫᵸⷠ-ⷿꙀ-ꚟ︮︯",e.ru.trimmer=e.trimmerSupport.generateTrimmer(e.ru.wordCharacters),e.Pipeline.registerFunction(e.ru.trimmer,"trimmer-ru"),e.ru.stemmer=(n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,t=new function(){var e,t,w=[new n("в",-1,1),new n("ив",0,2),new n("ыв",0,2),new n("вши",-1,1),new n("ивши",3,2),new n("ывши",3,2),new n("вшись",-1,1),new n("ившись",6,2),new n("ывшись",6,2)],i=[new n("ее",-1,1),new n("ие",-1,1),new n("ое",-1,1),new n("ые",-1,1),new n("ими",-1,1),new n("ыми",-1,1),new n("ей",-1,1),new n("ий",-1,1),new n("ой",-1,1),new n("ый",-1,1),new n("ем",-1,1),new n("им",-1,1),new n("ом",-1,1),new n("ым",-1,1),new n("его",-1,1),new n("ого",-1,1),new n("ему",-1,1),new n("ому",-1,1),new n("их",-1,1),new n("ых",-1,1),new n("ею",-1,1),new n("ою",-1,1),new n("ую",-1,1),new n("юю",-1,1),new n("ая",-1,1),new n("яя",-1,1)],u=[new n("ем",-1,1),new n("нн",-1,1),new n("вш",-1,1),new n("ивш",2,2),new n("ывш",2,2),new n("щ",-1,1),new n("ющ",5,1),new n("ующ",6,2)],s=[new n("сь",-1,1),new n("ся",-1,1)],o=[new n("ла",-1,1),new n("ила",0,2),new n("ыла",0,2),new n("на",-1,1),new n("ена",3,2),new n("ете",-1,1),new n("ите",-1,2),new n("йте",-1,1),new n("ейте",7,2),new n("уйте",7,2),new n("ли",-1,1),new n("или",10,2),new n("ыли",10,2),new n("й",-1,1),new n("ей",13,2),new n("уй",13,2),new n("л",-1,1),new n("ил",16,2),new n("ыл",16,2),new n("ем",-1,1),new n("им",-1,2),new n("ым",-1,2),new n("н",-1,1),new n("ен",22,2),new n("ло",-1,1),new n("ило",24,2),new n("ыло",24,2),new n("но",-1,1),new n("ено",27,2),new n("нно",27,1),new n("ет",-1,1),new n("ует",30,2),new n("ит",-1,2),new n("ыт",-1,2),new n("ют",-1,1),new n("уют",34,2),new n("ят",-1,2),new n("ны",-1,1),new n("ены",37,2),new n("ть",-1,1),new n("ить",39,2),new n("ыть",39,2),new n("ешь",-1,1),new n("ишь",-1,2),new n("ю",-1,2),new n("ую",44,2)],c=[new n("а",-1,1),new n("ев",-1,1),new n("ов",-1,1),new n("е",-1,1),new n("ие",3,1),new n("ье",3,1),new n("и",-1,1),new n("еи",6,1),new n("ии",6,1),new n("ами",6,1),new n("ями",6,1),new n("иями",10,1),new n("й",-1,1),new n("ей",12,1),new n("ией",13,1),new n("ий",12,1),new n("ой",12,1),new n("ам",-1,1),new n("ем",-1,1),new n("ием",18,1),new n("ом",-1,1),new n("ям",-1,1),new n("иям",21,1),new n("о",-1,1),new n("у",-1,1),new n("ах",-1,1),new n("ях",-1,1),new n("иях",26,1),new n("ы",-1,1),new n("ь",-1,1),new n("ю",-1,1),new n("ию",30,1),new n("ью",30,1),new n("я",-1,1),new n("ия",33,1),new n("ья",33,1)],m=[new n("ост",-1,1),new n("ость",-1,1)],l=[new n("ейше",-1,1),new n("н",-1,2),new n("ейш",-1,1),new n("ь",-1,3)],f=[33,65,8,232],a=new r;function p(){for(;!a.in_grouping(f,1072,1103);){if(a.cursor>=a.limit)return!1;a.cursor++}return!0}function d(){for(;!a.out_grouping(f,1072,1103);){if(a.cursor>=a.limit)return!1;a.cursor++}return!0}function _(e,n){var r,t;if(a.ket=a.cursor,r=a.find_among_b(e,n)){switch(a.bra=a.cursor,r){case 1:if(t=a.limit-a.cursor,!a.eq_s_b(1,"а")&&(a.cursor=a.limit-t,!a.eq_s_b(1,"я")))return!1;case 2:a.slice_del()}return!0}return!1}function b(e,n){var r;return a.ket=a.cursor,!!(r=a.find_among_b(e,n))&&(a.bra=a.cursor,1==r&&a.slice_del(),!0)}function h(){return!!b(i,26)&&(_(u,8),!0)}function g(){var n;a.ket=a.cursor,(n=a.find_among_b(m,2))&&(a.bra=a.cursor,e<=a.cursor&&1==n&&a.slice_del())}this.setCurrent=function(e){a.setCurrent(e)},this.getCurrent=function(){return a.getCurrent()},this.stem=function(){return t=a.limit,e=t,p()&&(t=a.cursor,d()&&p()&&d()&&(e=a.cursor)),a.cursor=a.limit,!(a.cursor=i&&t[(e-=i)>>3]&1<<(7&e))return this.cursor++,!0}return!1},in_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e<=s&&e>=i&&t[(e-=i)>>3]&1<<(7&e))return this.cursor--,!0}return!1},out_grouping:function(t,i,s){if(this.cursors||e>3]&1<<(7&e)))return this.cursor++,!0}return!1},out_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e>s||e>3]&1<<(7&e)))return this.cursor--,!0}return!1},eq_s:function(t,i){if(this.limit-this.cursor>1),f=0,l=o0||e==s||c)break;c=!0}}for(;;){if(o>=(_=t[s]).s_size){if(this.cursor=n+_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n+_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},find_among_b:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit_backward,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o=0;_--){if(n-l==u){f=-1;break}if(f=r.charCodeAt(n-1-l)-m.s[_])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var m;if(o>=(m=t[s]).s_size){if(this.cursor=n-m.s_size,!m.method)return m.result;var b=m.method();if(this.cursor=n-m.s_size,b)return m.result}if((s=m.substring_i)<0)return 0}},replace_s:function(t,i,s){var e=s.length-(i-t),n=r.substring(0,t),u=r.substring(i);return r=n+s+u,this.limit+=e,this.cursor>=i?this.cursor+=e:this.cursor>t&&(this.cursor=t),e},slice_check:function(){if(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>r.length)throw"faulty slice operation"},slice_from:function(r){this.slice_check(),this.replace_s(this.bra,this.ket,r)},slice_del:function(){this.slice_from("")},insert:function(r,t,i){var s=this.replace_s(r,t,i);r<=this.bra&&(this.bra+=s),r<=this.ket&&(this.ket+=s)},slice_to:function(){return this.slice_check(),r.substring(this.bra,this.ket)},eq_v_b:function(r){return this.eq_s_b(r.length,r)}}}},r.trimmerSupport={generateTrimmer:function(r){var t=new RegExp("^[^"+r+"]+"),i=new RegExp("[^"+r+"]+$");return function(r){return"function"==typeof r.update?r.update(function(r){return r.replace(t,"").replace(i,"")}):r.replace(t,"").replace(i,"")}}}}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.sv.js b/docs/v3/v1/assets/javascripts/lunr/lunr.sv.js deleted file mode 100644 index 4bb0f9f92..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.sv.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,n,t;e.sv=function(){this.pipeline.reset(),this.pipeline.add(e.sv.trimmer,e.sv.stopWordFilter,e.sv.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sv.stemmer))},e.sv.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.sv.trimmer=e.trimmerSupport.generateTrimmer(e.sv.wordCharacters),e.Pipeline.registerFunction(e.sv.trimmer,"trimmer-sv"),e.sv.stemmer=(r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,t=new function(){var e,t,i=[new r("a",-1,1),new r("arna",0,1),new r("erna",0,1),new r("heterna",2,1),new r("orna",0,1),new r("ad",-1,1),new r("e",-1,1),new r("ade",6,1),new r("ande",6,1),new r("arne",6,1),new r("are",6,1),new r("aste",6,1),new r("en",-1,1),new r("anden",12,1),new r("aren",12,1),new r("heten",12,1),new r("ern",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",18,1),new r("or",-1,1),new r("s",-1,2),new r("as",21,1),new r("arnas",22,1),new r("ernas",22,1),new r("ornas",22,1),new r("es",21,1),new r("ades",26,1),new r("andes",26,1),new r("ens",21,1),new r("arens",29,1),new r("hetens",29,1),new r("erns",21,1),new r("at",-1,1),new r("andet",-1,1),new r("het",-1,1),new r("ast",-1,1)],s=[new r("dd",-1,-1),new r("gd",-1,-1),new r("nn",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1),new r("tt",-1,-1)],a=[new r("ig",-1,1),new r("lig",0,1),new r("els",-1,1),new r("fullt",-1,3),new r("löst",-1,2)],o=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,24,0,32],u=[119,127,149],m=new n;this.setCurrent=function(e){m.setCurrent(e)},this.getCurrent=function(){return m.getCurrent()},this.stem=function(){var r,n=m.cursor;return function(){var r,n=m.cursor+3;if(t=m.limit,0<=n||n<=m.limit){for(e=n;;){if(r=m.cursor,m.in_grouping(o,97,246)){m.cursor=r;break}if(m.cursor=r,m.cursor>=m.limit)return;m.cursor++}for(;!m.out_grouping(o,97,246);){if(m.cursor>=m.limit)return;m.cursor++}(t=m.cursor)=t&&(m.limit_backward=t,m.cursor=m.limit,m.ket=m.cursor,e=m.find_among_b(i,37),m.limit_backward=r,e))switch(m.bra=m.cursor,e){case 1:m.slice_del();break;case 2:m.in_grouping_b(u,98,121)&&m.slice_del()}}(),m.cursor=m.limit,r=m.limit_backward,m.cursor>=t&&(m.limit_backward=t,m.cursor=m.limit,m.find_among_b(s,7)&&(m.cursor=m.limit,m.ket=m.cursor,m.cursor>m.limit_backward&&(m.bra=--m.cursor,m.slice_del())),m.limit_backward=r),m.cursor=m.limit,function(){var e,r;if(m.cursor>=t){if(r=m.limit_backward,m.limit_backward=t,m.cursor=m.limit,m.ket=m.cursor,e=m.find_among_b(a,5))switch(m.bra=m.cursor,e){case 1:m.slice_del();break;case 2:m.slice_from("lös");break;case 3:m.slice_from("full")}m.limit_backward=r}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return t.setCurrent(e),t.stem(),t.getCurrent()}):(t.setCurrent(e),t.stem(),t.getCurrent())}),e.Pipeline.registerFunction(e.sv.stemmer,"stemmer-sv"),e.sv.stopWordFilter=e.generateStopWordFilter("alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över".split(" ")),e.Pipeline.registerFunction(e.sv.stopWordFilter,"stopWordFilter-sv")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/lunr.tr.js b/docs/v3/v1/assets/javascripts/lunr/lunr.tr.js deleted file mode 100644 index c42b349e8..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/lunr.tr.js +++ /dev/null @@ -1 +0,0 @@ -!function(r,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(r.lunr)}(this,function(){return function(r){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var i,e,n;r.tr=function(){this.pipeline.reset(),this.pipeline.add(r.tr.trimmer,r.tr.stopWordFilter,r.tr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(r.tr.stemmer))},r.tr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",r.tr.trimmer=r.trimmerSupport.generateTrimmer(r.tr.wordCharacters),r.Pipeline.registerFunction(r.tr.trimmer,"trimmer-tr"),r.tr.stemmer=(i=r.stemmerSupport.Among,e=r.stemmerSupport.SnowballProgram,n=new function(){var r,n=[new i("m",-1,-1),new i("n",-1,-1),new i("miz",-1,-1),new i("niz",-1,-1),new i("muz",-1,-1),new i("nuz",-1,-1),new i("müz",-1,-1),new i("nüz",-1,-1),new i("mız",-1,-1),new i("nız",-1,-1)],t=[new i("leri",-1,-1),new i("ları",-1,-1)],u=[new i("ni",-1,-1),new i("nu",-1,-1),new i("nü",-1,-1),new i("nı",-1,-1)],o=[new i("in",-1,-1),new i("un",-1,-1),new i("ün",-1,-1),new i("ın",-1,-1)],s=[new i("a",-1,-1),new i("e",-1,-1)],c=[new i("na",-1,-1),new i("ne",-1,-1)],l=[new i("da",-1,-1),new i("ta",-1,-1),new i("de",-1,-1),new i("te",-1,-1)],a=[new i("nda",-1,-1),new i("nde",-1,-1)],m=[new i("dan",-1,-1),new i("tan",-1,-1),new i("den",-1,-1),new i("ten",-1,-1)],d=[new i("ndan",-1,-1),new i("nden",-1,-1)],f=[new i("la",-1,-1),new i("le",-1,-1)],b=[new i("ca",-1,-1),new i("ce",-1,-1)],w=[new i("im",-1,-1),new i("um",-1,-1),new i("üm",-1,-1),new i("ım",-1,-1)],_=[new i("sin",-1,-1),new i("sun",-1,-1),new i("sün",-1,-1),new i("sın",-1,-1)],k=[new i("iz",-1,-1),new i("uz",-1,-1),new i("üz",-1,-1),new i("ız",-1,-1)],p=[new i("siniz",-1,-1),new i("sunuz",-1,-1),new i("sünüz",-1,-1),new i("sınız",-1,-1)],g=[new i("lar",-1,-1),new i("ler",-1,-1)],y=[new i("niz",-1,-1),new i("nuz",-1,-1),new i("nüz",-1,-1),new i("nız",-1,-1)],z=[new i("dir",-1,-1),new i("tir",-1,-1),new i("dur",-1,-1),new i("tur",-1,-1),new i("dür",-1,-1),new i("tür",-1,-1),new i("dır",-1,-1),new i("tır",-1,-1)],h=[new i("casına",-1,-1),new i("cesine",-1,-1)],v=[new i("di",-1,-1),new i("ti",-1,-1),new i("dik",-1,-1),new i("tik",-1,-1),new i("duk",-1,-1),new i("tuk",-1,-1),new i("dük",-1,-1),new i("tük",-1,-1),new i("dık",-1,-1),new i("tık",-1,-1),new i("dim",-1,-1),new i("tim",-1,-1),new i("dum",-1,-1),new i("tum",-1,-1),new i("düm",-1,-1),new i("tüm",-1,-1),new i("dım",-1,-1),new i("tım",-1,-1),new i("din",-1,-1),new i("tin",-1,-1),new i("dun",-1,-1),new i("tun",-1,-1),new i("dün",-1,-1),new i("tün",-1,-1),new i("dın",-1,-1),new i("tın",-1,-1),new i("du",-1,-1),new i("tu",-1,-1),new i("dü",-1,-1),new i("tü",-1,-1),new i("dı",-1,-1),new i("tı",-1,-1)],q=[new i("sa",-1,-1),new i("se",-1,-1),new i("sak",-1,-1),new i("sek",-1,-1),new i("sam",-1,-1),new i("sem",-1,-1),new i("san",-1,-1),new i("sen",-1,-1)],C=[new i("miş",-1,-1),new i("muş",-1,-1),new i("müş",-1,-1),new i("mış",-1,-1)],P=[new i("b",-1,1),new i("c",-1,2),new i("d",-1,3),new i("ğ",-1,4)],F=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,8,0,0,0,0,0,0,1],S=[1,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,1],W=[65],L=[65],x=[["a",[1,64,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],97,305],["e",[17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130],101,252],["ı",[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],97,305],["i",[17],101,105],["o",W,111,117],["ö",L,246,252],["u",W,111,117]],A=new e;function E(r,i,e){for(;;){var n=A.limit-A.cursor;if(A.in_grouping_b(r,i,e)){A.cursor=A.limit-n;break}if(A.cursor=A.limit-n,A.cursor<=A.limit_backward)return!1;A.cursor--}return!0}function j(){var r,i;r=A.limit-A.cursor,E(F,97,305);for(var e=0;eA.limit_backward&&(A.cursor--,e=A.limit-A.cursor,i()))?(A.cursor=A.limit-e,!0):(A.cursor=A.limit-n,r()?(A.cursor=A.limit-n,!1):(A.cursor=A.limit-n,!(A.cursor<=A.limit_backward)&&(A.cursor--,!!i()&&(A.cursor=A.limit-n,!0))))}function Z(r){return T(r,function(){return A.in_grouping_b(F,97,305)})}function B(){return Z(function(){return A.eq_s_b(1,"n")})}function D(){return Z(function(){return A.eq_s_b(1,"y")})}function G(){return A.find_among_b(n,10)&&T(function(){return A.in_grouping_b(S,105,305)},function(){return A.out_grouping_b(F,97,305)})}function H(){return j()&&A.in_grouping_b(S,105,305)&&Z(function(){return A.eq_s_b(1,"s")})}function I(){return A.find_among_b(t,2)}function J(){return j()&&A.find_among_b(o,4)&&B()}function K(){return j()&&A.find_among_b(l,4)}function M(){return j()&&A.find_among_b(a,2)}function N(){return j()&&A.find_among_b(w,4)&&D()}function O(){return j()&&A.find_among_b(_,4)}function Q(){return j()&&A.find_among_b(k,4)&&D()}function R(){return A.find_among_b(p,4)}function U(){return j()&&A.find_among_b(g,2)}function V(){return j()&&A.find_among_b(z,8)}function X(){return j()&&A.find_among_b(v,32)&&D()}function Y(){return A.find_among_b(q,8)&&D()}function $(){return j()&&A.find_among_b(C,4)&&D()}function rr(){var r=A.limit-A.cursor;return!($()||(A.cursor=A.limit-r,X()||(A.cursor=A.limit-r,Y()||(A.cursor=A.limit-r,A.eq_s_b(3,"ken")&&D()))))}function ir(){if(A.find_among_b(h,2)){var r=A.limit-A.cursor;if(R()||(A.cursor=A.limit-r,U()||(A.cursor=A.limit-r,N()||(A.cursor=A.limit-r,O()||(A.cursor=A.limit-r,Q()||(A.cursor=A.limit-r))))),$())return!1}return!0}function er(){if(!j()||!A.find_among_b(y,4))return!0;var r=A.limit-A.cursor;return!X()&&(A.cursor=A.limit-r,!Y())}function nr(){var i,e,n,t=A.limit-A.cursor;if(A.ket=A.cursor,r=!0,rr()&&(A.cursor=A.limit-t,ir()&&(A.cursor=A.limit-t,function(){if(U()){A.bra=A.cursor,A.slice_del();var i=A.limit-A.cursor;return A.ket=A.cursor,V()||(A.cursor=A.limit-i,X()||(A.cursor=A.limit-i,Y()||(A.cursor=A.limit-i,$()||(A.cursor=A.limit-i)))),r=!1,!1}return!0}()&&(A.cursor=A.limit-t,er()&&(A.cursor=A.limit-t,n=A.limit-A.cursor,!(R()||(A.cursor=A.limit-n,Q()||(A.cursor=A.limit-n,O()||(A.cursor=A.limit-n,N()))))||(A.bra=A.cursor,A.slice_del(),e=A.limit-A.cursor,A.ket=A.cursor,$()||(A.cursor=A.limit-e),0)))))){if(A.cursor=A.limit-t,!V())return;A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,i=A.limit-A.cursor,R()||(A.cursor=A.limit-i,U()||(A.cursor=A.limit-i,N()||(A.cursor=A.limit-i,O()||(A.cursor=A.limit-i,Q()||(A.cursor=A.limit-i))))),$()||(A.cursor=A.limit-i)}A.bra=A.cursor,A.slice_del()}function tr(){var r,i,e,n;if(A.ket=A.cursor,A.eq_s_b(2,"ki")){if(r=A.limit-A.cursor,K())return A.bra=A.cursor,A.slice_del(),i=A.limit-A.cursor,A.ket=A.cursor,U()?(A.bra=A.cursor,A.slice_del(),tr()):(A.cursor=A.limit-i,G()&&(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr()))),!0;if(A.cursor=A.limit-r,J()){if(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,e=A.limit-A.cursor,I())A.bra=A.cursor,A.slice_del();else{if(A.cursor=A.limit-e,A.ket=A.cursor,!G()&&(A.cursor=A.limit-e,!H()&&(A.cursor=A.limit-e,!tr())))return!0;A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr())}return!0}if(A.cursor=A.limit-r,M()){if(n=A.limit-A.cursor,I())A.bra=A.cursor,A.slice_del();else if(A.cursor=A.limit-n,H())A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr());else if(A.cursor=A.limit-n,!tr())return!1;return!0}}return!1}function ur(r){if(A.ket=A.cursor,!M()&&(A.cursor=A.limit-r,!j()||!A.find_among_b(c,2)))return!1;var i=A.limit-A.cursor;if(I())A.bra=A.cursor,A.slice_del();else if(A.cursor=A.limit-i,H())A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr());else if(A.cursor=A.limit-i,!tr())return!1;return!0}function or(r){if(A.ket=A.cursor,!(j()&&A.find_among_b(d,2)||(A.cursor=A.limit-r,j()&&A.find_among_b(u,4))))return!1;var i=A.limit-A.cursor;return!(!H()&&(A.cursor=A.limit-i,!I()))&&(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr()),!0)}function sr(){var r,i=A.limit-A.cursor;return A.ket=A.cursor,!!(J()||(A.cursor=A.limit-i,j()&&A.find_among_b(f,2)&&D()))&&(A.bra=A.cursor,A.slice_del(),r=A.limit-A.cursor,A.ket=A.cursor,!(!U()||(A.bra=A.cursor,A.slice_del(),!tr()))||(A.cursor=A.limit-r,A.ket=A.cursor,!(G()||(A.cursor=A.limit-r,H()||(A.cursor=A.limit-r,tr())))||(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr()),!0)))}function cr(){var r,i,e=A.limit-A.cursor;if(A.ket=A.cursor,!(K()||(A.cursor=A.limit-e,j()&&A.in_grouping_b(S,105,305)&&D()||(A.cursor=A.limit-e,j()&&A.find_among_b(s,2)&&D()))))return!1;if(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,r=A.limit-A.cursor,G())A.bra=A.cursor,A.slice_del(),i=A.limit-A.cursor,A.ket=A.cursor,U()||(A.cursor=A.limit-i);else if(A.cursor=A.limit-r,!U())return!0;return A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,tr(),!0}function lr(){var r,i,e=A.limit-A.cursor;if(A.ket=A.cursor,U())return A.bra=A.cursor,A.slice_del(),void tr();if(A.cursor=A.limit-e,A.ket=A.cursor,j()&&A.find_among_b(b,2)&&B())if(A.bra=A.cursor,A.slice_del(),r=A.limit-A.cursor,A.ket=A.cursor,I())A.bra=A.cursor,A.slice_del();else{if(A.cursor=A.limit-r,A.ket=A.cursor,!G()&&(A.cursor=A.limit-r,!H())){if(A.cursor=A.limit-r,A.ket=A.cursor,!U())return;if(A.bra=A.cursor,A.slice_del(),!tr())return}A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr())}else if(A.cursor=A.limit-e,!ur(e)&&(A.cursor=A.limit-e,!or(e))){if(A.cursor=A.limit-e,A.ket=A.cursor,j()&&A.find_among_b(m,4))return A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,i=A.limit-A.cursor,void(G()?(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr())):(A.cursor=A.limit-i,U()?(A.bra=A.cursor,A.slice_del(),tr()):(A.cursor=A.limit-i,tr())));if(A.cursor=A.limit-e,!sr()){if(A.cursor=A.limit-e,I())return A.bra=A.cursor,void A.slice_del();A.cursor=A.limit-e,tr()||(A.cursor=A.limit-e,cr()||(A.cursor=A.limit-e,A.ket=A.cursor,(G()||(A.cursor=A.limit-e,H()))&&(A.bra=A.cursor,A.slice_del(),A.ket=A.cursor,U()&&(A.bra=A.cursor,A.slice_del(),tr()))))}}}function ar(r,i,e){if(A.cursor=A.limit-r,function(){for(;;){var r=A.limit-A.cursor;if(A.in_grouping_b(F,97,305)){A.cursor=A.limit-r;break}if(A.cursor=A.limit-r,A.cursor<=A.limit_backward)return!1;A.cursor--}return!0}()){var n=A.limit-A.cursor;if(!A.eq_s_b(1,i)&&(A.cursor=A.limit-n,!A.eq_s_b(1,e)))return!0;A.cursor=A.limit-r;var t=A.cursor;return A.insert(A.cursor,A.cursor,e),A.cursor=t,!1}return!0}function mr(r,i,e){for(;!A.eq_s(i,e);){if(A.cursor>=A.limit)return!0;A.cursor++}return i!=A.limit||(A.cursor=r,!1)}function dr(){var r,i,e=A.cursor;return!(!mr(r=A.cursor,2,"ad")||(A.cursor=r,!mr(r,5,"soyad")))&&(A.limit_backward=e,A.cursor=A.limit,i=A.limit-A.cursor,(A.eq_s_b(1,"d")||(A.cursor=A.limit-i,A.eq_s_b(1,"g")))&&ar(i,"a","ı")&&ar(i,"e","i")&&ar(i,"o","u")&&ar(i,"ö","ü"),A.cursor=A.limit,function(){var r;if(A.ket=A.cursor,r=A.find_among_b(P,4))switch(A.bra=A.cursor,r){case 1:A.slice_from("p");break;case 2:A.slice_from("ç");break;case 3:A.slice_from("t");break;case 4:A.slice_from("k")}}(),!0)}this.setCurrent=function(r){A.setCurrent(r)},this.getCurrent=function(){return A.getCurrent()},this.stem=function(){return!!(function(){for(var r,i=A.cursor,e=2;;){for(r=A.cursor;!A.in_grouping(F,97,305);){if(A.cursor>=A.limit)return A.cursor=r,!(e>0||(A.cursor=i,0));A.cursor++}e--}}()&&(A.limit_backward=A.cursor,A.cursor=A.limit,nr(),A.cursor=A.limit,r&&(lr(),A.cursor=A.limit_backward,dr())))}},function(r){return"function"==typeof r.update?r.update(function(r){return n.setCurrent(r),n.stem(),n.getCurrent()}):(n.setCurrent(r),n.stem(),n.getCurrent())}),r.Pipeline.registerFunction(r.tr.stemmer,"stemmer-tr"),r.tr.stopWordFilter=r.generateStopWordFilter("acaba altmış altı ama ancak arada aslında ayrıca bana bazı belki ben benden beni benim beri beş bile bin bir biri birkaç birkez birçok birşey birşeyi biz bizden bize bizi bizim bu buna bunda bundan bunlar bunları bunların bunu bunun burada böyle böylece da daha dahi de defa değil diye diğer doksan dokuz dolayı dolayısıyla dört edecek eden ederek edilecek ediliyor edilmesi ediyor elli en etmesi etti ettiği ettiğini eğer gibi göre halen hangi hatta hem henüz hep hepsi her herhangi herkesin hiç hiçbir iki ile ilgili ise itibaren itibariyle için işte kadar karşın katrilyon kendi kendilerine kendini kendisi kendisine kendisini kez ki kim kimden kime kimi kimse kırk milyar milyon mu mü mı nasıl ne neden nedenle nerde nerede nereye niye niçin o olan olarak oldu olduklarını olduğu olduğunu olmadı olmadığı olmak olması olmayan olmaz olsa olsun olup olur olursa oluyor on ona ondan onlar onlardan onları onların onu onun otuz oysa pek rağmen sadece sanki sekiz seksen sen senden seni senin siz sizden sizi sizin tarafından trilyon tüm var vardı ve veya ya yani yapacak yapmak yaptı yaptıkları yaptığı yaptığını yapılan yapılması yapıyor yedi yerine yetmiş yine yirmi yoksa yüz zaten çok çünkü öyle üzere üç şey şeyden şeyi şeyler şu şuna şunda şundan şunları şunu şöyle".split(" ")),r.Pipeline.registerFunction(r.tr.stopWordFilter,"stopWordFilter-tr")}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/lunr/tinyseg.js b/docs/v3/v1/assets/javascripts/lunr/tinyseg.js deleted file mode 100644 index f7ec60326..000000000 --- a/docs/v3/v1/assets/javascripts/lunr/tinyseg.js +++ /dev/null @@ -1 +0,0 @@ -!function(_,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(_.lunr)}(this,function(){return function(_){function t(){var _={"[一二三四五六七八九十百千万億兆]":"M","[一-龠々〆ヵヶ]":"H","[ぁ-ん]":"I","[ァ-ヴーア-ン゙ー]":"K","[a-zA-Za-zA-Z]":"A","[0-90-9]":"N"};for(var t in this.chartype_=[],_){var H=new RegExp;H.compile(t),this.chartype_.push([H,_[t]])}return this.BIAS__=-332,this.BC1__={HH:6,II:2461,KH:406,OH:-1378},this.BC2__={AA:-3267,AI:2744,AN:-878,HH:-4070,HM:-1711,HN:4012,HO:3761,IA:1327,IH:-1184,II:-1332,IK:1721,IO:5492,KI:3831,KK:-8741,MH:-3132,MK:3334,OO:-2920},this.BC3__={HH:996,HI:626,HK:-721,HN:-1307,HO:-836,IH:-301,KK:2762,MK:1079,MM:4034,OA:-1652,OH:266},this.BP1__={BB:295,OB:304,OO:-125,UB:352},this.BP2__={BO:60,OO:-1762},this.BQ1__={BHH:1150,BHM:1521,BII:-1158,BIM:886,BMH:1208,BNH:449,BOH:-91,BOO:-2597,OHI:451,OIH:-296,OKA:1851,OKH:-1020,OKK:904,OOO:2965},this.BQ2__={BHH:118,BHI:-1159,BHM:466,BIH:-919,BKK:-1720,BKO:864,OHH:-1139,OHM:-181,OIH:153,UHI:-1146},this.BQ3__={BHH:-792,BHI:2664,BII:-299,BKI:419,BMH:937,BMM:8335,BNN:998,BOH:775,OHH:2174,OHM:439,OII:280,OKH:1798,OKI:-793,OKO:-2242,OMH:-2402,OOO:11699},this.BQ4__={BHH:-3895,BIH:3761,BII:-4654,BIK:1348,BKK:-1806,BMI:-3385,BOO:-12396,OAH:926,OHH:266,OHK:-2036,ONN:-973},this.BW1__={",と":660,",同":727,"B1あ":1404,"B1同":542,"、と":660,"、同":727,"」と":1682,"あっ":1505,"いう":1743,"いっ":-2055,"いる":672,"うし":-4817,"うん":665,"から":3472,"がら":600,"こう":-790,"こと":2083,"こん":-1262,"さら":-4143,"さん":4573,"した":2641,"して":1104,"すで":-3399,"そこ":1977,"それ":-871,"たち":1122,"ため":601,"った":3463,"つい":-802,"てい":805,"てき":1249,"でき":1127,"です":3445,"では":844,"とい":-4915,"とみ":1922,"どこ":3887,"ない":5713,"なっ":3015,"など":7379,"なん":-1113,"にし":2468,"には":1498,"にも":1671,"に対":-912,"の一":-501,"の中":741,"ませ":2448,"まで":1711,"まま":2600,"まる":-2155,"やむ":-1947,"よっ":-2565,"れた":2369,"れで":-913,"をし":1860,"を見":731,"亡く":-1886,"京都":2558,"取り":-2784,"大き":-2604,"大阪":1497,"平方":-2314,"引き":-1336,"日本":-195,"本当":-2423,"毎日":-2113,"目指":-724,"B1あ":1404,"B1同":542,"」と":1682},this.BW2__={"..":-11822,11:-669,"――":-5730,"−−":-13175,"いう":-1609,"うか":2490,"かし":-1350,"かも":-602,"から":-7194,"かれ":4612,"がい":853,"がら":-3198,"きた":1941,"くな":-1597,"こと":-8392,"この":-4193,"させ":4533,"され":13168,"さん":-3977,"しい":-1819,"しか":-545,"した":5078,"して":972,"しな":939,"その":-3744,"たい":-1253,"たた":-662,"ただ":-3857,"たち":-786,"たと":1224,"たは":-939,"った":4589,"って":1647,"っと":-2094,"てい":6144,"てき":3640,"てく":2551,"ては":-3110,"ても":-3065,"でい":2666,"でき":-1528,"でし":-3828,"です":-4761,"でも":-4203,"とい":1890,"とこ":-1746,"とと":-2279,"との":720,"とみ":5168,"とも":-3941,"ない":-2488,"なが":-1313,"など":-6509,"なの":2614,"なん":3099,"にお":-1615,"にし":2748,"にな":2454,"によ":-7236,"に対":-14943,"に従":-4688,"に関":-11388,"のか":2093,"ので":-7059,"のに":-6041,"のの":-6125,"はい":1073,"はが":-1033,"はず":-2532,"ばれ":1813,"まし":-1316,"まで":-6621,"まれ":5409,"めて":-3153,"もい":2230,"もの":-10713,"らか":-944,"らし":-1611,"らに":-1897,"りし":651,"りま":1620,"れた":4270,"れて":849,"れば":4114,"ろう":6067,"われ":7901,"を通":-11877,"んだ":728,"んな":-4115,"一人":602,"一方":-1375,"一日":970,"一部":-1051,"上が":-4479,"会社":-1116,"出て":2163,"分の":-7758,"同党":970,"同日":-913,"大阪":-2471,"委員":-1250,"少な":-1050,"年度":-8669,"年間":-1626,"府県":-2363,"手権":-1982,"新聞":-4066,"日新":-722,"日本":-7068,"日米":3372,"曜日":-601,"朝鮮":-2355,"本人":-2697,"東京":-1543,"然と":-1384,"社会":-1276,"立て":-990,"第に":-1612,"米国":-4268,"11":-669},this.BW3__={"あた":-2194,"あり":719,"ある":3846,"い.":-1185,"い。":-1185,"いい":5308,"いえ":2079,"いく":3029,"いた":2056,"いっ":1883,"いる":5600,"いわ":1527,"うち":1117,"うと":4798,"えと":1454,"か.":2857,"か。":2857,"かけ":-743,"かっ":-4098,"かに":-669,"から":6520,"かり":-2670,"が,":1816,"が、":1816,"がき":-4855,"がけ":-1127,"がっ":-913,"がら":-4977,"がり":-2064,"きた":1645,"けど":1374,"こと":7397,"この":1542,"ころ":-2757,"さい":-714,"さを":976,"し,":1557,"し、":1557,"しい":-3714,"した":3562,"して":1449,"しな":2608,"しま":1200,"す.":-1310,"す。":-1310,"する":6521,"ず,":3426,"ず、":3426,"ずに":841,"そう":428,"た.":8875,"た。":8875,"たい":-594,"たの":812,"たり":-1183,"たる":-853,"だ.":4098,"だ。":4098,"だっ":1004,"った":-4748,"って":300,"てい":6240,"てお":855,"ても":302,"です":1437,"でに":-1482,"では":2295,"とう":-1387,"とし":2266,"との":541,"とも":-3543,"どう":4664,"ない":1796,"なく":-903,"など":2135,"に,":-1021,"に、":-1021,"にし":1771,"にな":1906,"には":2644,"の,":-724,"の、":-724,"の子":-1e3,"は,":1337,"は、":1337,"べき":2181,"まし":1113,"ます":6943,"まっ":-1549,"まで":6154,"まれ":-793,"らし":1479,"られ":6820,"るる":3818,"れ,":854,"れ、":854,"れた":1850,"れて":1375,"れば":-3246,"れる":1091,"われ":-605,"んだ":606,"んで":798,"カ月":990,"会議":860,"入り":1232,"大会":2217,"始め":1681,"市":965,"新聞":-5055,"日,":974,"日、":974,"社会":2024,"カ月":990},this.TC1__={AAA:1093,HHH:1029,HHM:580,HII:998,HOH:-390,HOM:-331,IHI:1169,IOH:-142,IOI:-1015,IOM:467,MMH:187,OOI:-1832},this.TC2__={HHO:2088,HII:-1023,HMM:-1154,IHI:-1965,KKH:703,OII:-2649},this.TC3__={AAA:-294,HHH:346,HHI:-341,HII:-1088,HIK:731,HOH:-1486,IHH:128,IHI:-3041,IHO:-1935,IIH:-825,IIM:-1035,IOI:-542,KHH:-1216,KKA:491,KKH:-1217,KOK:-1009,MHH:-2694,MHM:-457,MHO:123,MMH:-471,NNH:-1689,NNO:662,OHO:-3393},this.TC4__={HHH:-203,HHI:1344,HHK:365,HHM:-122,HHN:182,HHO:669,HIH:804,HII:679,HOH:446,IHH:695,IHO:-2324,IIH:321,III:1497,IIO:656,IOO:54,KAK:4845,KKA:3386,KKK:3065,MHH:-405,MHI:201,MMH:-241,MMM:661,MOM:841},this.TQ1__={BHHH:-227,BHHI:316,BHIH:-132,BIHH:60,BIII:1595,BNHH:-744,BOHH:225,BOOO:-908,OAKK:482,OHHH:281,OHIH:249,OIHI:200,OIIH:-68},this.TQ2__={BIHH:-1401,BIII:-1033,BKAK:-543,BOOO:-5591},this.TQ3__={BHHH:478,BHHM:-1073,BHIH:222,BHII:-504,BIIH:-116,BIII:-105,BMHI:-863,BMHM:-464,BOMH:620,OHHH:346,OHHI:1729,OHII:997,OHMH:481,OIHH:623,OIIH:1344,OKAK:2792,OKHH:587,OKKA:679,OOHH:110,OOII:-685},this.TQ4__={BHHH:-721,BHHM:-3604,BHII:-966,BIIH:-607,BIII:-2181,OAAA:-2763,OAKK:180,OHHH:-294,OHHI:2446,OHHO:480,OHIH:-1573,OIHH:1935,OIHI:-493,OIIH:626,OIII:-4007,OKAK:-8156},this.TW1__={"につい":-4681,"東京都":2026},this.TW2__={"ある程":-2049,"いった":-1256,"ころが":-2434,"しょう":3873,"その後":-4430,"だって":-1049,"ていた":1833,"として":-4657,"ともに":-4517,"もので":1882,"一気に":-792,"初めて":-1512,"同時に":-8097,"大きな":-1255,"対して":-2721,"社会党":-3216},this.TW3__={"いただ":-1734,"してい":1314,"として":-4314,"につい":-5483,"にとっ":-5989,"に当た":-6247,"ので,":-727,"ので、":-727,"のもの":-600,"れから":-3752,"十二月":-2287},this.TW4__={"いう.":8576,"いう。":8576,"からな":-2348,"してい":2958,"たが,":1516,"たが、":1516,"ている":1538,"という":1349,"ました":5543,"ません":1097,"ようと":-4258,"よると":5865},this.UC1__={A:484,K:93,M:645,O:-505},this.UC2__={A:819,H:1059,I:409,M:3987,N:5775,O:646},this.UC3__={A:-1370,I:2311},this.UC4__={A:-2643,H:1809,I:-1032,K:-3450,M:3565,N:3876,O:6646},this.UC5__={H:313,I:-1238,K:-799,M:539,O:-831},this.UC6__={H:-506,I:-253,K:87,M:247,O:-387},this.UP1__={O:-214},this.UP2__={B:69,O:935},this.UP3__={B:189},this.UQ1__={BH:21,BI:-12,BK:-99,BN:142,BO:-56,OH:-95,OI:477,OK:410,OO:-2422},this.UQ2__={BH:216,BI:113,OK:1759},this.UQ3__={BA:-479,BH:42,BI:1913,BK:-7198,BM:3160,BN:6427,BO:14761,OI:-827,ON:-3212},this.UW1__={",":156,"、":156,"「":-463,"あ":-941,"う":-127,"が":-553,"き":121,"こ":505,"で":-201,"と":-547,"ど":-123,"に":-789,"の":-185,"は":-847,"も":-466,"や":-470,"よ":182,"ら":-292,"り":208,"れ":169,"を":-446,"ん":-137,"・":-135,"主":-402,"京":-268,"区":-912,"午":871,"国":-460,"大":561,"委":729,"市":-411,"日":-141,"理":361,"生":-408,"県":-386,"都":-718,"「":-463,"・":-135},this.UW2__={",":-829,"、":-829,"〇":892,"「":-645,"」":3145,"あ":-538,"い":505,"う":134,"お":-502,"か":1454,"が":-856,"く":-412,"こ":1141,"さ":878,"ざ":540,"し":1529,"す":-675,"せ":300,"そ":-1011,"た":188,"だ":1837,"つ":-949,"て":-291,"で":-268,"と":-981,"ど":1273,"な":1063,"に":-1764,"の":130,"は":-409,"ひ":-1273,"べ":1261,"ま":600,"も":-1263,"や":-402,"よ":1639,"り":-579,"る":-694,"れ":571,"を":-2516,"ん":2095,"ア":-587,"カ":306,"キ":568,"ッ":831,"三":-758,"不":-2150,"世":-302,"中":-968,"主":-861,"事":492,"人":-123,"会":978,"保":362,"入":548,"初":-3025,"副":-1566,"北":-3414,"区":-422,"大":-1769,"天":-865,"太":-483,"子":-1519,"学":760,"実":1023,"小":-2009,"市":-813,"年":-1060,"強":1067,"手":-1519,"揺":-1033,"政":1522,"文":-1355,"新":-1682,"日":-1815,"明":-1462,"最":-630,"朝":-1843,"本":-1650,"東":-931,"果":-665,"次":-2378,"民":-180,"気":-1740,"理":752,"発":529,"目":-1584,"相":-242,"県":-1165,"立":-763,"第":810,"米":509,"自":-1353,"行":838,"西":-744,"見":-3874,"調":1010,"議":1198,"込":3041,"開":1758,"間":-1257,"「":-645,"」":3145,"ッ":831,"ア":-587,"カ":306,"キ":568},this.UW3__={",":4889,1:-800,"−":-1723,"、":4889,"々":-2311,"〇":5827,"」":2670,"〓":-3573,"あ":-2696,"い":1006,"う":2342,"え":1983,"お":-4864,"か":-1163,"が":3271,"く":1004,"け":388,"げ":401,"こ":-3552,"ご":-3116,"さ":-1058,"し":-395,"す":584,"せ":3685,"そ":-5228,"た":842,"ち":-521,"っ":-1444,"つ":-1081,"て":6167,"で":2318,"と":1691,"ど":-899,"な":-2788,"に":2745,"の":4056,"は":4555,"ひ":-2171,"ふ":-1798,"へ":1199,"ほ":-5516,"ま":-4384,"み":-120,"め":1205,"も":2323,"や":-788,"よ":-202,"ら":727,"り":649,"る":5905,"れ":2773,"わ":-1207,"を":6620,"ん":-518,"ア":551,"グ":1319,"ス":874,"ッ":-1350,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278,"・":-3794,"一":-1619,"下":-1759,"世":-2087,"両":3815,"中":653,"主":-758,"予":-1193,"二":974,"人":2742,"今":792,"他":1889,"以":-1368,"低":811,"何":4265,"作":-361,"保":-2439,"元":4858,"党":3593,"全":1574,"公":-3030,"六":755,"共":-1880,"円":5807,"再":3095,"分":457,"初":2475,"別":1129,"前":2286,"副":4437,"力":365,"動":-949,"務":-1872,"化":1327,"北":-1038,"区":4646,"千":-2309,"午":-783,"協":-1006,"口":483,"右":1233,"各":3588,"合":-241,"同":3906,"和":-837,"員":4513,"国":642,"型":1389,"場":1219,"外":-241,"妻":2016,"学":-1356,"安":-423,"実":-1008,"家":1078,"小":-513,"少":-3102,"州":1155,"市":3197,"平":-1804,"年":2416,"広":-1030,"府":1605,"度":1452,"建":-2352,"当":-3885,"得":1905,"思":-1291,"性":1822,"戸":-488,"指":-3973,"政":-2013,"教":-1479,"数":3222,"文":-1489,"新":1764,"日":2099,"旧":5792,"昨":-661,"時":-1248,"曜":-951,"最":-937,"月":4125,"期":360,"李":3094,"村":364,"東":-805,"核":5156,"森":2438,"業":484,"氏":2613,"民":-1694,"決":-1073,"法":1868,"海":-495,"無":979,"物":461,"特":-3850,"生":-273,"用":914,"町":1215,"的":7313,"直":-1835,"省":792,"県":6293,"知":-1528,"私":4231,"税":401,"立":-960,"第":1201,"米":7767,"系":3066,"約":3663,"級":1384,"統":-4229,"総":1163,"線":1255,"者":6457,"能":725,"自":-2869,"英":785,"見":1044,"調":-562,"財":-733,"費":1777,"車":1835,"軍":1375,"込":-1504,"通":-1136,"選":-681,"郎":1026,"郡":4404,"部":1200,"金":2163,"長":421,"開":-1432,"間":1302,"関":-1282,"雨":2009,"電":-1045,"非":2066,"駅":1620,"1":-800,"」":2670,"・":-3794,"ッ":-1350,"ア":551,"グ":1319,"ス":874,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278},this.UW4__={",":3930,".":3508,"―":-4841,"、":3930,"。":3508,"〇":4999,"「":1895,"」":3798,"〓":-5156,"あ":4752,"い":-3435,"う":-640,"え":-2514,"お":2405,"か":530,"が":6006,"き":-4482,"ぎ":-3821,"く":-3788,"け":-4376,"げ":-4734,"こ":2255,"ご":1979,"さ":2864,"し":-843,"じ":-2506,"す":-731,"ず":1251,"せ":181,"そ":4091,"た":5034,"だ":5408,"ち":-3654,"っ":-5882,"つ":-1659,"て":3994,"で":7410,"と":4547,"な":5433,"に":6499,"ぬ":1853,"ね":1413,"の":7396,"は":8578,"ば":1940,"ひ":4249,"び":-4134,"ふ":1345,"へ":6665,"べ":-744,"ほ":1464,"ま":1051,"み":-2082,"む":-882,"め":-5046,"も":4169,"ゃ":-2666,"や":2795,"ょ":-1544,"よ":3351,"ら":-2922,"り":-9726,"る":-14896,"れ":-2613,"ろ":-4570,"わ":-1783,"を":13150,"ん":-2352,"カ":2145,"コ":1789,"セ":1287,"ッ":-724,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637,"・":-4371,"ー":-11870,"一":-2069,"中":2210,"予":782,"事":-190,"井":-1768,"人":1036,"以":544,"会":950,"体":-1286,"作":530,"側":4292,"先":601,"党":-2006,"共":-1212,"内":584,"円":788,"初":1347,"前":1623,"副":3879,"力":-302,"動":-740,"務":-2715,"化":776,"区":4517,"協":1013,"参":1555,"合":-1834,"和":-681,"員":-910,"器":-851,"回":1500,"国":-619,"園":-1200,"地":866,"場":-1410,"塁":-2094,"士":-1413,"多":1067,"大":571,"子":-4802,"学":-1397,"定":-1057,"寺":-809,"小":1910,"屋":-1328,"山":-1500,"島":-2056,"川":-2667,"市":2771,"年":374,"庁":-4556,"後":456,"性":553,"感":916,"所":-1566,"支":856,"改":787,"政":2182,"教":704,"文":522,"方":-856,"日":1798,"時":1829,"最":845,"月":-9066,"木":-485,"来":-442,"校":-360,"業":-1043,"氏":5388,"民":-2716,"気":-910,"沢":-939,"済":-543,"物":-735,"率":672,"球":-1267,"生":-1286,"産":-1101,"田":-2900,"町":1826,"的":2586,"目":922,"省":-3485,"県":2997,"空":-867,"立":-2112,"第":788,"米":2937,"系":786,"約":2171,"経":1146,"統":-1169,"総":940,"線":-994,"署":749,"者":2145,"能":-730,"般":-852,"行":-792,"規":792,"警":-1184,"議":-244,"谷":-1e3,"賞":730,"車":-1481,"軍":1158,"輪":-1433,"込":-3370,"近":929,"道":-1291,"選":2596,"郎":-4866,"都":1192,"野":-1100,"銀":-2213,"長":357,"間":-2344,"院":-2297,"際":-2604,"電":-878,"領":-1659,"題":-792,"館":-1984,"首":1749,"高":2120,"「":1895,"」":3798,"・":-4371,"ッ":-724,"ー":-11870,"カ":2145,"コ":1789,"セ":1287,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637},this.UW5__={",":465,".":-299,1:-514,E2:-32768,"]":-2762,"、":465,"。":-299,"「":363,"あ":1655,"い":331,"う":-503,"え":1199,"お":527,"か":647,"が":-421,"き":1624,"ぎ":1971,"く":312,"げ":-983,"さ":-1537,"し":-1371,"す":-852,"だ":-1186,"ち":1093,"っ":52,"つ":921,"て":-18,"で":-850,"と":-127,"ど":1682,"な":-787,"に":-1224,"の":-635,"は":-578,"べ":1001,"み":502,"め":865,"ゃ":3350,"ょ":854,"り":-208,"る":429,"れ":504,"わ":419,"を":-1264,"ん":327,"イ":241,"ル":451,"ン":-343,"中":-871,"京":722,"会":-1153,"党":-654,"務":3519,"区":-901,"告":848,"員":2104,"大":-1296,"学":-548,"定":1785,"嵐":-1304,"市":-2991,"席":921,"年":1763,"思":872,"所":-814,"挙":1618,"新":-1682,"日":218,"月":-4353,"査":932,"格":1356,"機":-1508,"氏":-1347,"田":240,"町":-3912,"的":-3149,"相":1319,"省":-1052,"県":-4003,"研":-997,"社":-278,"空":-813,"統":1955,"者":-2233,"表":663,"語":-1073,"議":1219,"選":-1018,"郎":-368,"長":786,"間":1191,"題":2368,"館":-689,"1":-514,"E2":-32768,"「":363,"イ":241,"ル":451,"ン":-343},this.UW6__={",":227,".":808,1:-270,E1:306,"、":227,"。":808,"あ":-307,"う":189,"か":241,"が":-73,"く":-121,"こ":-200,"じ":1782,"す":383,"た":-428,"っ":573,"て":-1014,"で":101,"と":-105,"な":-253,"に":-149,"の":-417,"は":-236,"も":-206,"り":187,"る":-135,"を":195,"ル":-673,"ン":-496,"一":-277,"中":201,"件":-800,"会":624,"前":302,"区":1792,"員":-1212,"委":798,"学":-960,"市":887,"広":-695,"後":535,"業":-697,"相":753,"社":-507,"福":974,"空":-822,"者":1811,"連":463,"郎":1082,"1":-270,"E1":306,"ル":-673,"ン":-496},this}t.prototype.ctype_=function(_){for(var t in this.chartype_)if(_.match(this.chartype_[t][0]))return this.chartype_[t][1];return"O"},t.prototype.ts_=function(_){return _||0},t.prototype.segment=function(_){if(null==_||null==_||""==_)return[];var t=[],H=["B3","B2","B1"],s=["O","O","O"],h=_.split("");for(K=0;K0&&(t.push(i),i="",N="B"),I=O,O=B,B=N,i+=H[K]}return t.push(i),t},_.TinySegmenter=t}}); \ No newline at end of file diff --git a/docs/v3/v1/assets/javascripts/modernizr.1aa3b519.js b/docs/v3/v1/assets/javascripts/modernizr.1aa3b519.js deleted file mode 100644 index 14e111fc3..000000000 --- a/docs/v3/v1/assets/javascripts/modernizr.1aa3b519.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,t){for(var n in t)e[n]=t[n]}(window,function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=4)}({4:function(e,t,n){"use strict";n(5)},5:function(e,t){!function(t){!function(e,t,n){function r(e,t){return typeof e===t}function o(e){var t=_.className,n=C._config.classPrefix||"";if(T&&(t=t.baseVal),C._config.enableJSClass){var r=new RegExp("(^|\\s)"+n+"no-js(\\s|$)");t=t.replace(r,"$1"+n+"js$2")}C._config.enableClasses&&(t+=" "+n+e.join(" "+n),T?_.className.baseVal=t:_.className=t)}function i(e,t){if("object"==typeof e)for(var n in e)b(e,n)&&i(n,e[n]);else{e=e.toLowerCase();var r=e.split("."),s=C[r[0]];if(2==r.length&&(s=s[r[1]]),void 0!==s)return C;t="function"==typeof t?t():t,1==r.length?C[r[0]]=t:(!C[r[0]]||C[r[0]]instanceof Boolean||(C[r[0]]=new Boolean(C[r[0]])),C[r[0]][r[1]]=t),o([(t&&0!=t?"":"no-")+r.join("-")]),C._trigger(e,t)}return C}function s(){return"function"!=typeof t.createElement?t.createElement(arguments[0]):T?t.createElementNS.call(t,"http://www.w3.org/2000/svg",arguments[0]):t.createElement.apply(t,arguments)}function a(){var e=t.body;return e||(e=s(T?"svg":"body"),e.fake=!0),e}function u(e,n,r,o){var i,u,l,f,c="modernizr",d=s("div"),p=a();if(parseInt(r,10))for(;r--;)l=s("div"),l.id=o?o[r]:c+(r+1),d.appendChild(l);return i=s("style"),i.type="text/css",i.id="s"+c,(p.fake?p:d).appendChild(i),p.appendChild(d),i.styleSheet?i.styleSheet.cssText=e:i.appendChild(t.createTextNode(e)),d.id=c,p.fake&&(p.style.background="",p.style.overflow="hidden",f=_.style.overflow,_.style.overflow="hidden",_.appendChild(p)),u=n(d,e),p.fake?(p.parentNode.removeChild(p),_.style.overflow=f,_.offsetHeight):d.parentNode.removeChild(d),!!u}function l(e,t){return!!~(""+e).indexOf(t)}function f(e){return e.replace(/([A-Z])/g,function(e,t){return"-"+t.toLowerCase()}).replace(/^ms-/,"-ms-")}function c(t,n,r){var o;if("getComputedStyle"in e){o=getComputedStyle.call(e,t,n);var i=e.console;if(null!==o)r&&(o=o.getPropertyValue(r));else if(i){var s=i.error?"error":"log";i[s].call(i,"getComputedStyle returning null, its possible modernizr test results are inaccurate")}}else o=!n&&t.currentStyle&&t.currentStyle[r];return o}function d(t,r){var o=t.length;if("CSS"in e&&"supports"in e.CSS){for(;o--;)if(e.CSS.supports(f(t[o]),r))return!0;return!1}if("CSSSupportsRule"in e){for(var i=[];o--;)i.push("("+f(t[o])+":"+r+")");return i=i.join(" or "),u("@supports ("+i+") { #modernizr { position: absolute; } }",function(e){return"absolute"==c(e,null,"position")})}return n}function p(e){return e.replace(/([a-z])-([a-z])/g,function(e,t,n){return t+n.toUpperCase()}).replace(/^-/,"")}function h(e,t,o,i){function a(){f&&(delete j.style,delete j.modElem)}if(i=!r(i,"undefined")&&i,!r(o,"undefined")){var u=d(e,o);if(!r(u,"undefined"))return u}for(var f,c,h,m,v,g=["modernizr","tspan","samp"];!j.style&&g.length;)f=!0,j.modElem=s(g.shift()),j.style=j.modElem.style;for(h=e.length,c=0;h>c;c++)if(m=e[c],v=j.style[m],l(m,"-")&&(m=p(m)),j.style[m]!==n){if(i||r(o,"undefined"))return a(),"pfx"!=t||m;try{j.style[m]=o}catch(e){}if(j.style[m]!=v)return a(),"pfx"!=t||m}return a(),!1}function m(e,t){return function(){return e.apply(t,arguments)}}function v(e,t,n){var o;for(var i in e)if(e[i]in t)return!1===n?e[i]:(o=t[e[i]],r(o,"function")?m(o,n||t):o);return!1}function g(e,t,n,o,i){var s=e.charAt(0).toUpperCase()+e.slice(1),a=(e+" "+k.join(s+" ")+s).split(" ");return r(t,"string")||r(t,"undefined")?h(a,t,o,i):(a=(e+" "+A.join(s+" ")+s).split(" "),v(a,t,n))}function y(e,t,r){return g(e,n,n,t,r)}var w=[],S={_version:"3.5.0",_config:{classPrefix:"",enableClasses:!0,enableJSClass:!0,usePrefixes:!0},_q:[],on:function(e,t){var n=this;setTimeout(function(){t(n[e])},0)},addTest:function(e,t,n){w.push({name:e,fn:t,options:n})},addAsyncTest:function(e){w.push({name:null,fn:e})}},C=function(){};C.prototype=S,C=new C;var b,x=[],_=t.documentElement,T="svg"===_.nodeName.toLowerCase();!function(){var e={}.hasOwnProperty;b=r(e,"undefined")||r(e.call,"undefined")?function(e,t){return t in e&&r(e.constructor.prototype[t],"undefined")}:function(t,n){return e.call(t,n)}}(),S._l={},S.on=function(e,t){this._l[e]||(this._l[e]=[]),this._l[e].push(t),C.hasOwnProperty(e)&&setTimeout(function(){C._trigger(e,C[e])},0)},S._trigger=function(e,t){if(this._l[e]){var n=this._l[e];setTimeout(function(){var e;for(e=0;e .md-nav__link { - color: inherit; } - -button[data-md-color-primary="pink"] { - background-color: #e91e63; } - -[data-md-color-primary="pink"] .md-typeset a { - color: #e91e63; } - -[data-md-color-primary="pink"] .md-header { - background-color: #e91e63; } - -[data-md-color-primary="pink"] .md-hero { - background-color: #e91e63; } - -[data-md-color-primary="pink"] .md-nav__link:active, -[data-md-color-primary="pink"] .md-nav__link--active { - color: #e91e63; } - -[data-md-color-primary="pink"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="purple"] { - background-color: #ab47bc; } - -[data-md-color-primary="purple"] .md-typeset a { - color: #ab47bc; } - -[data-md-color-primary="purple"] .md-header { - background-color: #ab47bc; } - -[data-md-color-primary="purple"] .md-hero { - background-color: #ab47bc; } - -[data-md-color-primary="purple"] .md-nav__link:active, -[data-md-color-primary="purple"] .md-nav__link--active { - color: #ab47bc; } - -[data-md-color-primary="purple"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="deep-purple"] { - background-color: #7e57c2; } - -[data-md-color-primary="deep-purple"] .md-typeset a { - color: #7e57c2; } - -[data-md-color-primary="deep-purple"] .md-header { - background-color: #7e57c2; } - -[data-md-color-primary="deep-purple"] .md-hero { - background-color: #7e57c2; } - -[data-md-color-primary="deep-purple"] .md-nav__link:active, -[data-md-color-primary="deep-purple"] .md-nav__link--active { - color: #7e57c2; } - -[data-md-color-primary="deep-purple"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="indigo"] { - background-color: #3f51b5; } - -[data-md-color-primary="indigo"] .md-typeset a { - color: #3f51b5; } - -[data-md-color-primary="indigo"] .md-header { - background-color: #3f51b5; } - -[data-md-color-primary="indigo"] .md-hero { - background-color: #3f51b5; } - -[data-md-color-primary="indigo"] .md-nav__link:active, -[data-md-color-primary="indigo"] .md-nav__link--active { - color: #3f51b5; } - -[data-md-color-primary="indigo"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="blue"] { - background-color: #2196f3; } - -[data-md-color-primary="blue"] .md-typeset a { - color: #2196f3; } - -[data-md-color-primary="blue"] .md-header { - background-color: #2196f3; } - -[data-md-color-primary="blue"] .md-hero { - background-color: #2196f3; } - -[data-md-color-primary="blue"] .md-nav__link:active, -[data-md-color-primary="blue"] .md-nav__link--active { - color: #2196f3; } - -[data-md-color-primary="blue"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="light-blue"] { - background-color: #03a9f4; } - -[data-md-color-primary="light-blue"] .md-typeset a { - color: #03a9f4; } - -[data-md-color-primary="light-blue"] .md-header { - background-color: #03a9f4; } - -[data-md-color-primary="light-blue"] .md-hero { - background-color: #03a9f4; } - -[data-md-color-primary="light-blue"] .md-nav__link:active, -[data-md-color-primary="light-blue"] .md-nav__link--active { - color: #03a9f4; } - -[data-md-color-primary="light-blue"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="cyan"] { - background-color: #00bcd4; } - -[data-md-color-primary="cyan"] .md-typeset a { - color: #00bcd4; } - -[data-md-color-primary="cyan"] .md-header { - background-color: #00bcd4; } - -[data-md-color-primary="cyan"] .md-hero { - background-color: #00bcd4; } - -[data-md-color-primary="cyan"] .md-nav__link:active, -[data-md-color-primary="cyan"] .md-nav__link--active { - color: #00bcd4; } - -[data-md-color-primary="cyan"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="teal"] { - background-color: #009688; } - -[data-md-color-primary="teal"] .md-typeset a { - color: #009688; } - -[data-md-color-primary="teal"] .md-header { - background-color: #009688; } - -[data-md-color-primary="teal"] .md-hero { - background-color: #009688; } - -[data-md-color-primary="teal"] .md-nav__link:active, -[data-md-color-primary="teal"] .md-nav__link--active { - color: #009688; } - -[data-md-color-primary="teal"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="green"] { - background-color: #4caf50; } - -[data-md-color-primary="green"] .md-typeset a { - color: #4caf50; } - -[data-md-color-primary="green"] .md-header { - background-color: #4caf50; } - -[data-md-color-primary="green"] .md-hero { - background-color: #4caf50; } - -[data-md-color-primary="green"] .md-nav__link:active, -[data-md-color-primary="green"] .md-nav__link--active { - color: #4caf50; } - -[data-md-color-primary="green"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="light-green"] { - background-color: #7cb342; } - -[data-md-color-primary="light-green"] .md-typeset a { - color: #7cb342; } - -[data-md-color-primary="light-green"] .md-header { - background-color: #7cb342; } - -[data-md-color-primary="light-green"] .md-hero { - background-color: #7cb342; } - -[data-md-color-primary="light-green"] .md-nav__link:active, -[data-md-color-primary="light-green"] .md-nav__link--active { - color: #7cb342; } - -[data-md-color-primary="light-green"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="lime"] { - background-color: #c0ca33; } - -[data-md-color-primary="lime"] .md-typeset a { - color: #c0ca33; } - -[data-md-color-primary="lime"] .md-header { - background-color: #c0ca33; } - -[data-md-color-primary="lime"] .md-hero { - background-color: #c0ca33; } - -[data-md-color-primary="lime"] .md-nav__link:active, -[data-md-color-primary="lime"] .md-nav__link--active { - color: #c0ca33; } - -[data-md-color-primary="lime"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="yellow"] { - background-color: #f9a825; } - -[data-md-color-primary="yellow"] .md-typeset a { - color: #f9a825; } - -[data-md-color-primary="yellow"] .md-header { - background-color: #f9a825; } - -[data-md-color-primary="yellow"] .md-hero { - background-color: #f9a825; } - -[data-md-color-primary="yellow"] .md-nav__link:active, -[data-md-color-primary="yellow"] .md-nav__link--active { - color: #f9a825; } - -[data-md-color-primary="yellow"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="amber"] { - background-color: #ffa000; } - -[data-md-color-primary="amber"] .md-typeset a { - color: #ffa000; } - -[data-md-color-primary="amber"] .md-header { - background-color: #ffa000; } - -[data-md-color-primary="amber"] .md-hero { - background-color: #ffa000; } - -[data-md-color-primary="amber"] .md-nav__link:active, -[data-md-color-primary="amber"] .md-nav__link--active { - color: #ffa000; } - -[data-md-color-primary="amber"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="orange"] { - background-color: #fb8c00; } - -[data-md-color-primary="orange"] .md-typeset a { - color: #fb8c00; } - -[data-md-color-primary="orange"] .md-header { - background-color: #fb8c00; } - -[data-md-color-primary="orange"] .md-hero { - background-color: #fb8c00; } - -[data-md-color-primary="orange"] .md-nav__link:active, -[data-md-color-primary="orange"] .md-nav__link--active { - color: #fb8c00; } - -[data-md-color-primary="orange"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="deep-orange"] { - background-color: #ff7043; } - -[data-md-color-primary="deep-orange"] .md-typeset a { - color: #ff7043; } - -[data-md-color-primary="deep-orange"] .md-header { - background-color: #ff7043; } - -[data-md-color-primary="deep-orange"] .md-hero { - background-color: #ff7043; } - -[data-md-color-primary="deep-orange"] .md-nav__link:active, -[data-md-color-primary="deep-orange"] .md-nav__link--active { - color: #ff7043; } - -[data-md-color-primary="deep-orange"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="brown"] { - background-color: #795548; } - -[data-md-color-primary="brown"] .md-typeset a { - color: #795548; } - -[data-md-color-primary="brown"] .md-header { - background-color: #795548; } - -[data-md-color-primary="brown"] .md-hero { - background-color: #795548; } - -[data-md-color-primary="brown"] .md-nav__link:active, -[data-md-color-primary="brown"] .md-nav__link--active { - color: #795548; } - -[data-md-color-primary="brown"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="grey"] { - background-color: #757575; } - -[data-md-color-primary="grey"] .md-typeset a { - color: #757575; } - -[data-md-color-primary="grey"] .md-header { - background-color: #757575; } - -[data-md-color-primary="grey"] .md-hero { - background-color: #757575; } - -[data-md-color-primary="grey"] .md-nav__link:active, -[data-md-color-primary="grey"] .md-nav__link--active { - color: #757575; } - -[data-md-color-primary="grey"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="blue-grey"] { - background-color: #546e7a; } - -[data-md-color-primary="blue-grey"] .md-typeset a { - color: #546e7a; } - -[data-md-color-primary="blue-grey"] .md-header { - background-color: #546e7a; } - -[data-md-color-primary="blue-grey"] .md-hero { - background-color: #546e7a; } - -[data-md-color-primary="blue-grey"] .md-nav__link:active, -[data-md-color-primary="blue-grey"] .md-nav__link--active { - color: #546e7a; } - -[data-md-color-primary="blue-grey"] .md-nav__item--nested > .md-nav__link { - color: inherit; } - -button[data-md-color-primary="white"] { - background-color: white; - color: rgba(0, 0, 0, 0.87); - box-shadow: 0 0 0.1rem rgba(0, 0, 0, 0.54) inset; } - -[data-md-color-primary="white"] .md-header { - background-color: white; - color: rgba(0, 0, 0, 0.87); } - -[data-md-color-primary="white"] .md-hero { - background-color: white; - color: rgba(0, 0, 0, 0.87); } - [data-md-color-primary="white"] .md-hero--expand { - border-bottom: 0.1rem solid rgba(0, 0, 0, 0.07); } - -button[data-md-color-accent="red"] { - background-color: #ff1744; } - -[data-md-color-accent="red"] .md-typeset a:hover, -[data-md-color-accent="red"] .md-typeset a:active { - color: #ff1744; } - -[data-md-color-accent="red"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="red"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #ff1744; } - -[data-md-color-accent="red"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="red"] .md-typeset .md-clipboard:active::before { - color: #ff1744; } - -[data-md-color-accent="red"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="red"] .md-typeset .footnote li:target .footnote-backref { - color: #ff1744; } - -[data-md-color-accent="red"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="red"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="red"] .md-typeset [id] .headerlink:focus { - color: #ff1744; } - -[data-md-color-accent="red"] .md-nav__link:focus, -[data-md-color-accent="red"] .md-nav__link:hover { - color: #ff1744; } - -[data-md-color-accent="red"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #ff1744; } - -[data-md-color-accent="red"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="red"] .md-search-result__link:hover { - background-color: rgba(255, 23, 68, 0.1); } - -[data-md-color-accent="red"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #ff1744; } - -[data-md-color-accent="red"] .md-source-file:hover::before { - background-color: #ff1744; } - -button[data-md-color-accent="pink"] { - background-color: #f50057; } - -[data-md-color-accent="pink"] .md-typeset a:hover, -[data-md-color-accent="pink"] .md-typeset a:active { - color: #f50057; } - -[data-md-color-accent="pink"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="pink"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #f50057; } - -[data-md-color-accent="pink"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="pink"] .md-typeset .md-clipboard:active::before { - color: #f50057; } - -[data-md-color-accent="pink"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="pink"] .md-typeset .footnote li:target .footnote-backref { - color: #f50057; } - -[data-md-color-accent="pink"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="pink"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="pink"] .md-typeset [id] .headerlink:focus { - color: #f50057; } - -[data-md-color-accent="pink"] .md-nav__link:focus, -[data-md-color-accent="pink"] .md-nav__link:hover { - color: #f50057; } - -[data-md-color-accent="pink"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #f50057; } - -[data-md-color-accent="pink"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="pink"] .md-search-result__link:hover { - background-color: rgba(245, 0, 87, 0.1); } - -[data-md-color-accent="pink"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #f50057; } - -[data-md-color-accent="pink"] .md-source-file:hover::before { - background-color: #f50057; } - -button[data-md-color-accent="purple"] { - background-color: #e040fb; } - -[data-md-color-accent="purple"] .md-typeset a:hover, -[data-md-color-accent="purple"] .md-typeset a:active { - color: #e040fb; } - -[data-md-color-accent="purple"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="purple"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #e040fb; } - -[data-md-color-accent="purple"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="purple"] .md-typeset .md-clipboard:active::before { - color: #e040fb; } - -[data-md-color-accent="purple"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="purple"] .md-typeset .footnote li:target .footnote-backref { - color: #e040fb; } - -[data-md-color-accent="purple"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="purple"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="purple"] .md-typeset [id] .headerlink:focus { - color: #e040fb; } - -[data-md-color-accent="purple"] .md-nav__link:focus, -[data-md-color-accent="purple"] .md-nav__link:hover { - color: #e040fb; } - -[data-md-color-accent="purple"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #e040fb; } - -[data-md-color-accent="purple"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="purple"] .md-search-result__link:hover { - background-color: rgba(224, 64, 251, 0.1); } - -[data-md-color-accent="purple"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #e040fb; } - -[data-md-color-accent="purple"] .md-source-file:hover::before { - background-color: #e040fb; } - -button[data-md-color-accent="deep-purple"] { - background-color: #7c4dff; } - -[data-md-color-accent="deep-purple"] .md-typeset a:hover, -[data-md-color-accent="deep-purple"] .md-typeset a:active { - color: #7c4dff; } - -[data-md-color-accent="deep-purple"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="deep-purple"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #7c4dff; } - -[data-md-color-accent="deep-purple"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="deep-purple"] .md-typeset .md-clipboard:active::before { - color: #7c4dff; } - -[data-md-color-accent="deep-purple"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="deep-purple"] .md-typeset .footnote li:target .footnote-backref { - color: #7c4dff; } - -[data-md-color-accent="deep-purple"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="deep-purple"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="deep-purple"] .md-typeset [id] .headerlink:focus { - color: #7c4dff; } - -[data-md-color-accent="deep-purple"] .md-nav__link:focus, -[data-md-color-accent="deep-purple"] .md-nav__link:hover { - color: #7c4dff; } - -[data-md-color-accent="deep-purple"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #7c4dff; } - -[data-md-color-accent="deep-purple"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="deep-purple"] .md-search-result__link:hover { - background-color: rgba(124, 77, 255, 0.1); } - -[data-md-color-accent="deep-purple"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #7c4dff; } - -[data-md-color-accent="deep-purple"] .md-source-file:hover::before { - background-color: #7c4dff; } - -button[data-md-color-accent="indigo"] { - background-color: #536dfe; } - -[data-md-color-accent="indigo"] .md-typeset a:hover, -[data-md-color-accent="indigo"] .md-typeset a:active { - color: #536dfe; } - -[data-md-color-accent="indigo"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="indigo"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #536dfe; } - -[data-md-color-accent="indigo"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="indigo"] .md-typeset .md-clipboard:active::before { - color: #536dfe; } - -[data-md-color-accent="indigo"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="indigo"] .md-typeset .footnote li:target .footnote-backref { - color: #536dfe; } - -[data-md-color-accent="indigo"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="indigo"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="indigo"] .md-typeset [id] .headerlink:focus { - color: #536dfe; } - -[data-md-color-accent="indigo"] .md-nav__link:focus, -[data-md-color-accent="indigo"] .md-nav__link:hover { - color: #536dfe; } - -[data-md-color-accent="indigo"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #536dfe; } - -[data-md-color-accent="indigo"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="indigo"] .md-search-result__link:hover { - background-color: rgba(83, 109, 254, 0.1); } - -[data-md-color-accent="indigo"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #536dfe; } - -[data-md-color-accent="indigo"] .md-source-file:hover::before { - background-color: #536dfe; } - -button[data-md-color-accent="blue"] { - background-color: #448aff; } - -[data-md-color-accent="blue"] .md-typeset a:hover, -[data-md-color-accent="blue"] .md-typeset a:active { - color: #448aff; } - -[data-md-color-accent="blue"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="blue"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #448aff; } - -[data-md-color-accent="blue"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="blue"] .md-typeset .md-clipboard:active::before { - color: #448aff; } - -[data-md-color-accent="blue"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="blue"] .md-typeset .footnote li:target .footnote-backref { - color: #448aff; } - -[data-md-color-accent="blue"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="blue"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="blue"] .md-typeset [id] .headerlink:focus { - color: #448aff; } - -[data-md-color-accent="blue"] .md-nav__link:focus, -[data-md-color-accent="blue"] .md-nav__link:hover { - color: #448aff; } - -[data-md-color-accent="blue"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #448aff; } - -[data-md-color-accent="blue"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="blue"] .md-search-result__link:hover { - background-color: rgba(68, 138, 255, 0.1); } - -[data-md-color-accent="blue"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #448aff; } - -[data-md-color-accent="blue"] .md-source-file:hover::before { - background-color: #448aff; } - -button[data-md-color-accent="light-blue"] { - background-color: #0091ea; } - -[data-md-color-accent="light-blue"] .md-typeset a:hover, -[data-md-color-accent="light-blue"] .md-typeset a:active { - color: #0091ea; } - -[data-md-color-accent="light-blue"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="light-blue"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #0091ea; } - -[data-md-color-accent="light-blue"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="light-blue"] .md-typeset .md-clipboard:active::before { - color: #0091ea; } - -[data-md-color-accent="light-blue"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="light-blue"] .md-typeset .footnote li:target .footnote-backref { - color: #0091ea; } - -[data-md-color-accent="light-blue"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="light-blue"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="light-blue"] .md-typeset [id] .headerlink:focus { - color: #0091ea; } - -[data-md-color-accent="light-blue"] .md-nav__link:focus, -[data-md-color-accent="light-blue"] .md-nav__link:hover { - color: #0091ea; } - -[data-md-color-accent="light-blue"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #0091ea; } - -[data-md-color-accent="light-blue"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="light-blue"] .md-search-result__link:hover { - background-color: rgba(0, 145, 234, 0.1); } - -[data-md-color-accent="light-blue"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #0091ea; } - -[data-md-color-accent="light-blue"] .md-source-file:hover::before { - background-color: #0091ea; } - -button[data-md-color-accent="cyan"] { - background-color: #00b8d4; } - -[data-md-color-accent="cyan"] .md-typeset a:hover, -[data-md-color-accent="cyan"] .md-typeset a:active { - color: #00b8d4; } - -[data-md-color-accent="cyan"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="cyan"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #00b8d4; } - -[data-md-color-accent="cyan"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="cyan"] .md-typeset .md-clipboard:active::before { - color: #00b8d4; } - -[data-md-color-accent="cyan"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="cyan"] .md-typeset .footnote li:target .footnote-backref { - color: #00b8d4; } - -[data-md-color-accent="cyan"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="cyan"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="cyan"] .md-typeset [id] .headerlink:focus { - color: #00b8d4; } - -[data-md-color-accent="cyan"] .md-nav__link:focus, -[data-md-color-accent="cyan"] .md-nav__link:hover { - color: #00b8d4; } - -[data-md-color-accent="cyan"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #00b8d4; } - -[data-md-color-accent="cyan"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="cyan"] .md-search-result__link:hover { - background-color: rgba(0, 184, 212, 0.1); } - -[data-md-color-accent="cyan"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #00b8d4; } - -[data-md-color-accent="cyan"] .md-source-file:hover::before { - background-color: #00b8d4; } - -button[data-md-color-accent="teal"] { - background-color: #00bfa5; } - -[data-md-color-accent="teal"] .md-typeset a:hover, -[data-md-color-accent="teal"] .md-typeset a:active { - color: #00bfa5; } - -[data-md-color-accent="teal"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="teal"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #00bfa5; } - -[data-md-color-accent="teal"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="teal"] .md-typeset .md-clipboard:active::before { - color: #00bfa5; } - -[data-md-color-accent="teal"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="teal"] .md-typeset .footnote li:target .footnote-backref { - color: #00bfa5; } - -[data-md-color-accent="teal"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="teal"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="teal"] .md-typeset [id] .headerlink:focus { - color: #00bfa5; } - -[data-md-color-accent="teal"] .md-nav__link:focus, -[data-md-color-accent="teal"] .md-nav__link:hover { - color: #00bfa5; } - -[data-md-color-accent="teal"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #00bfa5; } - -[data-md-color-accent="teal"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="teal"] .md-search-result__link:hover { - background-color: rgba(0, 191, 165, 0.1); } - -[data-md-color-accent="teal"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #00bfa5; } - -[data-md-color-accent="teal"] .md-source-file:hover::before { - background-color: #00bfa5; } - -button[data-md-color-accent="green"] { - background-color: #00c853; } - -[data-md-color-accent="green"] .md-typeset a:hover, -[data-md-color-accent="green"] .md-typeset a:active { - color: #00c853; } - -[data-md-color-accent="green"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="green"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #00c853; } - -[data-md-color-accent="green"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="green"] .md-typeset .md-clipboard:active::before { - color: #00c853; } - -[data-md-color-accent="green"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="green"] .md-typeset .footnote li:target .footnote-backref { - color: #00c853; } - -[data-md-color-accent="green"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="green"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="green"] .md-typeset [id] .headerlink:focus { - color: #00c853; } - -[data-md-color-accent="green"] .md-nav__link:focus, -[data-md-color-accent="green"] .md-nav__link:hover { - color: #00c853; } - -[data-md-color-accent="green"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #00c853; } - -[data-md-color-accent="green"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="green"] .md-search-result__link:hover { - background-color: rgba(0, 200, 83, 0.1); } - -[data-md-color-accent="green"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #00c853; } - -[data-md-color-accent="green"] .md-source-file:hover::before { - background-color: #00c853; } - -button[data-md-color-accent="light-green"] { - background-color: #64dd17; } - -[data-md-color-accent="light-green"] .md-typeset a:hover, -[data-md-color-accent="light-green"] .md-typeset a:active { - color: #64dd17; } - -[data-md-color-accent="light-green"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="light-green"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #64dd17; } - -[data-md-color-accent="light-green"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="light-green"] .md-typeset .md-clipboard:active::before { - color: #64dd17; } - -[data-md-color-accent="light-green"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="light-green"] .md-typeset .footnote li:target .footnote-backref { - color: #64dd17; } - -[data-md-color-accent="light-green"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="light-green"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="light-green"] .md-typeset [id] .headerlink:focus { - color: #64dd17; } - -[data-md-color-accent="light-green"] .md-nav__link:focus, -[data-md-color-accent="light-green"] .md-nav__link:hover { - color: #64dd17; } - -[data-md-color-accent="light-green"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #64dd17; } - -[data-md-color-accent="light-green"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="light-green"] .md-search-result__link:hover { - background-color: rgba(100, 221, 23, 0.1); } - -[data-md-color-accent="light-green"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #64dd17; } - -[data-md-color-accent="light-green"] .md-source-file:hover::before { - background-color: #64dd17; } - -button[data-md-color-accent="lime"] { - background-color: #aeea00; } - -[data-md-color-accent="lime"] .md-typeset a:hover, -[data-md-color-accent="lime"] .md-typeset a:active { - color: #aeea00; } - -[data-md-color-accent="lime"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="lime"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #aeea00; } - -[data-md-color-accent="lime"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="lime"] .md-typeset .md-clipboard:active::before { - color: #aeea00; } - -[data-md-color-accent="lime"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="lime"] .md-typeset .footnote li:target .footnote-backref { - color: #aeea00; } - -[data-md-color-accent="lime"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="lime"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="lime"] .md-typeset [id] .headerlink:focus { - color: #aeea00; } - -[data-md-color-accent="lime"] .md-nav__link:focus, -[data-md-color-accent="lime"] .md-nav__link:hover { - color: #aeea00; } - -[data-md-color-accent="lime"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #aeea00; } - -[data-md-color-accent="lime"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="lime"] .md-search-result__link:hover { - background-color: rgba(174, 234, 0, 0.1); } - -[data-md-color-accent="lime"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #aeea00; } - -[data-md-color-accent="lime"] .md-source-file:hover::before { - background-color: #aeea00; } - -button[data-md-color-accent="yellow"] { - background-color: #ffd600; } - -[data-md-color-accent="yellow"] .md-typeset a:hover, -[data-md-color-accent="yellow"] .md-typeset a:active { - color: #ffd600; } - -[data-md-color-accent="yellow"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="yellow"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #ffd600; } - -[data-md-color-accent="yellow"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="yellow"] .md-typeset .md-clipboard:active::before { - color: #ffd600; } - -[data-md-color-accent="yellow"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="yellow"] .md-typeset .footnote li:target .footnote-backref { - color: #ffd600; } - -[data-md-color-accent="yellow"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="yellow"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="yellow"] .md-typeset [id] .headerlink:focus { - color: #ffd600; } - -[data-md-color-accent="yellow"] .md-nav__link:focus, -[data-md-color-accent="yellow"] .md-nav__link:hover { - color: #ffd600; } - -[data-md-color-accent="yellow"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #ffd600; } - -[data-md-color-accent="yellow"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="yellow"] .md-search-result__link:hover { - background-color: rgba(255, 214, 0, 0.1); } - -[data-md-color-accent="yellow"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #ffd600; } - -[data-md-color-accent="yellow"] .md-source-file:hover::before { - background-color: #ffd600; } - -button[data-md-color-accent="amber"] { - background-color: #ffab00; } - -[data-md-color-accent="amber"] .md-typeset a:hover, -[data-md-color-accent="amber"] .md-typeset a:active { - color: #ffab00; } - -[data-md-color-accent="amber"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="amber"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #ffab00; } - -[data-md-color-accent="amber"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="amber"] .md-typeset .md-clipboard:active::before { - color: #ffab00; } - -[data-md-color-accent="amber"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="amber"] .md-typeset .footnote li:target .footnote-backref { - color: #ffab00; } - -[data-md-color-accent="amber"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="amber"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="amber"] .md-typeset [id] .headerlink:focus { - color: #ffab00; } - -[data-md-color-accent="amber"] .md-nav__link:focus, -[data-md-color-accent="amber"] .md-nav__link:hover { - color: #ffab00; } - -[data-md-color-accent="amber"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #ffab00; } - -[data-md-color-accent="amber"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="amber"] .md-search-result__link:hover { - background-color: rgba(255, 171, 0, 0.1); } - -[data-md-color-accent="amber"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #ffab00; } - -[data-md-color-accent="amber"] .md-source-file:hover::before { - background-color: #ffab00; } - -button[data-md-color-accent="orange"] { - background-color: #ff9100; } - -[data-md-color-accent="orange"] .md-typeset a:hover, -[data-md-color-accent="orange"] .md-typeset a:active { - color: #ff9100; } - -[data-md-color-accent="orange"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="orange"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #ff9100; } - -[data-md-color-accent="orange"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="orange"] .md-typeset .md-clipboard:active::before { - color: #ff9100; } - -[data-md-color-accent="orange"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="orange"] .md-typeset .footnote li:target .footnote-backref { - color: #ff9100; } - -[data-md-color-accent="orange"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="orange"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="orange"] .md-typeset [id] .headerlink:focus { - color: #ff9100; } - -[data-md-color-accent="orange"] .md-nav__link:focus, -[data-md-color-accent="orange"] .md-nav__link:hover { - color: #ff9100; } - -[data-md-color-accent="orange"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #ff9100; } - -[data-md-color-accent="orange"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="orange"] .md-search-result__link:hover { - background-color: rgba(255, 145, 0, 0.1); } - -[data-md-color-accent="orange"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #ff9100; } - -[data-md-color-accent="orange"] .md-source-file:hover::before { - background-color: #ff9100; } - -button[data-md-color-accent="deep-orange"] { - background-color: #ff6e40; } - -[data-md-color-accent="deep-orange"] .md-typeset a:hover, -[data-md-color-accent="deep-orange"] .md-typeset a:active { - color: #ff6e40; } - -[data-md-color-accent="deep-orange"] .md-typeset pre code::-webkit-scrollbar-thumb:hover, -[data-md-color-accent="deep-orange"] .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover { - background-color: #ff6e40; } - -[data-md-color-accent="deep-orange"] .md-typeset .md-clipboard:hover::before, -[data-md-color-accent="deep-orange"] .md-typeset .md-clipboard:active::before { - color: #ff6e40; } - -[data-md-color-accent="deep-orange"] .md-typeset .footnote li:hover .footnote-backref:hover, -[data-md-color-accent="deep-orange"] .md-typeset .footnote li:target .footnote-backref { - color: #ff6e40; } - -[data-md-color-accent="deep-orange"] .md-typeset [id]:hover .headerlink:hover, -[data-md-color-accent="deep-orange"] .md-typeset [id]:target .headerlink, -[data-md-color-accent="deep-orange"] .md-typeset [id] .headerlink:focus { - color: #ff6e40; } - -[data-md-color-accent="deep-orange"] .md-nav__link:focus, -[data-md-color-accent="deep-orange"] .md-nav__link:hover { - color: #ff6e40; } - -[data-md-color-accent="deep-orange"] .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #ff6e40; } - -[data-md-color-accent="deep-orange"] .md-search-result__link[data-md-state="active"], [data-md-color-accent="deep-orange"] .md-search-result__link:hover { - background-color: rgba(255, 110, 64, 0.1); } - -[data-md-color-accent="deep-orange"] .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #ff6e40; } - -[data-md-color-accent="deep-orange"] .md-source-file:hover::before { - background-color: #ff6e40; } - -@media only screen and (max-width: 59.9375em) { - [data-md-color-primary="red"] .md-nav__source { - background-color: rgba(190, 66, 64, 0.9675); } - [data-md-color-primary="pink"] .md-nav__source { - background-color: rgba(185, 24, 79, 0.9675); } - [data-md-color-primary="purple"] .md-nav__source { - background-color: rgba(136, 57, 150, 0.9675); } - [data-md-color-primary="deep-purple"] .md-nav__source { - background-color: rgba(100, 69, 154, 0.9675); } - [data-md-color-primary="indigo"] .md-nav__source { - background-color: rgba(50, 64, 144, 0.9675); } - [data-md-color-primary="blue"] .md-nav__source { - background-color: rgba(26, 119, 193, 0.9675); } - [data-md-color-primary="light-blue"] .md-nav__source { - background-color: rgba(2, 134, 194, 0.9675); } - [data-md-color-primary="cyan"] .md-nav__source { - background-color: rgba(0, 150, 169, 0.9675); } - [data-md-color-primary="teal"] .md-nav__source { - background-color: rgba(0, 119, 108, 0.9675); } - [data-md-color-primary="green"] .md-nav__source { - background-color: rgba(60, 139, 64, 0.9675); } - [data-md-color-primary="light-green"] .md-nav__source { - background-color: rgba(99, 142, 53, 0.9675); } - [data-md-color-primary="lime"] .md-nav__source { - background-color: rgba(153, 161, 41, 0.9675); } - [data-md-color-primary="yellow"] .md-nav__source { - background-color: rgba(198, 134, 29, 0.9675); } - [data-md-color-primary="amber"] .md-nav__source { - background-color: rgba(203, 127, 0, 0.9675); } - [data-md-color-primary="orange"] .md-nav__source { - background-color: rgba(200, 111, 0, 0.9675); } - [data-md-color-primary="deep-orange"] .md-nav__source { - background-color: rgba(203, 89, 53, 0.9675); } - [data-md-color-primary="brown"] .md-nav__source { - background-color: rgba(96, 68, 57, 0.9675); } - [data-md-color-primary="grey"] .md-nav__source { - background-color: rgba(93, 93, 93, 0.9675); } - [data-md-color-primary="blue-grey"] .md-nav__source { - background-color: rgba(67, 88, 97, 0.9675); } - [data-md-color-primary="white"] .md-nav__source { - background-color: rgba(0, 0, 0, 0.07); - color: rgba(0, 0, 0, 0.87); } } - -@media only screen and (max-width: 76.1875em) { - html [data-md-color-primary="red"] .md-nav--primary .md-nav__title--site { - background-color: #ef5350; } - html [data-md-color-primary="pink"] .md-nav--primary .md-nav__title--site { - background-color: #e91e63; } - html [data-md-color-primary="purple"] .md-nav--primary .md-nav__title--site { - background-color: #ab47bc; } - html [data-md-color-primary="deep-purple"] .md-nav--primary .md-nav__title--site { - background-color: #7e57c2; } - html [data-md-color-primary="indigo"] .md-nav--primary .md-nav__title--site { - background-color: #3f51b5; } - html [data-md-color-primary="blue"] .md-nav--primary .md-nav__title--site { - background-color: #2196f3; } - html [data-md-color-primary="light-blue"] .md-nav--primary .md-nav__title--site { - background-color: #03a9f4; } - html [data-md-color-primary="cyan"] .md-nav--primary .md-nav__title--site { - background-color: #00bcd4; } - html [data-md-color-primary="teal"] .md-nav--primary .md-nav__title--site { - background-color: #009688; } - html [data-md-color-primary="green"] .md-nav--primary .md-nav__title--site { - background-color: #4caf50; } - html [data-md-color-primary="light-green"] .md-nav--primary .md-nav__title--site { - background-color: #7cb342; } - html [data-md-color-primary="lime"] .md-nav--primary .md-nav__title--site { - background-color: #c0ca33; } - html [data-md-color-primary="yellow"] .md-nav--primary .md-nav__title--site { - background-color: #f9a825; } - html [data-md-color-primary="amber"] .md-nav--primary .md-nav__title--site { - background-color: #ffa000; } - html [data-md-color-primary="orange"] .md-nav--primary .md-nav__title--site { - background-color: #fb8c00; } - html [data-md-color-primary="deep-orange"] .md-nav--primary .md-nav__title--site { - background-color: #ff7043; } - html [data-md-color-primary="brown"] .md-nav--primary .md-nav__title--site { - background-color: #795548; } - html [data-md-color-primary="grey"] .md-nav--primary .md-nav__title--site { - background-color: #757575; } - html [data-md-color-primary="blue-grey"] .md-nav--primary .md-nav__title--site { - background-color: #546e7a; } - html [data-md-color-primary="white"] .md-nav--primary .md-nav__title--site { - background-color: white; - color: rgba(0, 0, 0, 0.87); } - [data-md-color-primary="white"] .md-hero { - border-bottom: 0.1rem solid rgba(0, 0, 0, 0.07); } } - -@media only screen and (min-width: 76.25em) { - [data-md-color-primary="red"] .md-tabs { - background-color: #ef5350; } - [data-md-color-primary="pink"] .md-tabs { - background-color: #e91e63; } - [data-md-color-primary="purple"] .md-tabs { - background-color: #ab47bc; } - [data-md-color-primary="deep-purple"] .md-tabs { - background-color: #7e57c2; } - [data-md-color-primary="indigo"] .md-tabs { - background-color: #3f51b5; } - [data-md-color-primary="blue"] .md-tabs { - background-color: #2196f3; } - [data-md-color-primary="light-blue"] .md-tabs { - background-color: #03a9f4; } - [data-md-color-primary="cyan"] .md-tabs { - background-color: #00bcd4; } - [data-md-color-primary="teal"] .md-tabs { - background-color: #009688; } - [data-md-color-primary="green"] .md-tabs { - background-color: #4caf50; } - [data-md-color-primary="light-green"] .md-tabs { - background-color: #7cb342; } - [data-md-color-primary="lime"] .md-tabs { - background-color: #c0ca33; } - [data-md-color-primary="yellow"] .md-tabs { - background-color: #f9a825; } - [data-md-color-primary="amber"] .md-tabs { - background-color: #ffa000; } - [data-md-color-primary="orange"] .md-tabs { - background-color: #fb8c00; } - [data-md-color-primary="deep-orange"] .md-tabs { - background-color: #ff7043; } - [data-md-color-primary="brown"] .md-tabs { - background-color: #795548; } - [data-md-color-primary="grey"] .md-tabs { - background-color: #757575; } - [data-md-color-primary="blue-grey"] .md-tabs { - background-color: #546e7a; } - [data-md-color-primary="white"] .md-tabs { - border-bottom: 0.1rem solid rgba(0, 0, 0, 0.07); - background-color: white; - color: rgba(0, 0, 0, 0.87); } } - -@media only screen and (min-width: 60em) { - [data-md-color-primary="white"] .md-search__input { - background-color: rgba(0, 0, 0, 0.07); } - [data-md-color-primary="white"] .md-search__input::-webkit-input-placeholder { - color: rgba(0, 0, 0, 0.54); } - [data-md-color-primary="white"] .md-search__input:-ms-input-placeholder { - color: rgba(0, 0, 0, 0.54); } - [data-md-color-primary="white"] .md-search__input::-ms-input-placeholder { - color: rgba(0, 0, 0, 0.54); } - [data-md-color-primary="white"] .md-search__input::placeholder { - color: rgba(0, 0, 0, 0.54); } } - -/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJhc3NldHMvc3R5bGVzaGVldHMvYXBwbGljYXRpb24tcGFsZXR0ZS4yMjkxNTEyNi5jc3MiLCJzb3VyY2VSb290IjoiIn0=*/ \ No newline at end of file diff --git a/docs/v3/v1/assets/stylesheets/application.451f80e5.css b/docs/v3/v1/assets/stylesheets/application.451f80e5.css deleted file mode 100644 index ec0393870..000000000 --- a/docs/v3/v1/assets/stylesheets/application.451f80e5.css +++ /dev/null @@ -1,2552 +0,0 @@ -@charset "UTF-8"; -html { - box-sizing: border-box; } - -*, -*::before, -*::after { - box-sizing: inherit; } - -html { - -webkit-text-size-adjust: none; - -moz-text-size-adjust: none; - -ms-text-size-adjust: none; - text-size-adjust: none; } - -body { - margin: 0; } - -hr { - overflow: visible; - box-sizing: content-box; } - -a { - -webkit-text-decoration-skip: objects; } - -a, -button, -label, -input { - -webkit-tap-highlight-color: transparent; } - -a { - color: inherit; - text-decoration: none; } - -small { - font-size: 80%; } - -sub, -sup { - position: relative; - font-size: 80%; - line-height: 0; - vertical-align: baseline; } - -sub { - bottom: -0.25em; } - -sup { - top: -0.5em; } - -img { - border-style: none; } - -table { - border-collapse: separate; - border-spacing: 0; } - -td, -th { - font-weight: normal; - vertical-align: top; } - -button { - margin: 0; - padding: 0; - border: 0; - outline-style: none; - background: transparent; - font-size: inherit; } - -input { - border: 0; - outline: 0; } - -.md-icon, .md-clipboard::before, .md-nav__title::before, .md-nav__button, .md-nav__link::after, .md-search-result__article--document::before, .md-source-file::before, .md-typeset .admonition > .admonition-title::before, .md-typeset details > .admonition-title::before, .md-typeset .admonition > summary::before, .md-typeset details > summary::before, .md-typeset .footnote-backref, .md-typeset .critic.comment::before, .md-typeset summary::after, .md-typeset .task-list-control .task-list-indicator::before { - font-family: "Material Icons"; - font-style: normal; - font-variant: normal; - font-weight: normal; - line-height: 1; - text-transform: none; - white-space: nowrap; - speak: none; - word-wrap: normal; - direction: ltr; } - .md-content__icon, .md-header-nav__button, .md-footer-nav__button, .md-nav__title::before, .md-nav__button, .md-search-result__article--document::before { - display: inline-block; - margin: 0.4rem; - padding: 0.8rem; - font-size: 2.4rem; - cursor: pointer; } - -.md-icon--arrow-back::before { - content: "\E5C4"; } - -.md-icon--arrow-forward::before { - content: "\E5C8"; } - -.md-icon--menu::before { - content: "\E5D2"; } - -.md-icon--search::before { - content: "\E8B6"; } - -[dir="rtl"] .md-icon--arrow-back::before { - content: "\E5C8"; } - -[dir="rtl"] .md-icon--arrow-forward::before { - content: "\E5C4"; } - -body { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; } - -body, -input { - color: rgba(0, 0, 0, 0.87); - -webkit-font-feature-settings: "kern", "liga"; - font-feature-settings: "kern", "liga"; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } - -pre, -code, -kbd { - color: rgba(0, 0, 0, 0.87); - -webkit-font-feature-settings: "kern"; - font-feature-settings: "kern"; - font-family: "Courier New", Courier, monospace; } - -.md-typeset { - font-size: 1.6rem; - line-height: 1.6; - -webkit-print-color-adjust: exact; } - .md-typeset p, - .md-typeset ul, - .md-typeset ol, - .md-typeset blockquote { - margin: 1em 0; } - .md-typeset h1 { - margin: 0 0 4rem; - color: rgba(0, 0, 0, 0.54); - font-size: 3.125rem; - font-weight: 300; - letter-spacing: -0.01em; - line-height: 1.3; } - .md-typeset h2 { - margin: 4rem 0 1.6rem; - font-size: 2.5rem; - font-weight: 300; - letter-spacing: -0.01em; - line-height: 1.4; } - .md-typeset h3 { - margin: 3.2rem 0 1.6rem; - font-size: 2rem; - font-weight: 400; - letter-spacing: -0.01em; - line-height: 1.5; } - .md-typeset h2 + h3 { - margin-top: 1.6rem; } - .md-typeset h4 { - margin: 1.6rem 0; - font-size: 1.6rem; - font-weight: 700; - letter-spacing: -0.01em; } - .md-typeset h5, - .md-typeset h6 { - margin: 1.6rem 0; - color: rgba(0, 0, 0, 0.54); - font-size: 1.28rem; - font-weight: 700; - letter-spacing: -0.01em; } - .md-typeset h5 { - text-transform: uppercase; } - .md-typeset hr { - margin: 1.5em 0; - border-bottom: 0.1rem dotted rgba(0, 0, 0, 0.26); } - .md-typeset a { - color: #3f51b5; - word-break: break-word; } - .md-typeset a, .md-typeset a::before { - transition: color 0.125s; } - .md-typeset a:hover, .md-typeset a:active { - color: #536dfe; } - .md-typeset code, - .md-typeset pre { - background-color: rgba(236, 236, 236, 0.5); - color: #37474F; - font-size: 85%; - direction: ltr; } - .md-typeset code { - margin: 0 0.29412em; - padding: 0.07353em 0; - border-radius: 0.2rem; - box-shadow: 0.29412em 0 0 rgba(236, 236, 236, 0.5), -0.29412em 0 0 rgba(236, 236, 236, 0.5); - word-break: break-word; - -webkit-box-decoration-break: clone; - box-decoration-break: clone; } - .md-typeset h1 code, - .md-typeset h2 code, - .md-typeset h3 code, - .md-typeset h4 code, - .md-typeset h5 code, - .md-typeset h6 code { - margin: 0; - background-color: transparent; - box-shadow: none; } - .md-typeset a > code { - margin: inherit; - padding: inherit; - border-radius: none; - background-color: inherit; - color: inherit; - box-shadow: none; } - .md-typeset pre { - position: relative; - margin: 1em 0; - border-radius: 0.2rem; - line-height: 1.4; - -webkit-overflow-scrolling: touch; } - .md-typeset pre > code { - display: block; - margin: 0; - padding: 1.05rem 1.2rem; - background-color: transparent; - font-size: inherit; - box-shadow: none; - -webkit-box-decoration-break: none; - box-decoration-break: none; - overflow: auto; } - .md-typeset pre > code::-webkit-scrollbar { - width: 0.4rem; - height: 0.4rem; } - .md-typeset pre > code::-webkit-scrollbar-thumb { - background-color: rgba(0, 0, 0, 0.26); } - .md-typeset pre > code::-webkit-scrollbar-thumb:hover { - background-color: #536dfe; } - .md-typeset kbd { - padding: 0 0.29412em; - border: 0.1rem solid #c9c9c9; - border-radius: 0.3rem; - border-bottom-color: #bcbcbc; - background-color: #FCFCFC; - color: #555555; - font-size: 85%; - box-shadow: 0 0.1rem 0 #b0b0b0; - word-break: break-word; } - .md-typeset mark { - margin: 0 0.25em; - padding: 0.0625em 0; - border-radius: 0.2rem; - background-color: rgba(255, 235, 59, 0.5); - box-shadow: 0.25em 0 0 rgba(255, 235, 59, 0.5), -0.25em 0 0 rgba(255, 235, 59, 0.5); - word-break: break-word; - -webkit-box-decoration-break: clone; - box-decoration-break: clone; } - .md-typeset abbr { - border-bottom: 0.1rem dotted rgba(0, 0, 0, 0.54); - text-decoration: none; - cursor: help; } - .md-typeset small { - opacity: 0.75; } - .md-typeset sup, - .md-typeset sub { - margin-left: 0.07812em; } - [dir="rtl"] .md-typeset sup, [dir="rtl"] - .md-typeset sub { - margin-right: 0.07812em; - margin-left: initial; } - .md-typeset blockquote { - padding-left: 1.2rem; - border-left: 0.4rem solid rgba(0, 0, 0, 0.26); - color: rgba(0, 0, 0, 0.54); } - [dir="rtl"] .md-typeset blockquote { - padding-right: 1.2rem; - padding-left: initial; - border-right: 0.4rem solid rgba(0, 0, 0, 0.26); - border-left: initial; } - .md-typeset ul { - list-style-type: disc; } - .md-typeset ul, - .md-typeset ol { - margin-left: 0.625em; - padding: 0; } - [dir="rtl"] .md-typeset ul, [dir="rtl"] - .md-typeset ol { - margin-right: 0.625em; - margin-left: initial; } - .md-typeset ul ol, - .md-typeset ol ol { - list-style-type: lower-alpha; } - .md-typeset ul ol ol, - .md-typeset ol ol ol { - list-style-type: lower-roman; } - .md-typeset ul li, - .md-typeset ol li { - margin-bottom: 0.5em; - margin-left: 1.25em; } - [dir="rtl"] .md-typeset ul li, [dir="rtl"] - .md-typeset ol li { - margin-right: 1.25em; - margin-left: initial; } - .md-typeset ul li p, - .md-typeset ul li blockquote, - .md-typeset ol li p, - .md-typeset ol li blockquote { - margin: 0.5em 0; } - .md-typeset ul li:last-child, - .md-typeset ol li:last-child { - margin-bottom: 0; } - .md-typeset ul li ul, - .md-typeset ul li ol, - .md-typeset ol li ul, - .md-typeset ol li ol { - margin: 0.5em 0 0.5em 0.625em; } - [dir="rtl"] .md-typeset ul li ul, [dir="rtl"] - .md-typeset ul li ol, [dir="rtl"] - .md-typeset ol li ul, [dir="rtl"] - .md-typeset ol li ol { - margin-right: 0.625em; - margin-left: initial; } - .md-typeset dd { - margin: 1em 0 1em 1.875em; } - [dir="rtl"] .md-typeset dd { - margin-right: 1.875em; - margin-left: initial; } - .md-typeset iframe, - .md-typeset img, - .md-typeset svg { - max-width: 100%; } - .md-typeset table:not([class]) { - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2); - display: inline-block; - max-width: 100%; - border-radius: 0.2rem; - font-size: 1.28rem; - overflow: auto; - -webkit-overflow-scrolling: touch; } - .md-typeset table:not([class]) + * { - margin-top: 1.5em; } - .md-typeset table:not([class]) th:not([align]), - .md-typeset table:not([class]) td:not([align]) { - text-align: left; } - [dir="rtl"] .md-typeset table:not([class]) th:not([align]), [dir="rtl"] - .md-typeset table:not([class]) td:not([align]) { - text-align: right; } - .md-typeset table:not([class]) th { - min-width: 10rem; - padding: 1.2rem 1.6rem; - background-color: rgba(0, 0, 0, 0.54); - color: white; - vertical-align: top; } - .md-typeset table:not([class]) td { - padding: 1.2rem 1.6rem; - border-top: 0.1rem solid rgba(0, 0, 0, 0.07); - vertical-align: top; } - .md-typeset table:not([class]) tr:first-child td { - border-top: 0; } - .md-typeset table:not([class]) a { - word-break: normal; } - .md-typeset__scrollwrap { - margin: 1em -1.6rem; - overflow-x: auto; - -webkit-overflow-scrolling: touch; } - .md-typeset .md-typeset__table { - display: inline-block; - margin-bottom: 0.5em; - padding: 0 1.6rem; } - .md-typeset .md-typeset__table table { - display: table; - width: 100%; - margin: 0; - overflow: hidden; } - -html { - height: 100%; - font-size: 62.5%; - overflow-x: hidden; } - -body { - position: relative; - height: 100%; } - -hr { - display: block; - height: 0.1rem; - padding: 0; - border: 0; } - -.md-svg { - display: none; } - -.md-grid { - max-width: 122rem; - margin-right: auto; - margin-left: auto; } - -.md-container, -.md-main { - overflow: auto; } - -.md-container { - display: table; - width: 100%; - height: 100%; - padding-top: 4.8rem; - table-layout: fixed; } - -.md-main { - display: table-row; - height: 100%; } - .md-main__inner { - height: 100%; - padding-top: 3rem; - padding-bottom: 0.1rem; } - -.md-toggle { - display: none; } - -.md-overlay { - position: fixed; - top: 0; - width: 0; - height: 0; - transition: width 0s 0.25s, height 0s 0.25s, opacity 0.25s; - background-color: rgba(0, 0, 0, 0.54); - opacity: 0; - z-index: 3; } - -.md-flex { - display: table; } - .md-flex__cell { - display: table-cell; - position: relative; - vertical-align: top; } - .md-flex__cell--shrink { - width: 0%; } - .md-flex__cell--stretch { - display: table; - width: 100%; - table-layout: fixed; } - .md-flex__ellipsis { - display: table-cell; - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; } - -.md-skip { - position: fixed; - width: 0.1rem; - height: 0.1rem; - margin: 1rem; - padding: 0.6rem 1rem; - clip: rect(0.1rem); - -webkit-transform: translateY(0.8rem); - transform: translateY(0.8rem); - border-radius: 0.2rem; - background-color: rgba(0, 0, 0, 0.87); - color: white; - font-size: 1.28rem; - opacity: 0; - overflow: hidden; } - .md-skip:focus { - width: auto; - height: auto; - clip: auto; - -webkit-transform: translateX(0); - transform: translateX(0); - transition: opacity 0.175s 0.075s, -webkit-transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); - transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.175s 0.075s; - transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.175s 0.075s, -webkit-transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); - opacity: 1; - z-index: 10; } - -@page { - margin: 25mm; } - -.md-clipboard { - position: absolute; - top: 0.6rem; - right: 0.6rem; - width: 2.8rem; - height: 2.8rem; - border-radius: 0.2rem; - font-size: 1.6rem; - cursor: pointer; - z-index: 1; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; } - .md-clipboard::before { - transition: color 0.25s, opacity 0.25s; - color: rgba(0, 0, 0, 0.07); - content: "\E14D"; } - pre:hover .md-clipboard::before, - .codehilite:hover .md-clipboard::before, .md-typeset .highlight:hover .md-clipboard::before { - color: rgba(0, 0, 0, 0.54); } - .md-clipboard:focus::before, .md-clipboard:hover::before { - color: #536dfe; } - .md-clipboard__message { - display: block; - position: absolute; - top: 0; - right: 3.4rem; - padding: 0.6rem 1rem; - -webkit-transform: translateX(0.8rem); - transform: translateX(0.8rem); - transition: opacity 0.175s, -webkit-transform 0.25s cubic-bezier(0.9, 0.1, 0.9, 0); - transition: transform 0.25s cubic-bezier(0.9, 0.1, 0.9, 0), opacity 0.175s; - transition: transform 0.25s cubic-bezier(0.9, 0.1, 0.9, 0), opacity 0.175s, -webkit-transform 0.25s cubic-bezier(0.9, 0.1, 0.9, 0); - border-radius: 0.2rem; - background-color: rgba(0, 0, 0, 0.54); - color: white; - font-size: 1.28rem; - white-space: nowrap; - opacity: 0; - pointer-events: none; } - .md-clipboard__message--active { - -webkit-transform: translateX(0); - transform: translateX(0); - transition: opacity 0.175s 0.075s, -webkit-transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); - transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.175s 0.075s; - transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.175s 0.075s, -webkit-transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); - opacity: 1; - pointer-events: initial; } - .md-clipboard__message::before { - content: attr(aria-label); } - .md-clipboard__message::after { - display: block; - position: absolute; - top: 50%; - right: -0.4rem; - width: 0; - margin-top: -0.4rem; - border-width: 0.4rem 0 0.4rem 0.4rem; - border-style: solid; - border-color: transparent rgba(0, 0, 0, 0.54); - content: ""; } - -.md-content__inner { - margin: 0 1.6rem 2.4rem; - padding-top: 1.2rem; } - .md-content__inner::before { - display: block; - height: 0.8rem; - content: ""; } - .md-content__inner > :last-child { - margin-bottom: 0; } - -.md-content__icon { - position: relative; - margin: 0.8rem 0; - padding: 0; - float: right; } - .md-typeset .md-content__icon { - color: rgba(0, 0, 0, 0.26); } - -.md-header { - position: fixed; - top: 0; - right: 0; - left: 0; - height: 4.8rem; - transition: background-color 0.25s, color 0.25s; - background-color: #3f51b5; - color: white; - box-shadow: none; - z-index: 2; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; } - .no-js .md-header { - transition: none; - box-shadow: none; } - .md-header[data-md-state="shadow"] { - transition: background-color 0.25s, color 0.25s, box-shadow 0.25s; - box-shadow: 0 0 0.4rem rgba(0, 0, 0, 0.1), 0 0.4rem 0.8rem rgba(0, 0, 0, 0.2); } - -.md-header-nav { - padding: 0 0.4rem; } - .md-header-nav__button { - position: relative; - transition: opacity 0.25s; - z-index: 1; } - .md-header-nav__button:hover { - opacity: 0.7; } - .md-header-nav__button.md-logo * { - display: block; } - .no-js .md-header-nav__button.md-icon--search { - display: none; } - .md-header-nav__topic { - display: block; - position: absolute; - transition: opacity 0.15s, -webkit-transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1); - transition: transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.15s; - transition: transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.15s, -webkit-transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1); - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; } - .md-header-nav__topic + .md-header-nav__topic { - -webkit-transform: translateX(2.5rem); - transform: translateX(2.5rem); - transition: opacity 0.15s, -webkit-transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1); - transition: transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1), opacity 0.15s; - transition: transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1), opacity 0.15s, -webkit-transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1); - opacity: 0; - z-index: -1; - pointer-events: none; } - [dir="rtl"] .md-header-nav__topic + .md-header-nav__topic { - -webkit-transform: translateX(-2.5rem); - transform: translateX(-2.5rem); } - .no-js .md-header-nav__topic { - position: initial; } - .no-js .md-header-nav__topic + .md-header-nav__topic { - display: none; } - .md-header-nav__title { - padding: 0 2rem; - font-size: 1.8rem; - line-height: 4.8rem; } - .md-header-nav__title[data-md-state="active"] .md-header-nav__topic { - -webkit-transform: translateX(-2.5rem); - transform: translateX(-2.5rem); - transition: opacity 0.15s, -webkit-transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1); - transition: transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1), opacity 0.15s; - transition: transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1), opacity 0.15s, -webkit-transform 0.4s cubic-bezier(1, 0.7, 0.1, 0.1); - opacity: 0; - z-index: -1; - pointer-events: none; } - [dir="rtl"] .md-header-nav__title[data-md-state="active"] .md-header-nav__topic { - -webkit-transform: translateX(2.5rem); - transform: translateX(2.5rem); } - .md-header-nav__title[data-md-state="active"] .md-header-nav__topic + .md-header-nav__topic { - -webkit-transform: translateX(0); - transform: translateX(0); - transition: opacity 0.15s, -webkit-transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1); - transition: transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.15s; - transition: transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.15s, -webkit-transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1); - opacity: 1; - z-index: 0; - pointer-events: initial; } - .md-header-nav__source { - display: none; } - -.md-hero { - transition: background 0.25s; - background-color: #3f51b5; - color: white; - font-size: 2rem; - overflow: hidden; } - .md-hero__inner { - margin-top: 2rem; - padding: 1.6rem 1.6rem 0.8rem; - transition: opacity 0.25s, -webkit-transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1); - transition: transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.25s; - transition: transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.25s, -webkit-transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1); - transition-delay: 0.1s; } - [data-md-state="hidden"] .md-hero__inner { - pointer-events: none; - -webkit-transform: translateY(1.25rem); - transform: translateY(1.25rem); - transition: opacity 0.1s 0s, -webkit-transform 0s 0.4s; - transition: transform 0s 0.4s, opacity 0.1s 0s; - transition: transform 0s 0.4s, opacity 0.1s 0s, -webkit-transform 0s 0.4s; - opacity: 0; } - .md-hero--expand .md-hero__inner { - margin-bottom: 2.4rem; } - -.md-footer-nav { - background-color: rgba(0, 0, 0, 0.87); - color: white; } - .md-footer-nav__inner { - padding: 0.4rem; - overflow: auto; } - .md-footer-nav__link { - padding-top: 2.8rem; - padding-bottom: 0.8rem; - transition: opacity 0.25s; } - .md-footer-nav__link:hover { - opacity: 0.7; } - .md-footer-nav__link--prev { - width: 25%; - float: left; } - [dir="rtl"] .md-footer-nav__link--prev { - float: right; } - .md-footer-nav__link--next { - width: 75%; - float: right; - text-align: right; } - [dir="rtl"] .md-footer-nav__link--next { - float: left; - text-align: left; } - .md-footer-nav__button { - transition: background 0.25s; } - .md-footer-nav__title { - position: relative; - padding: 0 2rem; - font-size: 1.8rem; - line-height: 4.8rem; } - .md-footer-nav__direction { - position: absolute; - right: 0; - left: 0; - margin-top: -2rem; - padding: 0 2rem; - color: rgba(255, 255, 255, 0.7); - font-size: 1.5rem; } - -.md-footer-meta { - background-color: rgba(0, 0, 0, 0.895); } - .md-footer-meta__inner { - padding: 0.4rem; - overflow: auto; } - html .md-footer-meta.md-typeset a { - color: rgba(255, 255, 255, 0.7); } - html .md-footer-meta.md-typeset a:focus, html .md-footer-meta.md-typeset a:hover { - color: white; } - -.md-footer-copyright { - margin: 0 1.2rem; - padding: 0.8rem 0; - color: rgba(255, 255, 255, 0.3); - font-size: 1.28rem; } - .md-footer-copyright__highlight { - color: rgba(255, 255, 255, 0.7); } - -.md-footer-social { - margin: 0 0.8rem; - padding: 0.4rem 0 1.2rem; } - .md-footer-social__link { - display: inline-block; - width: 3.2rem; - height: 3.2rem; - font-size: 1.6rem; - text-align: center; } - .md-footer-social__link::before { - line-height: 1.9; } - -.md-nav { - font-size: 1.4rem; - line-height: 1.3; } - .md-nav__title { - display: block; - padding: 0 1.2rem; - font-weight: 700; - text-overflow: ellipsis; - overflow: hidden; } - .md-nav__title::before { - display: none; - content: "\E5C4"; } - [dir="rtl"] .md-nav__title::before { - content: "\E5C8"; } - .md-nav__title .md-nav__button { - display: none; } - .md-nav__list { - margin: 0; - padding: 0; - list-style: none; } - .md-nav__item { - padding: 0 1.2rem; } - .md-nav__item:last-child { - padding-bottom: 1.2rem; } - .md-nav__item .md-nav__item { - padding-right: 0; } - [dir="rtl"] .md-nav__item .md-nav__item { - padding-right: 1.2rem; - padding-left: 0; } - .md-nav__item .md-nav__item:last-child { - padding-bottom: 0; } - .md-nav__button img { - width: 100%; - height: auto; } - .md-nav__link { - display: block; - margin-top: 0.625em; - transition: color 0.125s; - text-overflow: ellipsis; - cursor: pointer; - overflow: hidden; } - .md-nav__item--nested > .md-nav__link::after { - content: "\E313"; } - html .md-nav__link[for="__toc"] { - display: none; } - html .md-nav__link[for="__toc"] ~ .md-nav { - display: none; } - html .md-nav__link[for="__toc"] + .md-nav__link::after { - display: none; } - .md-nav__link[data-md-state="blur"] { - color: rgba(0, 0, 0, 0.54); } - .md-nav__link:active, .md-nav__link--active { - color: #3f51b5; } - .md-nav__item--nested > .md-nav__link { - color: inherit; } - .md-nav__link:focus, .md-nav__link:hover { - color: #536dfe; } - .md-nav__source { - display: none; } - -.no-js .md-search { - display: none; } - -.md-search__overlay { - opacity: 0; - z-index: 1; } - -.md-search__form { - position: relative; } - -.md-search__input { - position: relative; - padding: 0 4.4rem 0 7.2rem; - text-overflow: ellipsis; - z-index: 2; } - [dir="rtl"] .md-search__input { - padding: 0 7.2rem 0 4.4rem; } - .md-search__input::-webkit-input-placeholder { - transition: color 0.25s cubic-bezier(0.1, 0.7, 0.1, 1); } - .md-search__input:-ms-input-placeholder { - transition: color 0.25s cubic-bezier(0.1, 0.7, 0.1, 1); } - .md-search__input::-ms-input-placeholder { - transition: color 0.25s cubic-bezier(0.1, 0.7, 0.1, 1); } - .md-search__input::placeholder { - transition: color 0.25s cubic-bezier(0.1, 0.7, 0.1, 1); } - .md-search__input ~ .md-search__icon, .md-search__input::-webkit-input-placeholder { - color: rgba(0, 0, 0, 0.54); } - .md-search__input ~ .md-search__icon, .md-search__input:-ms-input-placeholder { - color: rgba(0, 0, 0, 0.54); } - .md-search__input ~ .md-search__icon, .md-search__input::-ms-input-placeholder { - color: rgba(0, 0, 0, 0.54); } - .md-search__input ~ .md-search__icon, .md-search__input::placeholder { - color: rgba(0, 0, 0, 0.54); } - .md-search__input::-ms-clear { - display: none; } - -.md-search__icon { - position: absolute; - transition: color 0.25s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.25s; - font-size: 2.4rem; - cursor: pointer; - z-index: 2; } - .md-search__icon:hover { - opacity: 0.7; } - .md-search__icon[for="__search"] { - top: 0.6rem; - left: 1rem; } - [dir="rtl"] .md-search__icon[for="__search"] { - right: 1rem; - left: initial; } - .md-search__icon[for="__search"]::before { - content: "\E8B6"; } - .md-search__icon[type="reset"] { - top: 0.6rem; - right: 1rem; - -webkit-transform: scale(0.125); - transform: scale(0.125); - transition: opacity 0.15s, -webkit-transform 0.15s cubic-bezier(0.1, 0.7, 0.1, 1); - transition: transform 0.15s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.15s; - transition: transform 0.15s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.15s, -webkit-transform 0.15s cubic-bezier(0.1, 0.7, 0.1, 1); - opacity: 0; } - [dir="rtl"] .md-search__icon[type="reset"] { - right: initial; - left: 1rem; } - [data-md-toggle="search"]:checked ~ .md-header .md-search__input:valid ~ .md-search__icon[type="reset"] { - -webkit-transform: scale(1); - transform: scale(1); - opacity: 1; } - [data-md-toggle="search"]:checked ~ .md-header .md-search__input:valid ~ .md-search__icon[type="reset"]:hover { - opacity: 0.7; } - -.md-search__output { - position: absolute; - width: 100%; - border-radius: 0 0 0.2rem 0.2rem; - overflow: hidden; - z-index: 1; } - -.md-search__scrollwrap { - height: 100%; - background-color: white; - box-shadow: 0 0.1rem 0 rgba(0, 0, 0, 0.07) inset; - overflow-y: auto; - -webkit-overflow-scrolling: touch; } - -.md-search-result { - color: rgba(0, 0, 0, 0.87); - word-break: break-word; } - .md-search-result__meta { - padding: 0 1.6rem; - background-color: rgba(0, 0, 0, 0.07); - color: rgba(0, 0, 0, 0.54); - font-size: 1.28rem; - line-height: 3.6rem; } - .md-search-result__list { - margin: 0; - padding: 0; - border-top: 0.1rem solid rgba(0, 0, 0, 0.07); - list-style: none; } - .md-search-result__item { - box-shadow: 0 -0.1rem 0 rgba(0, 0, 0, 0.07); } - .md-search-result__link { - display: block; - transition: background 0.25s; - outline: 0; - overflow: hidden; } - .md-search-result__link[data-md-state="active"], .md-search-result__link:hover { - background-color: rgba(83, 109, 254, 0.1); } - .md-search-result__link[data-md-state="active"] .md-search-result__article::before, .md-search-result__link:hover .md-search-result__article::before { - opacity: 0.7; } - .md-search-result__link:last-child .md-search-result__teaser { - margin-bottom: 1.2rem; } - .md-search-result__article { - position: relative; - padding: 0 1.6rem; - overflow: auto; } - .md-search-result__article--document::before { - position: absolute; - left: 0; - margin: 0.2rem; - transition: opacity 0.25s; - color: rgba(0, 0, 0, 0.54); - content: "\E880"; } - [dir="rtl"] .md-search-result__article--document::before { - right: 0; - left: initial; } - .md-search-result__article--document .md-search-result__title { - margin: 1.1rem 0; - font-size: 1.6rem; - font-weight: 400; - line-height: 1.4; } - .md-search-result__title { - margin: 0.5em 0; - font-size: 1.28rem; - font-weight: 700; - line-height: 1.4; } - .md-search-result__teaser { - display: -webkit-box; - max-height: 3.3rem; - margin: 0.5em 0; - color: rgba(0, 0, 0, 0.54); - font-size: 1.28rem; - line-height: 1.4; - text-overflow: ellipsis; - overflow: hidden; - -webkit-line-clamp: 2; } - .md-search-result em { - font-style: normal; - font-weight: 700; - text-decoration: underline; } - -.md-sidebar { - position: absolute; - width: 24.2rem; - padding: 2.4rem 0; - overflow: hidden; } - .md-sidebar[data-md-state="lock"] { - position: fixed; - top: 4.8rem; } - .md-sidebar--secondary { - display: none; } - .md-sidebar__scrollwrap { - max-height: 100%; - margin: 0 0.4rem; - overflow-y: auto; - -webkit-backface-visibility: hidden; - backface-visibility: hidden; } - .md-sidebar__scrollwrap::-webkit-scrollbar { - width: 0.4rem; - height: 0.4rem; } - .md-sidebar__scrollwrap::-webkit-scrollbar-thumb { - background-color: rgba(0, 0, 0, 0.26); } - .md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #536dfe; } - -@-webkit-keyframes md-source__facts--done { - 0% { - height: 0; } - 100% { - height: 1.3rem; } } - -@keyframes md-source__facts--done { - 0% { - height: 0; } - 100% { - height: 1.3rem; } } - -@-webkit-keyframes md-source__fact--done { - 0% { - -webkit-transform: translateY(100%); - transform: translateY(100%); - opacity: 0; } - 50% { - opacity: 0; } - 100% { - -webkit-transform: translateY(0%); - transform: translateY(0%); - opacity: 1; } } - -@keyframes md-source__fact--done { - 0% { - -webkit-transform: translateY(100%); - transform: translateY(100%); - opacity: 0; } - 50% { - opacity: 0; } - 100% { - -webkit-transform: translateY(0%); - transform: translateY(0%); - opacity: 1; } } - -.md-source { - display: block; - padding-right: 1.2rem; - transition: opacity 0.25s; - font-size: 1.3rem; - line-height: 1.2; - white-space: nowrap; } - [dir="rtl"] .md-source { - padding-right: initial; - padding-left: 1.2rem; } - .md-source:hover { - opacity: 0.7; } - .md-source::after { - display: inline-block; - height: 4.8rem; - content: ""; - vertical-align: middle; } - .md-source__icon { - display: inline-block; - width: 4.8rem; - height: 4.8rem; - content: ""; - vertical-align: middle; } - .md-source__icon svg { - width: 2.4rem; - height: 2.4rem; - margin-top: 1.2rem; - margin-left: 1.2rem; } - [dir="rtl"] .md-source__icon svg { - margin-right: 1.2rem; - margin-left: initial; } - .md-source__icon + .md-source__repository { - margin-left: -4.4rem; - padding-left: 4rem; } - [dir="rtl"] .md-source__icon + .md-source__repository { - margin-right: -4.4rem; - margin-left: initial; - padding-right: 4rem; - padding-left: initial; } - .md-source__repository { - display: inline-block; - max-width: 100%; - margin-left: 1.2rem; - font-weight: 700; - text-overflow: ellipsis; - overflow: hidden; - vertical-align: middle; } - .md-source__facts { - margin: 0; - padding: 0; - font-size: 1.1rem; - font-weight: 700; - list-style-type: none; - opacity: 0.75; - overflow: hidden; } - [data-md-state="done"] .md-source__facts { - -webkit-animation: md-source__facts--done 0.25s ease-in; - animation: md-source__facts--done 0.25s ease-in; } - .md-source__fact { - float: left; } - [dir="rtl"] .md-source__fact { - float: right; } - [data-md-state="done"] .md-source__fact { - -webkit-animation: md-source__fact--done 0.4s ease-out; - animation: md-source__fact--done 0.4s ease-out; } - .md-source__fact::before { - margin: 0 0.2rem; - content: "\B7"; } - .md-source__fact:first-child::before { - display: none; } - -.md-source-file { - display: inline-block; - margin: 1em 0.5em 1em 0; - padding-right: 0.5rem; - border-radius: 0.2rem; - background-color: rgba(0, 0, 0, 0.07); - font-size: 1.28rem; - list-style-type: none; - cursor: pointer; - overflow: hidden; } - .md-source-file::before { - display: inline-block; - margin-right: 0.5rem; - padding: 0.5rem; - background-color: rgba(0, 0, 0, 0.26); - color: white; - font-size: 1.6rem; - content: "\E86F"; - vertical-align: middle; } - html .md-source-file { - transition: background 0.4s, color 0.4s, box-shadow 0.4s cubic-bezier(0.4, 0, 0.2, 1); } - html .md-source-file::before { - transition: inherit; } - html body .md-typeset .md-source-file { - color: rgba(0, 0, 0, 0.54); } - .md-source-file:hover { - box-shadow: 0 0 8px rgba(0, 0, 0, 0.18), 0 8px 16px rgba(0, 0, 0, 0.36); } - .md-source-file:hover::before { - background-color: #536dfe; } - -.md-tabs { - width: 100%; - transition: background 0.25s; - background-color: #3f51b5; - color: white; - overflow: auto; } - .md-tabs__list { - margin: 0; - margin-left: 0.4rem; - padding: 0; - list-style: none; - white-space: nowrap; } - .md-tabs__item { - display: inline-block; - height: 4.8rem; - padding-right: 1.2rem; - padding-left: 1.2rem; } - .md-tabs__link { - display: block; - margin-top: 1.6rem; - transition: opacity 0.25s, -webkit-transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1); - transition: transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.25s; - transition: transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.25s, -webkit-transform 0.4s cubic-bezier(0.1, 0.7, 0.1, 1); - font-size: 1.4rem; - opacity: 0.7; } - .md-tabs__link--active, .md-tabs__link:hover { - color: inherit; - opacity: 1; } - .md-tabs__item:nth-child(2) .md-tabs__link { - transition-delay: 0.02s; } - .md-tabs__item:nth-child(3) .md-tabs__link { - transition-delay: 0.04s; } - .md-tabs__item:nth-child(4) .md-tabs__link { - transition-delay: 0.06s; } - .md-tabs__item:nth-child(5) .md-tabs__link { - transition-delay: 0.08s; } - .md-tabs__item:nth-child(6) .md-tabs__link { - transition-delay: 0.1s; } - .md-tabs__item:nth-child(7) .md-tabs__link { - transition-delay: 0.12s; } - .md-tabs__item:nth-child(8) .md-tabs__link { - transition-delay: 0.14s; } - .md-tabs__item:nth-child(9) .md-tabs__link { - transition-delay: 0.16s; } - .md-tabs__item:nth-child(10) .md-tabs__link { - transition-delay: 0.18s; } - .md-tabs__item:nth-child(11) .md-tabs__link { - transition-delay: 0.2s; } - .md-tabs__item:nth-child(12) .md-tabs__link { - transition-delay: 0.22s; } - .md-tabs__item:nth-child(13) .md-tabs__link { - transition-delay: 0.24s; } - .md-tabs__item:nth-child(14) .md-tabs__link { - transition-delay: 0.26s; } - .md-tabs__item:nth-child(15) .md-tabs__link { - transition-delay: 0.28s; } - .md-tabs__item:nth-child(16) .md-tabs__link { - transition-delay: 0.3s; } - .md-tabs[data-md-state="hidden"] { - pointer-events: none; } - .md-tabs[data-md-state="hidden"] .md-tabs__link { - -webkit-transform: translateY(50%); - transform: translateY(50%); - transition: color 0.25s, opacity 0.1s, -webkit-transform 0s 0.4s; - transition: color 0.25s, transform 0s 0.4s, opacity 0.1s; - transition: color 0.25s, transform 0s 0.4s, opacity 0.1s, -webkit-transform 0s 0.4s; - opacity: 0; } - -.md-typeset .admonition, .md-typeset details { - box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2); - position: relative; - margin: 1.5625em 0; - padding: 0 1.2rem; - border-left: 0.4rem solid #448aff; - border-radius: 0.2rem; - font-size: 1.28rem; - overflow: auto; } - [dir="rtl"] .md-typeset .admonition, [dir="rtl"] .md-typeset details { - border-right: 0.4rem solid #448aff; - border-left: none; } - html .md-typeset .admonition > :last-child, html .md-typeset details > :last-child { - margin-bottom: 1.2rem; } - .md-typeset .admonition .admonition, .md-typeset details .admonition, .md-typeset .admonition details, .md-typeset details details { - margin: 1em 0; } - .md-typeset .admonition > .admonition-title, .md-typeset details > .admonition-title, .md-typeset .admonition > summary, .md-typeset details > summary { - margin: 0 -1.2rem; - padding: 0.8rem 1.2rem 0.8rem 4rem; - border-bottom: 0.1rem solid rgba(68, 138, 255, 0.1); - background-color: rgba(68, 138, 255, 0.1); - font-weight: 700; } - [dir="rtl"] .md-typeset .admonition > .admonition-title, [dir="rtl"] .md-typeset details > .admonition-title, [dir="rtl"] .md-typeset .admonition > summary, [dir="rtl"] .md-typeset details > summary { - padding: 0.8rem 4rem 0.8rem 1.2rem; } - .md-typeset .admonition > .admonition-title:last-child, .md-typeset details > .admonition-title:last-child, .md-typeset .admonition > summary:last-child, .md-typeset details > summary:last-child { - margin-bottom: 0; } - .md-typeset .admonition > .admonition-title::before, .md-typeset details > .admonition-title::before, .md-typeset .admonition > summary::before, .md-typeset details > summary::before { - position: absolute; - left: 1.2rem; - color: #448aff; - font-size: 2rem; - content: "\E3C9"; } - [dir="rtl"] .md-typeset .admonition > .admonition-title::before, [dir="rtl"] .md-typeset details > .admonition-title::before, [dir="rtl"] .md-typeset .admonition > summary::before, [dir="rtl"] .md-typeset details > summary::before { - right: 1.2rem; - left: initial; } - .md-typeset .admonition.summary, .md-typeset details.summary, .md-typeset .admonition.tldr, .md-typeset details.tldr, .md-typeset .admonition.abstract, .md-typeset details.abstract { - border-left-color: #00b0ff; } - [dir="rtl"] .md-typeset .admonition.summary, [dir="rtl"] .md-typeset details.summary, [dir="rtl"] .md-typeset .admonition.tldr, [dir="rtl"] .md-typeset details.tldr, [dir="rtl"] .md-typeset .admonition.abstract, [dir="rtl"] .md-typeset details.abstract { - border-right-color: #00b0ff; } - .md-typeset .admonition.summary > .admonition-title, .md-typeset details.summary > .admonition-title, .md-typeset .admonition.tldr > .admonition-title, .md-typeset details.tldr > .admonition-title, .md-typeset .admonition.summary > summary, .md-typeset details.summary > summary, .md-typeset .admonition.tldr > summary, .md-typeset details.tldr > summary, .md-typeset .admonition.abstract > .admonition-title, .md-typeset details.abstract > .admonition-title, .md-typeset .admonition.abstract > summary, .md-typeset details.abstract > summary { - border-bottom-color: 0.1rem solid rgba(0, 176, 255, 0.1); - background-color: rgba(0, 176, 255, 0.1); } - .md-typeset .admonition.summary > .admonition-title::before, .md-typeset details.summary > .admonition-title::before, .md-typeset .admonition.tldr > .admonition-title::before, .md-typeset details.tldr > .admonition-title::before, .md-typeset .admonition.summary > summary::before, .md-typeset details.summary > summary::before, .md-typeset .admonition.tldr > summary::before, .md-typeset details.tldr > summary::before, .md-typeset .admonition.abstract > .admonition-title::before, .md-typeset details.abstract > .admonition-title::before, .md-typeset .admonition.abstract > summary::before, .md-typeset details.abstract > summary::before { - color: #00b0ff; - content: "\E8D2"; } - .md-typeset .admonition.todo, .md-typeset details.todo, .md-typeset .admonition.info, .md-typeset details.info { - border-left-color: #00b8d4; } - [dir="rtl"] .md-typeset .admonition.todo, [dir="rtl"] .md-typeset details.todo, [dir="rtl"] .md-typeset .admonition.info, [dir="rtl"] .md-typeset details.info { - border-right-color: #00b8d4; } - .md-typeset .admonition.todo > .admonition-title, .md-typeset details.todo > .admonition-title, .md-typeset .admonition.todo > summary, .md-typeset details.todo > summary, .md-typeset .admonition.info > .admonition-title, .md-typeset details.info > .admonition-title, .md-typeset .admonition.info > summary, .md-typeset details.info > summary { - border-bottom-color: 0.1rem solid rgba(0, 184, 212, 0.1); - background-color: rgba(0, 184, 212, 0.1); } - .md-typeset .admonition.todo > .admonition-title::before, .md-typeset details.todo > .admonition-title::before, .md-typeset .admonition.todo > summary::before, .md-typeset details.todo > summary::before, .md-typeset .admonition.info > .admonition-title::before, .md-typeset details.info > .admonition-title::before, .md-typeset .admonition.info > summary::before, .md-typeset details.info > summary::before { - color: #00b8d4; - content: "\E88E"; } - .md-typeset .admonition.hint, .md-typeset details.hint, .md-typeset .admonition.important, .md-typeset details.important, .md-typeset .admonition.tip, .md-typeset details.tip { - border-left-color: #00bfa5; } - [dir="rtl"] .md-typeset .admonition.hint, [dir="rtl"] .md-typeset details.hint, [dir="rtl"] .md-typeset .admonition.important, [dir="rtl"] .md-typeset details.important, [dir="rtl"] .md-typeset .admonition.tip, [dir="rtl"] .md-typeset details.tip { - border-right-color: #00bfa5; } - .md-typeset .admonition.hint > .admonition-title, .md-typeset details.hint > .admonition-title, .md-typeset .admonition.important > .admonition-title, .md-typeset details.important > .admonition-title, .md-typeset .admonition.hint > summary, .md-typeset details.hint > summary, .md-typeset .admonition.important > summary, .md-typeset details.important > summary, .md-typeset .admonition.tip > .admonition-title, .md-typeset details.tip > .admonition-title, .md-typeset .admonition.tip > summary, .md-typeset details.tip > summary { - border-bottom-color: 0.1rem solid rgba(0, 191, 165, 0.1); - background-color: rgba(0, 191, 165, 0.1); } - .md-typeset .admonition.hint > .admonition-title::before, .md-typeset details.hint > .admonition-title::before, .md-typeset .admonition.important > .admonition-title::before, .md-typeset details.important > .admonition-title::before, .md-typeset .admonition.hint > summary::before, .md-typeset details.hint > summary::before, .md-typeset .admonition.important > summary::before, .md-typeset details.important > summary::before, .md-typeset .admonition.tip > .admonition-title::before, .md-typeset details.tip > .admonition-title::before, .md-typeset .admonition.tip > summary::before, .md-typeset details.tip > summary::before { - color: #00bfa5; - content: "\E80E"; } - .md-typeset .admonition.check, .md-typeset details.check, .md-typeset .admonition.done, .md-typeset details.done, .md-typeset .admonition.success, .md-typeset details.success { - border-left-color: #00c853; } - [dir="rtl"] .md-typeset .admonition.check, [dir="rtl"] .md-typeset details.check, [dir="rtl"] .md-typeset .admonition.done, [dir="rtl"] .md-typeset details.done, [dir="rtl"] .md-typeset .admonition.success, [dir="rtl"] .md-typeset details.success { - border-right-color: #00c853; } - .md-typeset .admonition.check > .admonition-title, .md-typeset details.check > .admonition-title, .md-typeset .admonition.done > .admonition-title, .md-typeset details.done > .admonition-title, .md-typeset .admonition.check > summary, .md-typeset details.check > summary, .md-typeset .admonition.done > summary, .md-typeset details.done > summary, .md-typeset .admonition.success > .admonition-title, .md-typeset details.success > .admonition-title, .md-typeset .admonition.success > summary, .md-typeset details.success > summary { - border-bottom-color: 0.1rem solid rgba(0, 200, 83, 0.1); - background-color: rgba(0, 200, 83, 0.1); } - .md-typeset .admonition.check > .admonition-title::before, .md-typeset details.check > .admonition-title::before, .md-typeset .admonition.done > .admonition-title::before, .md-typeset details.done > .admonition-title::before, .md-typeset .admonition.check > summary::before, .md-typeset details.check > summary::before, .md-typeset .admonition.done > summary::before, .md-typeset details.done > summary::before, .md-typeset .admonition.success > .admonition-title::before, .md-typeset details.success > .admonition-title::before, .md-typeset .admonition.success > summary::before, .md-typeset details.success > summary::before { - color: #00c853; - content: "\E876"; } - .md-typeset .admonition.help, .md-typeset details.help, .md-typeset .admonition.faq, .md-typeset details.faq, .md-typeset .admonition.question, .md-typeset details.question { - border-left-color: #64dd17; } - [dir="rtl"] .md-typeset .admonition.help, [dir="rtl"] .md-typeset details.help, [dir="rtl"] .md-typeset .admonition.faq, [dir="rtl"] .md-typeset details.faq, [dir="rtl"] .md-typeset .admonition.question, [dir="rtl"] .md-typeset details.question { - border-right-color: #64dd17; } - .md-typeset .admonition.help > .admonition-title, .md-typeset details.help > .admonition-title, .md-typeset .admonition.faq > .admonition-title, .md-typeset details.faq > .admonition-title, .md-typeset .admonition.help > summary, .md-typeset details.help > summary, .md-typeset .admonition.faq > summary, .md-typeset details.faq > summary, .md-typeset .admonition.question > .admonition-title, .md-typeset details.question > .admonition-title, .md-typeset .admonition.question > summary, .md-typeset details.question > summary { - border-bottom-color: 0.1rem solid rgba(100, 221, 23, 0.1); - background-color: rgba(100, 221, 23, 0.1); } - .md-typeset .admonition.help > .admonition-title::before, .md-typeset details.help > .admonition-title::before, .md-typeset .admonition.faq > .admonition-title::before, .md-typeset details.faq > .admonition-title::before, .md-typeset .admonition.help > summary::before, .md-typeset details.help > summary::before, .md-typeset .admonition.faq > summary::before, .md-typeset details.faq > summary::before, .md-typeset .admonition.question > .admonition-title::before, .md-typeset details.question > .admonition-title::before, .md-typeset .admonition.question > summary::before, .md-typeset details.question > summary::before { - color: #64dd17; - content: "\E887"; } - .md-typeset .admonition.caution, .md-typeset details.caution, .md-typeset .admonition.attention, .md-typeset details.attention, .md-typeset .admonition.warning, .md-typeset details.warning { - border-left-color: #ff9100; } - [dir="rtl"] .md-typeset .admonition.caution, [dir="rtl"] .md-typeset details.caution, [dir="rtl"] .md-typeset .admonition.attention, [dir="rtl"] .md-typeset details.attention, [dir="rtl"] .md-typeset .admonition.warning, [dir="rtl"] .md-typeset details.warning { - border-right-color: #ff9100; } - .md-typeset .admonition.caution > .admonition-title, .md-typeset details.caution > .admonition-title, .md-typeset .admonition.attention > .admonition-title, .md-typeset details.attention > .admonition-title, .md-typeset .admonition.caution > summary, .md-typeset details.caution > summary, .md-typeset .admonition.attention > summary, .md-typeset details.attention > summary, .md-typeset .admonition.warning > .admonition-title, .md-typeset details.warning > .admonition-title, .md-typeset .admonition.warning > summary, .md-typeset details.warning > summary { - border-bottom-color: 0.1rem solid rgba(255, 145, 0, 0.1); - background-color: rgba(255, 145, 0, 0.1); } - .md-typeset .admonition.caution > .admonition-title::before, .md-typeset details.caution > .admonition-title::before, .md-typeset .admonition.attention > .admonition-title::before, .md-typeset details.attention > .admonition-title::before, .md-typeset .admonition.caution > summary::before, .md-typeset details.caution > summary::before, .md-typeset .admonition.attention > summary::before, .md-typeset details.attention > summary::before, .md-typeset .admonition.warning > .admonition-title::before, .md-typeset details.warning > .admonition-title::before, .md-typeset .admonition.warning > summary::before, .md-typeset details.warning > summary::before { - color: #ff9100; - content: "\E002"; } - .md-typeset .admonition.fail, .md-typeset details.fail, .md-typeset .admonition.missing, .md-typeset details.missing, .md-typeset .admonition.failure, .md-typeset details.failure { - border-left-color: #ff5252; } - [dir="rtl"] .md-typeset .admonition.fail, [dir="rtl"] .md-typeset details.fail, [dir="rtl"] .md-typeset .admonition.missing, [dir="rtl"] .md-typeset details.missing, [dir="rtl"] .md-typeset .admonition.failure, [dir="rtl"] .md-typeset details.failure { - border-right-color: #ff5252; } - .md-typeset .admonition.fail > .admonition-title, .md-typeset details.fail > .admonition-title, .md-typeset .admonition.missing > .admonition-title, .md-typeset details.missing > .admonition-title, .md-typeset .admonition.fail > summary, .md-typeset details.fail > summary, .md-typeset .admonition.missing > summary, .md-typeset details.missing > summary, .md-typeset .admonition.failure > .admonition-title, .md-typeset details.failure > .admonition-title, .md-typeset .admonition.failure > summary, .md-typeset details.failure > summary { - border-bottom-color: 0.1rem solid rgba(255, 82, 82, 0.1); - background-color: rgba(255, 82, 82, 0.1); } - .md-typeset .admonition.fail > .admonition-title::before, .md-typeset details.fail > .admonition-title::before, .md-typeset .admonition.missing > .admonition-title::before, .md-typeset details.missing > .admonition-title::before, .md-typeset .admonition.fail > summary::before, .md-typeset details.fail > summary::before, .md-typeset .admonition.missing > summary::before, .md-typeset details.missing > summary::before, .md-typeset .admonition.failure > .admonition-title::before, .md-typeset details.failure > .admonition-title::before, .md-typeset .admonition.failure > summary::before, .md-typeset details.failure > summary::before { - color: #ff5252; - content: "\E14C"; } - .md-typeset .admonition.error, .md-typeset details.error, .md-typeset .admonition.danger, .md-typeset details.danger { - border-left-color: #ff1744; } - [dir="rtl"] .md-typeset .admonition.error, [dir="rtl"] .md-typeset details.error, [dir="rtl"] .md-typeset .admonition.danger, [dir="rtl"] .md-typeset details.danger { - border-right-color: #ff1744; } - .md-typeset .admonition.error > .admonition-title, .md-typeset details.error > .admonition-title, .md-typeset .admonition.error > summary, .md-typeset details.error > summary, .md-typeset .admonition.danger > .admonition-title, .md-typeset details.danger > .admonition-title, .md-typeset .admonition.danger > summary, .md-typeset details.danger > summary { - border-bottom-color: 0.1rem solid rgba(255, 23, 68, 0.1); - background-color: rgba(255, 23, 68, 0.1); } - .md-typeset .admonition.error > .admonition-title::before, .md-typeset details.error > .admonition-title::before, .md-typeset .admonition.error > summary::before, .md-typeset details.error > summary::before, .md-typeset .admonition.danger > .admonition-title::before, .md-typeset details.danger > .admonition-title::before, .md-typeset .admonition.danger > summary::before, .md-typeset details.danger > summary::before { - color: #ff1744; - content: "\E3E7"; } - .md-typeset .admonition.bug, .md-typeset details.bug { - border-left-color: #f50057; } - [dir="rtl"] .md-typeset .admonition.bug, [dir="rtl"] .md-typeset details.bug { - border-right-color: #f50057; } - .md-typeset .admonition.bug > .admonition-title, .md-typeset details.bug > .admonition-title, .md-typeset .admonition.bug > summary, .md-typeset details.bug > summary { - border-bottom-color: 0.1rem solid rgba(245, 0, 87, 0.1); - background-color: rgba(245, 0, 87, 0.1); } - .md-typeset .admonition.bug > .admonition-title::before, .md-typeset details.bug > .admonition-title::before, .md-typeset .admonition.bug > summary::before, .md-typeset details.bug > summary::before { - color: #f50057; - content: "\E868"; } - .md-typeset .admonition.example, .md-typeset details.example { - border-left-color: #651fff; } - [dir="rtl"] .md-typeset .admonition.example, [dir="rtl"] .md-typeset details.example { - border-right-color: #651fff; } - .md-typeset .admonition.example > .admonition-title, .md-typeset details.example > .admonition-title, .md-typeset .admonition.example > summary, .md-typeset details.example > summary { - border-bottom-color: 0.1rem solid rgba(101, 31, 255, 0.1); - background-color: rgba(101, 31, 255, 0.1); } - .md-typeset .admonition.example > .admonition-title::before, .md-typeset details.example > .admonition-title::before, .md-typeset .admonition.example > summary::before, .md-typeset details.example > summary::before { - color: #651fff; - content: "\E242"; } - .md-typeset .admonition.cite, .md-typeset details.cite, .md-typeset .admonition.quote, .md-typeset details.quote { - border-left-color: #9e9e9e; } - [dir="rtl"] .md-typeset .admonition.cite, [dir="rtl"] .md-typeset details.cite, [dir="rtl"] .md-typeset .admonition.quote, [dir="rtl"] .md-typeset details.quote { - border-right-color: #9e9e9e; } - .md-typeset .admonition.cite > .admonition-title, .md-typeset details.cite > .admonition-title, .md-typeset .admonition.cite > summary, .md-typeset details.cite > summary, .md-typeset .admonition.quote > .admonition-title, .md-typeset details.quote > .admonition-title, .md-typeset .admonition.quote > summary, .md-typeset details.quote > summary { - border-bottom-color: 0.1rem solid rgba(158, 158, 158, 0.1); - background-color: rgba(158, 158, 158, 0.1); } - .md-typeset .admonition.cite > .admonition-title::before, .md-typeset details.cite > .admonition-title::before, .md-typeset .admonition.cite > summary::before, .md-typeset details.cite > summary::before, .md-typeset .admonition.quote > .admonition-title::before, .md-typeset details.quote > .admonition-title::before, .md-typeset .admonition.quote > summary::before, .md-typeset details.quote > summary::before { - color: #9e9e9e; - content: "\E244"; } - -.codehilite .o, .md-typeset .highlight .o { - color: inherit; } - -.codehilite .ow, .md-typeset .highlight .ow { - color: inherit; } - -.codehilite .ge, .md-typeset .highlight .ge { - color: #000000; } - -.codehilite .gr, .md-typeset .highlight .gr { - color: #AA0000; } - -.codehilite .gh, .md-typeset .highlight .gh { - color: #999999; } - -.codehilite .go, .md-typeset .highlight .go { - color: #888888; } - -.codehilite .gp, .md-typeset .highlight .gp { - color: #555555; } - -.codehilite .gs, .md-typeset .highlight .gs { - color: inherit; } - -.codehilite .gu, .md-typeset .highlight .gu { - color: #AAAAAA; } - -.codehilite .gt, .md-typeset .highlight .gt { - color: #AA0000; } - -.codehilite .gd, .md-typeset .highlight .gd { - background-color: #FFDDDD; } - -.codehilite .gi, .md-typeset .highlight .gi { - background-color: #DDFFDD; } - -.codehilite .k, .md-typeset .highlight .k { - color: #3B78E7; } - -.codehilite .kc, .md-typeset .highlight .kc { - color: #A71D5D; } - -.codehilite .kd, .md-typeset .highlight .kd { - color: #3B78E7; } - -.codehilite .kn, .md-typeset .highlight .kn { - color: #3B78E7; } - -.codehilite .kp, .md-typeset .highlight .kp { - color: #A71D5D; } - -.codehilite .kr, .md-typeset .highlight .kr { - color: #3E61A2; } - -.codehilite .kt, .md-typeset .highlight .kt { - color: #3E61A2; } - -.codehilite .c, .md-typeset .highlight .c { - color: #999999; } - -.codehilite .cm, .md-typeset .highlight .cm { - color: #999999; } - -.codehilite .cp, .md-typeset .highlight .cp { - color: #666666; } - -.codehilite .c1, .md-typeset .highlight .c1 { - color: #999999; } - -.codehilite .ch, .md-typeset .highlight .ch { - color: #999999; } - -.codehilite .cs, .md-typeset .highlight .cs { - color: #999999; } - -.codehilite .na, .md-typeset .highlight .na { - color: #C2185B; } - -.codehilite .nb, .md-typeset .highlight .nb { - color: #C2185B; } - -.codehilite .bp, .md-typeset .highlight .bp { - color: #3E61A2; } - -.codehilite .nc, .md-typeset .highlight .nc { - color: #C2185B; } - -.codehilite .no, .md-typeset .highlight .no { - color: #3E61A2; } - -.codehilite .nd, .md-typeset .highlight .nd { - color: #666666; } - -.codehilite .ni, .md-typeset .highlight .ni { - color: #666666; } - -.codehilite .ne, .md-typeset .highlight .ne { - color: #C2185B; } - -.codehilite .nf, .md-typeset .highlight .nf { - color: #C2185B; } - -.codehilite .nl, .md-typeset .highlight .nl { - color: #3B5179; } - -.codehilite .nn, .md-typeset .highlight .nn { - color: #EC407A; } - -.codehilite .nt, .md-typeset .highlight .nt { - color: #3B78E7; } - -.codehilite .nv, .md-typeset .highlight .nv { - color: #3E61A2; } - -.codehilite .vc, .md-typeset .highlight .vc { - color: #3E61A2; } - -.codehilite .vg, .md-typeset .highlight .vg { - color: #3E61A2; } - -.codehilite .vi, .md-typeset .highlight .vi { - color: #3E61A2; } - -.codehilite .nx, .md-typeset .highlight .nx { - color: #EC407A; } - -.codehilite .m, .md-typeset .highlight .m { - color: #E74C3C; } - -.codehilite .mf, .md-typeset .highlight .mf { - color: #E74C3C; } - -.codehilite .mh, .md-typeset .highlight .mh { - color: #E74C3C; } - -.codehilite .mi, .md-typeset .highlight .mi { - color: #E74C3C; } - -.codehilite .il, .md-typeset .highlight .il { - color: #E74C3C; } - -.codehilite .mo, .md-typeset .highlight .mo { - color: #E74C3C; } - -.codehilite .s, .md-typeset .highlight .s { - color: #0D904F; } - -.codehilite .sb, .md-typeset .highlight .sb { - color: #0D904F; } - -.codehilite .sc, .md-typeset .highlight .sc { - color: #0D904F; } - -.codehilite .sd, .md-typeset .highlight .sd { - color: #999999; } - -.codehilite .s2, .md-typeset .highlight .s2 { - color: #0D904F; } - -.codehilite .se, .md-typeset .highlight .se { - color: #183691; } - -.codehilite .sh, .md-typeset .highlight .sh { - color: #183691; } - -.codehilite .si, .md-typeset .highlight .si { - color: #183691; } - -.codehilite .sx, .md-typeset .highlight .sx { - color: #183691; } - -.codehilite .sr, .md-typeset .highlight .sr { - color: #009926; } - -.codehilite .s1, .md-typeset .highlight .s1 { - color: #0D904F; } - -.codehilite .ss, .md-typeset .highlight .ss { - color: #0D904F; } - -.codehilite .err, .md-typeset .highlight .err { - color: #A61717; } - -.codehilite .w, .md-typeset .highlight .w { - color: transparent; } - -.codehilite .hll, .md-typeset .highlight .hll { - display: block; - margin: 0 -1.2rem; - padding: 0 1.2rem; - background-color: rgba(255, 235, 59, 0.5); } - -.md-typeset .codehilite, .md-typeset .highlight { - position: relative; - margin: 1em 0; - padding: 0; - border-radius: 0.2rem; - background-color: rgba(236, 236, 236, 0.5); - color: #37474F; - line-height: 1.4; - -webkit-overflow-scrolling: touch; } - .md-typeset .codehilite pre, .md-typeset .highlight pre, - .md-typeset .codehilite code, .md-typeset .highlight code { - display: block; - margin: 0; - padding: 1.05rem 1.2rem; - background-color: transparent; - overflow: auto; - vertical-align: top; } - .md-typeset .codehilite pre::-webkit-scrollbar, .md-typeset .highlight pre::-webkit-scrollbar, - .md-typeset .codehilite code::-webkit-scrollbar, .md-typeset .highlight code::-webkit-scrollbar { - width: 0.4rem; - height: 0.4rem; } - .md-typeset .codehilite pre::-webkit-scrollbar-thumb, .md-typeset .highlight pre::-webkit-scrollbar-thumb, - .md-typeset .codehilite code::-webkit-scrollbar-thumb, .md-typeset .highlight code::-webkit-scrollbar-thumb { - background-color: rgba(0, 0, 0, 0.26); } - .md-typeset .codehilite pre::-webkit-scrollbar-thumb:hover, .md-typeset .highlight pre::-webkit-scrollbar-thumb:hover, - .md-typeset .codehilite code::-webkit-scrollbar-thumb:hover, .md-typeset .highlight code::-webkit-scrollbar-thumb:hover { - background-color: #536dfe; } - -.md-typeset pre.codehilite, .md-typeset pre.highlight { - overflow: visible; } - .md-typeset pre.codehilite code, .md-typeset pre.highlight code { - display: block; - padding: 1.05rem 1.2rem; - overflow: auto; } - -.md-typeset .codehilitetable, .md-typeset .highlighttable { - display: block; - margin: 1em 0; - border-radius: 0.2em; - font-size: 1.6rem; - overflow: hidden; } - .md-typeset .codehilitetable tbody, .md-typeset .highlighttable tbody, - .md-typeset .codehilitetable td, .md-typeset .highlighttable td { - display: block; - padding: 0; } - .md-typeset .codehilitetable tr, .md-typeset .highlighttable tr { - display: flex; } - .md-typeset .codehilitetable .codehilite, .md-typeset .highlighttable .codehilite, .md-typeset .codehilitetable .highlight, .md-typeset .highlighttable .highlight, - .md-typeset .codehilitetable .linenodiv, .md-typeset .highlighttable .linenodiv { - margin: 0; - border-radius: 0; } - .md-typeset .codehilitetable .linenodiv, .md-typeset .highlighttable .linenodiv { - padding: 1.05rem 1.2rem; } - .md-typeset .codehilitetable .linenos, .md-typeset .highlighttable .linenos { - background-color: rgba(0, 0, 0, 0.07); - color: rgba(0, 0, 0, 0.26); - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; } - .md-typeset .codehilitetable .linenos pre, .md-typeset .highlighttable .linenos pre { - margin: 0; - padding: 0; - background-color: transparent; - color: inherit; - text-align: right; } - .md-typeset .codehilitetable .code, .md-typeset .highlighttable .code { - flex: 1; - overflow: hidden; } - -.md-typeset > .codehilitetable, .md-typeset > .highlighttable { - box-shadow: none; } - -.md-typeset [id^="fnref:"] { - display: inline-block; } - .md-typeset [id^="fnref:"]:target { - margin-top: -7.6rem; - padding-top: 7.6rem; - pointer-events: none; } - -.md-typeset [id^="fn:"]::before { - display: none; - height: 0; - content: ""; } - -.md-typeset [id^="fn:"]:target::before { - display: block; - margin-top: -7rem; - padding-top: 7rem; - pointer-events: none; } - -.md-typeset .footnote { - color: rgba(0, 0, 0, 0.54); - font-size: 1.28rem; } - .md-typeset .footnote ol { - margin-left: 0; } - .md-typeset .footnote li { - transition: color 0.25s; } - .md-typeset .footnote li:target { - color: rgba(0, 0, 0, 0.87); } - .md-typeset .footnote li :first-child { - margin-top: 0; } - .md-typeset .footnote li:hover .footnote-backref, - .md-typeset .footnote li:target .footnote-backref { - -webkit-transform: translateX(0); - transform: translateX(0); - opacity: 1; } - .md-typeset .footnote li:hover .footnote-backref:hover, - .md-typeset .footnote li:target .footnote-backref { - color: #536dfe; } - -.md-typeset .footnote-ref { - display: inline-block; - pointer-events: initial; } - .md-typeset .footnote-ref::before { - display: inline; - margin: 0 0.2em; - border-left: 0.1rem solid rgba(0, 0, 0, 0.26); - font-size: 1.25em; - content: ""; - vertical-align: -0.5rem; } - -.md-typeset .footnote-backref { - display: inline-block; - -webkit-transform: translateX(0.5rem); - transform: translateX(0.5rem); - transition: color 0.25s, opacity 0.125s 0.125s, -webkit-transform 0.25s 0.125s; - transition: transform 0.25s 0.125s, color 0.25s, opacity 0.125s 0.125s; - transition: transform 0.25s 0.125s, color 0.25s, opacity 0.125s 0.125s, -webkit-transform 0.25s 0.125s; - color: rgba(0, 0, 0, 0.26); - font-size: 0; - opacity: 0; - vertical-align: text-bottom; } - [dir="rtl"] .md-typeset .footnote-backref { - -webkit-transform: translateX(-0.5rem); - transform: translateX(-0.5rem); } - .md-typeset .footnote-backref::before { - display: inline-block; - font-size: 1.6rem; - content: "\E31B"; } - [dir="rtl"] .md-typeset .footnote-backref::before { - -webkit-transform: scaleX(-1); - transform: scaleX(-1); } - -.md-typeset .headerlink { - display: inline-block; - margin-left: 1rem; - -webkit-transform: translate(0, 0.5rem); - transform: translate(0, 0.5rem); - transition: color 0.25s, opacity 0.125s 0.25s, -webkit-transform 0.25s 0.25s; - transition: transform 0.25s 0.25s, color 0.25s, opacity 0.125s 0.25s; - transition: transform 0.25s 0.25s, color 0.25s, opacity 0.125s 0.25s, -webkit-transform 0.25s 0.25s; - opacity: 0; } - [dir="rtl"] .md-typeset .headerlink { - margin-right: 1rem; - margin-left: initial; } - html body .md-typeset .headerlink { - color: rgba(0, 0, 0, 0.26); } - -.md-typeset h1[id]::before { - display: block; - margin-top: -0.9rem; - padding-top: 0.9rem; - content: ""; } - -.md-typeset h1[id]:target::before { - margin-top: -6.9rem; - padding-top: 6.9rem; } - -.md-typeset h1[id]:hover .headerlink, -.md-typeset h1[id]:target .headerlink, -.md-typeset h1[id] .headerlink:focus { - -webkit-transform: translate(0, 0); - transform: translate(0, 0); - opacity: 1; } - -.md-typeset h1[id]:hover .headerlink:hover, -.md-typeset h1[id]:target .headerlink, -.md-typeset h1[id] .headerlink:focus { - color: #536dfe; } - -.md-typeset h2[id]::before { - display: block; - margin-top: -0.8rem; - padding-top: 0.8rem; - content: ""; } - -.md-typeset h2[id]:target::before { - margin-top: -6.8rem; - padding-top: 6.8rem; } - -.md-typeset h2[id]:hover .headerlink, -.md-typeset h2[id]:target .headerlink, -.md-typeset h2[id] .headerlink:focus { - -webkit-transform: translate(0, 0); - transform: translate(0, 0); - opacity: 1; } - -.md-typeset h2[id]:hover .headerlink:hover, -.md-typeset h2[id]:target .headerlink, -.md-typeset h2[id] .headerlink:focus { - color: #536dfe; } - -.md-typeset h3[id]::before { - display: block; - margin-top: -0.9rem; - padding-top: 0.9rem; - content: ""; } - -.md-typeset h3[id]:target::before { - margin-top: -6.9rem; - padding-top: 6.9rem; } - -.md-typeset h3[id]:hover .headerlink, -.md-typeset h3[id]:target .headerlink, -.md-typeset h3[id] .headerlink:focus { - -webkit-transform: translate(0, 0); - transform: translate(0, 0); - opacity: 1; } - -.md-typeset h3[id]:hover .headerlink:hover, -.md-typeset h3[id]:target .headerlink, -.md-typeset h3[id] .headerlink:focus { - color: #536dfe; } - -.md-typeset h4[id]::before { - display: block; - margin-top: -0.9rem; - padding-top: 0.9rem; - content: ""; } - -.md-typeset h4[id]:target::before { - margin-top: -6.9rem; - padding-top: 6.9rem; } - -.md-typeset h4[id]:hover .headerlink, -.md-typeset h4[id]:target .headerlink, -.md-typeset h4[id] .headerlink:focus { - -webkit-transform: translate(0, 0); - transform: translate(0, 0); - opacity: 1; } - -.md-typeset h4[id]:hover .headerlink:hover, -.md-typeset h4[id]:target .headerlink, -.md-typeset h4[id] .headerlink:focus { - color: #536dfe; } - -.md-typeset h5[id]::before { - display: block; - margin-top: -1.1rem; - padding-top: 1.1rem; - content: ""; } - -.md-typeset h5[id]:target::before { - margin-top: -7.1rem; - padding-top: 7.1rem; } - -.md-typeset h5[id]:hover .headerlink, -.md-typeset h5[id]:target .headerlink, -.md-typeset h5[id] .headerlink:focus { - -webkit-transform: translate(0, 0); - transform: translate(0, 0); - opacity: 1; } - -.md-typeset h5[id]:hover .headerlink:hover, -.md-typeset h5[id]:target .headerlink, -.md-typeset h5[id] .headerlink:focus { - color: #536dfe; } - -.md-typeset h6[id]::before { - display: block; - margin-top: -1.1rem; - padding-top: 1.1rem; - content: ""; } - -.md-typeset h6[id]:target::before { - margin-top: -7.1rem; - padding-top: 7.1rem; } - -.md-typeset h6[id]:hover .headerlink, -.md-typeset h6[id]:target .headerlink, -.md-typeset h6[id] .headerlink:focus { - -webkit-transform: translate(0, 0); - transform: translate(0, 0); - opacity: 1; } - -.md-typeset h6[id]:hover .headerlink:hover, -.md-typeset h6[id]:target .headerlink, -.md-typeset h6[id] .headerlink:focus { - color: #536dfe; } - -.md-typeset .MJXc-display { - margin: 0.75em 0; - padding: 0.75em 0; - overflow: auto; - -webkit-overflow-scrolling: touch; } - -.md-typeset .MathJax_CHTML { - outline: 0; } - -.md-typeset del.critic, -.md-typeset ins.critic, -.md-typeset .critic.comment { - margin: 0 0.25em; - padding: 0.0625em 0; - border-radius: 0.2rem; - -webkit-box-decoration-break: clone; - box-decoration-break: clone; } - -.md-typeset del.critic { - background-color: #FFDDDD; - box-shadow: 0.25em 0 0 #FFDDDD, -0.25em 0 0 #FFDDDD; } - -.md-typeset ins.critic { - background-color: #DDFFDD; - box-shadow: 0.25em 0 0 #DDFFDD, -0.25em 0 0 #DDFFDD; } - -.md-typeset .critic.comment { - background-color: rgba(236, 236, 236, 0.5); - color: #37474F; - box-shadow: 0.25em 0 0 rgba(236, 236, 236, 0.5), -0.25em 0 0 rgba(236, 236, 236, 0.5); } - .md-typeset .critic.comment::before { - padding-right: 0.125em; - color: rgba(0, 0, 0, 0.26); - content: "\E0B7"; - vertical-align: -0.125em; } - -.md-typeset .critic.block { - display: block; - margin: 1em 0; - padding-right: 1.6rem; - padding-left: 1.6rem; - box-shadow: none; } - .md-typeset .critic.block :first-child { - margin-top: 0.5em; } - .md-typeset .critic.block :last-child { - margin-bottom: 0.5em; } - -.md-typeset details { - display: block; - padding-top: 0; } - .md-typeset details[open] > summary::after { - -webkit-transform: rotate(180deg); - transform: rotate(180deg); } - .md-typeset details:not([open]) { - padding-bottom: 0; } - .md-typeset details:not([open]) > summary { - border-bottom: none; } - .md-typeset details summary { - padding-right: 4rem; } - [dir="rtl"] .md-typeset details summary { - padding-left: 4rem; } - .no-details .md-typeset details:not([open]) > * { - display: none; } - .no-details .md-typeset details:not([open]) summary { - display: block; } - -.md-typeset summary { - display: block; - outline: none; - cursor: pointer; } - .md-typeset summary::-webkit-details-marker { - display: none; } - .md-typeset summary::after { - position: absolute; - top: 0.8rem; - right: 1.2rem; - color: rgba(0, 0, 0, 0.26); - font-size: 2rem; - content: "\E313"; } - [dir="rtl"] .md-typeset summary::after { - right: initial; - left: 1.2rem; } - -.md-typeset .emojione { - width: 2rem; - vertical-align: text-top; } - -.md-typeset code.codehilite, .md-typeset code.highlight { - margin: 0 0.29412em; - padding: 0.07353em 0; } - -.md-typeset .superfences-content { - display: none; - order: 99; - width: 100%; - background-color: white; } - .md-typeset .superfences-content > * { - margin: 0; - border-radius: 0; } - -.md-typeset .superfences-tabs { - display: flex; - position: relative; - flex-wrap: wrap; - margin: 1em 0; - border: 0.1rem solid rgba(0, 0, 0, 0.07); - border-radius: 0.2em; } - .md-typeset .superfences-tabs > input { - display: none; } - .md-typeset .superfences-tabs > input:checked + label { - font-weight: 700; } - .md-typeset .superfences-tabs > input:checked + label + .superfences-content { - display: block; } - .md-typeset .superfences-tabs > label { - width: auto; - padding: 1.2rem 1.2rem; - transition: color 0.125s; - font-size: 1.28rem; - cursor: pointer; } - html .md-typeset .superfences-tabs > label:hover { - color: #536dfe; } - -.md-typeset .task-list-item { - position: relative; - list-style-type: none; } - .md-typeset .task-list-item [type="checkbox"] { - position: absolute; - top: 0.45em; - left: -2em; } - [dir="rtl"] .md-typeset .task-list-item [type="checkbox"] { - right: -2em; - left: initial; } - -.md-typeset .task-list-control .task-list-indicator::before { - position: absolute; - top: 0.15em; - left: -1.25em; - color: rgba(0, 0, 0, 0.26); - font-size: 1.25em; - content: "\E835"; - vertical-align: -0.25em; } - [dir="rtl"] .md-typeset .task-list-control .task-list-indicator::before { - right: -1.25em; - left: initial; } - -.md-typeset .task-list-control [type="checkbox"]:checked + .task-list-indicator::before { - content: "\E834"; } - -.md-typeset .task-list-control [type="checkbox"] { - opacity: 0; - z-index: -1; } - -@media print { - .md-typeset a::after { - color: rgba(0, 0, 0, 0.54); - content: " [" attr(href) "]"; } - .md-typeset code, - .md-typeset pre { - white-space: pre-wrap; } - .md-typeset code { - box-shadow: none; - -webkit-box-decoration-break: initial; - box-decoration-break: initial; } - .md-clipboard { - display: none; } - .md-content__icon { - display: none; } - .md-header { - display: none; } - .md-footer { - display: none; } - .md-sidebar { - display: none; } - .md-tabs { - display: none; } - .md-typeset .headerlink { - display: none; } } - -@media only screen and (max-width: 44.9375em) { - .md-typeset pre { - margin: 1em -1.6rem; - border-radius: 0; } - .md-typeset pre > code { - padding: 1.05rem 1.6rem; } - .md-footer-nav__link--prev .md-footer-nav__title { - display: none; } - .md-search-result__teaser { - max-height: 5rem; - -webkit-line-clamp: 3; } - .codehilite .hll, .md-typeset .highlight .hll { - margin: 0 -1.6rem; - padding: 0 1.6rem; } - .md-typeset > .codehilite, .md-typeset > .highlight { - margin: 1em -1.6rem; - border-radius: 0; } - .md-typeset > .codehilite pre, .md-typeset > .highlight pre, - .md-typeset > .codehilite code, - .md-typeset > .highlight code { - padding: 1.05rem 1.6rem; } - .md-typeset > .codehilitetable, .md-typeset > .highlighttable { - margin: 1em -1.6rem; - border-radius: 0; } - .md-typeset > .codehilitetable .codehilite > pre, .md-typeset > .highlighttable .codehilite > pre, .md-typeset > .codehilitetable .highlight > pre, .md-typeset > .highlighttable .highlight > pre, - .md-typeset > .codehilitetable .codehilite > code, - .md-typeset > .highlighttable .codehilite > code, .md-typeset > .codehilitetable .highlight > code, .md-typeset > .highlighttable .highlight > code, - .md-typeset > .codehilitetable .linenodiv, - .md-typeset > .highlighttable .linenodiv { - padding: 1rem 1.6rem; } - .md-typeset > p > .MJXc-display { - margin: 0.75em -1.6rem; - padding: 0.25em 1.6rem; } - .md-typeset > .superfences-tabs { - margin: 1em -1.6rem; - border: 0; - border-top: 0.1rem solid rgba(0, 0, 0, 0.07); - border-radius: 0; } - .md-typeset > .superfences-tabs pre, - .md-typeset > .superfences-tabs code { - padding: 1.05rem 1.6rem; } } - -@media only screen and (min-width: 100em) { - html { - font-size: 68.75%; } } - -@media only screen and (min-width: 125em) { - html { - font-size: 75%; } } - -@media only screen and (max-width: 59.9375em) { - body[data-md-state="lock"] { - overflow: hidden; } - .ios body[data-md-state="lock"] .md-container { - display: none; } - html .md-nav__link[for="__toc"] { - display: block; - padding-right: 4.8rem; } - html .md-nav__link[for="__toc"]::after { - color: inherit; - content: "\E8DE"; } - html .md-nav__link[for="__toc"] + .md-nav__link { - display: none; } - html .md-nav__link[for="__toc"] ~ .md-nav { - display: flex; } - html [dir="rtl"] .md-nav__link { - padding-right: 1.6rem; - padding-left: 4.8rem; } - .md-nav__source { - display: block; - padding: 0 0.4rem; - background-color: rgba(50, 64, 144, 0.9675); - color: white; } - .md-search__overlay { - position: absolute; - top: 0.4rem; - left: 0.4rem; - width: 3.6rem; - height: 3.6rem; - -webkit-transform-origin: center; - transform-origin: center; - transition: opacity 0.2s 0.2s, -webkit-transform 0.3s 0.1s; - transition: transform 0.3s 0.1s, opacity 0.2s 0.2s; - transition: transform 0.3s 0.1s, opacity 0.2s 0.2s, -webkit-transform 0.3s 0.1s; - border-radius: 2rem; - background-color: white; - overflow: hidden; - pointer-events: none; } - [dir="rtl"] .md-search__overlay { - right: 0.4rem; - left: initial; } - [data-md-toggle="search"]:checked ~ .md-header .md-search__overlay { - transition: opacity 0.1s, -webkit-transform 0.4s; - transition: transform 0.4s, opacity 0.1s; - transition: transform 0.4s, opacity 0.1s, -webkit-transform 0.4s; - opacity: 1; } - .md-search__inner { - position: fixed; - top: 0; - left: 100%; - width: 100%; - height: 100%; - -webkit-transform: translateX(5%); - transform: translateX(5%); - transition: right 0s 0.3s, left 0s 0.3s, opacity 0.15s 0.15s, -webkit-transform 0.15s 0.15s cubic-bezier(0.4, 0, 0.2, 1); - transition: right 0s 0.3s, left 0s 0.3s, transform 0.15s 0.15s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.15s 0.15s; - transition: right 0s 0.3s, left 0s 0.3s, transform 0.15s 0.15s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.15s 0.15s, -webkit-transform 0.15s 0.15s cubic-bezier(0.4, 0, 0.2, 1); - opacity: 0; - z-index: 2; } - [data-md-toggle="search"]:checked ~ .md-header .md-search__inner { - left: 0; - -webkit-transform: translateX(0); - transform: translateX(0); - transition: right 0s 0s, left 0s 0s, opacity 0.15s 0.15s, -webkit-transform 0.15s 0.15s cubic-bezier(0.1, 0.7, 0.1, 1); - transition: right 0s 0s, left 0s 0s, transform 0.15s 0.15s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.15s 0.15s; - transition: right 0s 0s, left 0s 0s, transform 0.15s 0.15s cubic-bezier(0.1, 0.7, 0.1, 1), opacity 0.15s 0.15s, -webkit-transform 0.15s 0.15s cubic-bezier(0.1, 0.7, 0.1, 1); - opacity: 1; } - [dir="rtl"] [data-md-toggle="search"]:checked ~ .md-header .md-search__inner { - right: 0; - left: initial; } - html [dir="rtl"] .md-search__inner { - right: 100%; - left: initial; - -webkit-transform: translateX(-5%); - transform: translateX(-5%); } - .md-search__input { - width: 100%; - height: 4.8rem; - font-size: 1.8rem; } - .md-search__icon[for="__search"] { - top: 1.2rem; - left: 1.6rem; } - .md-search__icon[for="__search"][for="__search"]::before { - content: "\E5C4"; } - [dir="rtl"] .md-search__icon[for="__search"][for="__search"]::before { - content: "\E5C8"; } - .md-search__icon[type="reset"] { - top: 1.2rem; - right: 1.6rem; } - .md-search__output { - top: 4.8rem; - bottom: 0; } - .md-search-result__article--document::before { - display: none; } } - -@media only screen and (max-width: 76.1875em) { - [data-md-toggle="drawer"]:checked ~ .md-overlay { - width: 100%; - height: 100%; - transition: width 0s, height 0s, opacity 0.25s; - opacity: 1; } - .md-header-nav__button.md-icon--home, .md-header-nav__button.md-logo { - display: none; } - .md-hero__inner { - margin-top: 4.8rem; - margin-bottom: 2.4rem; } - .md-nav { - background-color: white; } - .md-nav--primary, - .md-nav--primary .md-nav { - display: flex; - position: absolute; - top: 0; - right: 0; - left: 0; - flex-direction: column; - height: 100%; - z-index: 1; } - .md-nav--primary .md-nav__title, - .md-nav--primary .md-nav__item { - font-size: 1.6rem; - line-height: 1.5; } - html .md-nav--primary .md-nav__title { - position: relative; - height: 11.2rem; - padding: 6rem 1.6rem 0.4rem; - background-color: rgba(0, 0, 0, 0.07); - color: rgba(0, 0, 0, 0.54); - font-weight: 400; - line-height: 4.8rem; - white-space: nowrap; - cursor: pointer; } - html .md-nav--primary .md-nav__title::before { - display: block; - position: absolute; - top: 0.4rem; - left: 0.4rem; - width: 4rem; - height: 4rem; - color: rgba(0, 0, 0, 0.54); } - html .md-nav--primary .md-nav__title ~ .md-nav__list { - background-color: white; - box-shadow: 0 0.1rem 0 rgba(0, 0, 0, 0.07) inset; } - html .md-nav--primary .md-nav__title ~ .md-nav__list > .md-nav__item:first-child { - border-top: 0; } - html .md-nav--primary .md-nav__title--site { - position: relative; - background-color: #3f51b5; - color: white; } - html .md-nav--primary .md-nav__title--site .md-nav__button { - display: block; - position: absolute; - top: 0.4rem; - left: 0.4rem; - width: 6.4rem; - height: 6.4rem; - font-size: 4.8rem; } - html .md-nav--primary .md-nav__title--site::before { - display: none; } - html [dir="rtl"] .md-nav--primary .md-nav__title::before { - right: 0.4rem; - left: initial; } - html [dir="rtl"] .md-nav--primary .md-nav__title--site .md-nav__button { - right: 0.4rem; - left: initial; } - .md-nav--primary .md-nav__list { - flex: 1; - overflow-y: auto; } - .md-nav--primary .md-nav__item { - padding: 0; - border-top: 0.1rem solid rgba(0, 0, 0, 0.07); } - [dir="rtl"] .md-nav--primary .md-nav__item { - padding: 0; } - .md-nav--primary .md-nav__item--nested > .md-nav__link { - padding-right: 4.8rem; } - [dir="rtl"] .md-nav--primary .md-nav__item--nested > .md-nav__link { - padding-right: 1.6rem; - padding-left: 4.8rem; } - .md-nav--primary .md-nav__item--nested > .md-nav__link::after { - content: "\E315"; } - [dir="rtl"] .md-nav--primary .md-nav__item--nested > .md-nav__link::after { - content: "\E314"; } - .md-nav--primary .md-nav__link { - position: relative; - margin-top: 0; - padding: 1.2rem 1.6rem; } - .md-nav--primary .md-nav__link::after { - position: absolute; - top: 50%; - right: 1.2rem; - margin-top: -1.2rem; - color: inherit; - font-size: 2.4rem; } - [dir="rtl"] .md-nav--primary .md-nav__link::after { - right: initial; - left: 1.2rem; } - .md-nav--primary .md-nav--secondary .md-nav__link { - position: static; } - .md-nav--primary .md-nav--secondary .md-nav { - position: static; - background-color: transparent; } - .md-nav--primary .md-nav--secondary .md-nav .md-nav__link { - padding-left: 2.8rem; } - [dir="rtl"] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link { - padding-right: 2.8rem; - padding-left: initial; } - .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link { - padding-left: 4rem; } - [dir="rtl"] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link { - padding-right: 4rem; - padding-left: initial; } - .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link { - padding-left: 5.2rem; } - [dir="rtl"] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link { - padding-right: 5.2rem; - padding-left: initial; } - .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link { - padding-left: 6.4rem; } - [dir="rtl"] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link { - padding-right: 6.4rem; - padding-left: initial; } - .md-nav__toggle ~ .md-nav { - display: flex; - -webkit-transform: translateX(100%); - transform: translateX(100%); - transition: opacity 0.125s 0.05s, -webkit-transform 0.25s cubic-bezier(0.8, 0, 0.6, 1); - transition: transform 0.25s cubic-bezier(0.8, 0, 0.6, 1), opacity 0.125s 0.05s; - transition: transform 0.25s cubic-bezier(0.8, 0, 0.6, 1), opacity 0.125s 0.05s, -webkit-transform 0.25s cubic-bezier(0.8, 0, 0.6, 1); - opacity: 0; } - [dir="rtl"] .md-nav__toggle ~ .md-nav { - -webkit-transform: translateX(-100%); - transform: translateX(-100%); } - .no-csstransforms3d .md-nav__toggle ~ .md-nav { - display: none; } - .md-nav__toggle:checked ~ .md-nav { - -webkit-transform: translateX(0); - transform: translateX(0); - transition: opacity 0.125s 0.125s, -webkit-transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); - transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.125s 0.125s; - transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.125s 0.125s, -webkit-transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); - opacity: 1; } - .no-csstransforms3d .md-nav__toggle:checked ~ .md-nav { - display: flex; } - .md-sidebar--primary { - position: fixed; - top: 0; - left: -24.2rem; - width: 24.2rem; - height: 100%; - -webkit-transform: translateX(0); - transform: translateX(0); - transition: box-shadow 0.25s, -webkit-transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); - transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.25s; - transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.25s, -webkit-transform 0.25s cubic-bezier(0.4, 0, 0.2, 1); - background-color: white; - z-index: 3; } - [dir="rtl"] .md-sidebar--primary { - right: -24.2rem; - left: initial; } - .no-csstransforms3d .md-sidebar--primary { - display: none; } - [data-md-toggle="drawer"]:checked ~ .md-container .md-sidebar--primary { - box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.4); - -webkit-transform: translateX(24.2rem); - transform: translateX(24.2rem); } - [dir="rtl"] [data-md-toggle="drawer"]:checked ~ .md-container .md-sidebar--primary { - -webkit-transform: translateX(-24.2rem); - transform: translateX(-24.2rem); } - .no-csstransforms3d [data-md-toggle="drawer"]:checked ~ .md-container .md-sidebar--primary { - display: block; } - .md-sidebar--primary .md-sidebar__scrollwrap { - overflow: hidden; } - .md-sidebar--primary .md-sidebar__scrollwrap { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - margin: 0; } - .md-tabs { - display: none; } } - -@media only screen and (min-width: 60em) { - .md-content { - margin-right: 24.2rem; } - [dir="rtl"] .md-content { - margin-right: initial; - margin-left: 24.2rem; } - .md-header-nav__button.md-icon--search { - display: none; } - .md-header-nav__source { - display: block; - width: 23rem; - max-width: 23rem; - margin-left: 2.8rem; - padding-right: 1.2rem; } - [dir="rtl"] .md-header-nav__source { - margin-right: 2.8rem; - margin-left: initial; - padding-right: initial; - padding-left: 1.2rem; } - .md-search { - padding: 0.4rem; } - .md-search__overlay { - position: fixed; - top: 0; - left: 0; - width: 0; - height: 0; - transition: width 0s 0.25s, height 0s 0.25s, opacity 0.25s; - background-color: rgba(0, 0, 0, 0.54); - cursor: pointer; } - [dir="rtl"] .md-search__overlay { - right: 0; - left: initial; } - [data-md-toggle="search"]:checked ~ .md-header .md-search__overlay { - width: 100%; - height: 100%; - transition: width 0s, height 0s, opacity 0.25s; - opacity: 1; } - .md-search__inner { - position: relative; - width: 23rem; - padding: 0.2rem 0; - float: right; - transition: width 0.25s cubic-bezier(0.1, 0.7, 0.1, 1); } - [dir="rtl"] .md-search__inner { - float: left; } - .md-search__form { - border-radius: 0.2rem; } - .md-search__input { - width: 100%; - height: 3.6rem; - padding-left: 4.4rem; - transition: background-color 0.25s cubic-bezier(0.1, 0.7, 0.1, 1), color 0.25s cubic-bezier(0.1, 0.7, 0.1, 1); - border-radius: 0.2rem; - background-color: rgba(0, 0, 0, 0.26); - color: inherit; - font-size: 1.6rem; } - [dir="rtl"] .md-search__input { - padding-right: 4.4rem; } - .md-search__input + .md-search__icon { - color: inherit; } - .md-search__input::-webkit-input-placeholder { - color: rgba(255, 255, 255, 0.7); } - .md-search__input:-ms-input-placeholder { - color: rgba(255, 255, 255, 0.7); } - .md-search__input::-ms-input-placeholder { - color: rgba(255, 255, 255, 0.7); } - .md-search__input::placeholder { - color: rgba(255, 255, 255, 0.7); } - .md-search__input:hover { - background-color: rgba(255, 255, 255, 0.12); } - [data-md-toggle="search"]:checked ~ .md-header .md-search__input { - border-radius: 0.2rem 0.2rem 0 0; - background-color: white; - color: rgba(0, 0, 0, 0.87); - text-overflow: none; } - [data-md-toggle="search"]:checked ~ .md-header .md-search__input + .md-search__icon, [data-md-toggle="search"]:checked ~ .md-header .md-search__input::-webkit-input-placeholder { - color: rgba(0, 0, 0, 0.54); } - [data-md-toggle="search"]:checked ~ .md-header .md-search__input + .md-search__icon, [data-md-toggle="search"]:checked ~ .md-header .md-search__input:-ms-input-placeholder { - color: rgba(0, 0, 0, 0.54); } - [data-md-toggle="search"]:checked ~ .md-header .md-search__input + .md-search__icon, [data-md-toggle="search"]:checked ~ .md-header .md-search__input::-ms-input-placeholder { - color: rgba(0, 0, 0, 0.54); } - [data-md-toggle="search"]:checked ~ .md-header .md-search__input + .md-search__icon, [data-md-toggle="search"]:checked ~ .md-header .md-search__input::placeholder { - color: rgba(0, 0, 0, 0.54); } - .md-search__output { - top: 3.8rem; - transition: opacity 0.4s; - opacity: 0; } - [data-md-toggle="search"]:checked ~ .md-header .md-search__output { - box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.4); - opacity: 1; } - .md-search__scrollwrap { - max-height: 0; } - [data-md-toggle="search"]:checked ~ .md-header .md-search__scrollwrap { - max-height: 75vh; } - .md-search__scrollwrap::-webkit-scrollbar { - width: 0.4rem; - height: 0.4rem; } - .md-search__scrollwrap::-webkit-scrollbar-thumb { - background-color: rgba(0, 0, 0, 0.26); } - .md-search__scrollwrap::-webkit-scrollbar-thumb:hover { - background-color: #536dfe; } - .md-search-result__meta { - padding-left: 4.4rem; } - [dir="rtl"] .md-search-result__meta { - padding-right: 4.4rem; - padding-left: initial; } - .md-search-result__article { - padding-left: 4.4rem; } - [dir="rtl"] .md-search-result__article { - padding-right: 4.4rem; - padding-left: 1.6rem; } - .md-sidebar--secondary { - display: block; - margin-left: 100%; - -webkit-transform: translate(-100%, 0); - transform: translate(-100%, 0); } - [dir="rtl"] .md-sidebar--secondary { - margin-right: 100%; - margin-left: initial; - -webkit-transform: translate(100%, 0); - transform: translate(100%, 0); } } - -@media only screen and (min-width: 76.25em) { - .md-content { - margin-left: 24.2rem; } - [dir="rtl"] .md-content { - margin-right: 24.2rem; } - .md-content__inner { - margin-right: 2.4rem; - margin-left: 2.4rem; } - .md-header-nav__button.md-icon--menu { - display: none; } - .md-nav[data-md-state="animate"] { - transition: max-height 0.25s cubic-bezier(0.86, 0, 0.07, 1); } - .md-nav__toggle ~ .md-nav { - max-height: 0; - overflow: hidden; } - .no-js .md-nav__toggle ~ .md-nav { - display: none; } - .md-nav__toggle:checked ~ .md-nav, .md-nav[data-md-state="expand"] { - max-height: 100%; } - .no-js .md-nav__toggle:checked ~ .md-nav, .no-js .md-nav[data-md-state="expand"] { - display: block; } - .md-nav__item--nested > .md-nav > .md-nav__title { - display: none; } - .md-nav__item--nested > .md-nav__link::after { - display: inline-block; - -webkit-transform-origin: 0.45em 0.45em; - transform-origin: 0.45em 0.45em; - -webkit-transform-style: preserve-3d; - transform-style: preserve-3d; - vertical-align: -0.125em; } - .js .md-nav__item--nested > .md-nav__link::after { - transition: -webkit-transform 0.4s; - transition: transform 0.4s; - transition: transform 0.4s, -webkit-transform 0.4s; } - .md-nav__item--nested .md-nav__toggle:checked ~ .md-nav__link::after { - -webkit-transform: rotateX(180deg); - transform: rotateX(180deg); } - [data-md-toggle="search"]:checked ~ .md-header .md-search__inner { - width: 68.8rem; } - .md-search__scrollwrap { - width: 68.8rem; } - .md-sidebar--secondary { - margin-left: 122rem; } - [dir="rtl"] .md-sidebar--secondary { - margin-right: 122rem; - margin-left: initial; } - .md-tabs ~ .md-main .md-nav--primary > .md-nav__list > .md-nav__item--nested { - font-size: 0; - visibility: hidden; } - .md-tabs--active ~ .md-main .md-nav--primary .md-nav__title { - display: block; - padding: 0; } - .md-tabs--active ~ .md-main .md-nav--primary .md-nav__title--site { - display: none; } - .no-js .md-tabs--active ~ .md-main .md-nav--primary .md-nav { - display: block; } - .md-tabs--active ~ .md-main .md-nav--primary > .md-nav__list > .md-nav__item { - font-size: 0; - visibility: hidden; } - .md-tabs--active ~ .md-main .md-nav--primary > .md-nav__list > .md-nav__item--nested { - display: none; - font-size: 1.4rem; - overflow: auto; - visibility: visible; } - .md-tabs--active ~ .md-main .md-nav--primary > .md-nav__list > .md-nav__item--nested > .md-nav__link { - display: none; } - .md-tabs--active ~ .md-main .md-nav--primary > .md-nav__list > .md-nav__item--active { - display: block; } - .md-tabs--active ~ .md-main .md-nav[data-md-level="1"] { - max-height: initial; - overflow: visible; } - .md-tabs--active ~ .md-main .md-nav[data-md-level="1"] > .md-nav__list > .md-nav__item { - padding-left: 0; } - .md-tabs--active ~ .md-main .md-nav[data-md-level="1"] .md-nav .md-nav__title { - display: none; } } - -@media only screen and (min-width: 45em) { - .md-footer-nav__link { - width: 50%; } - .md-footer-copyright { - max-width: 75%; - float: left; } - [dir="rtl"] .md-footer-copyright { - float: right; } - .md-footer-social { - padding: 1.2rem 0; - float: right; } - [dir="rtl"] .md-footer-social { - float: left; } } - -@media only screen and (max-width: 29.9375em) { - [data-md-toggle="search"]:checked ~ .md-header .md-search__overlay { - -webkit-transform: scale(45); - transform: scale(45); } } - -@media only screen and (min-width: 30em) and (max-width: 44.9375em) { - [data-md-toggle="search"]:checked ~ .md-header .md-search__overlay { - -webkit-transform: scale(60); - transform: scale(60); } } - -@media only screen and (min-width: 45em) and (max-width: 59.9375em) { - [data-md-toggle="search"]:checked ~ .md-header .md-search__overlay { - -webkit-transform: scale(75); - transform: scale(75); } } - -@media only screen and (min-width: 60em) and (max-width: 76.1875em) { - [data-md-toggle="search"]:checked ~ .md-header .md-search__inner { - width: 46.8rem; } - .md-search__scrollwrap { - width: 46.8rem; } - .md-search-result__teaser { - max-height: 5rem; - -webkit-line-clamp: 3; } } - -/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiIsImZpbGUiOiJhc3NldHMvc3R5bGVzaGVldHMvYXBwbGljYXRpb24uNDUxZjgwZTUuY3NzIiwic291cmNlUm9vdCI6IiJ9*/ \ No newline at end of file diff --git a/docs/v3/v1/common/docs/adalclient/index.html b/docs/v3/v1/common/docs/adalclient/index.html deleted file mode 100644 index 5f414b9a7..000000000 --- a/docs/v3/v1/common/docs/adalclient/index.html +++ /dev/null @@ -1,1994 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - adalclient - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -

- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
- -
- - -
-
- - - - - -

@pnp/core/adalclient

-

Added in 1.0.4

-

This module contains the AdalClient class which can be used to authenticate to any AzureAD secured resource. It is designed to work seamlessly with -SharePoint Framework's permissions.

-

Setup and Use inside SharePoint Framework

-

Using the SharePoint Framework is the preferred way to make use of the AdalClient as we can use the AADTokenProvider to efficiently get tokens on your behalf. You can also read more about how this process works and the necessary SPFx configurations in the SharePoint Framework 1.6 release notes. This method only work for SharePoint Framework >= 1.6. For earlier versions of SharePoint Framework you can still use the AdalClient as outlined above using the constructor to specify the values for an AAD Application you have setup.

-

Calling the graph api

-

By providing the context in the onInit we can create the adal client from known information.

-
import { graph } from "@pnp/graph";
-import { getRandomString } from "@pnp/core";
-
-// ...
-
-public onInit(): Promise<void> {
-
-  return super.onInit().then(_ => {
-
-    // other init code may be present
-    graph.setup({
-      spfxContext: this.context
-    });
-  });
-}
-
-public render(): void {
-
-  // here we are creating a team with a random name, required Group ReadWrite All permissions
-  const teamName = `ATeam.${getRandomString(4)}`;
-
-  this.domElement.innerHTML = `Hello, I am creating a team named "${teamName}" for you...`;
-
-  graph.teams.create(teamName, "This is a description").then(t => {
-
-    this.domElement.innerHTML += "done!";
-
-  }).catch(e => {
-
-    this.domElement.innerHTML = `Oops, I ran into a problem...${JSON.stringify(e, null, 4)}`;
-  });
-}
-
- - -

Calling the SharePoint API

-

This example shows how to use the ADALClient with the @pnp/sp library to call

-
import { sp } from "@pnp/sp";
-import { AdalClient } from "@pnp/core";
-
-// ...
-
-public onInit(): Promise<void> {
-
-  return super.onInit().then(_ => {
-
-    // other init code may be present
-    sp.setup({
-      spfxContext: this.context,
-      sp: {
-        fetchClientFactory: () => ,
-      },
-    });
-
-  });
-}
-
-public render(): void {
-
-  sp.web.get().then(t => {
-    this.domElement.innerHTML = JSON.stringify(t);
-  }).catch(e => {
-    this.domElement.innerHTML = JSON.stringify(e);
-  });
-}
-
- - -

Calling the any API

-

You can also use the AdalClient to execute AAD authenticated requests to any API which is properly configured to accept the incoming tokens. This approach will only work within SharePoint Framework >= 1.6. Here we call the SharePoint REST API without the sp library as an example.

-
import { AdalClient, FetchOptions } from "@pnp/core";
-import { ODataDefaultParser } from "@pnp/queryable";
-
-// ...
-
-public render(): void {
-
-  // create an ADAL Client
-  const client = AdalClient.fromSPFxContext(this.context);
-
-  // setup the request options
-  const opts: FetchOptions = {
-    method: "GET",
-    headers: {
-      "Accept": "application/json",
-    },
-  };
-
-  // execute the request
-  client.fetch("https://tenant.sharepoint.com/_api/web", opts).then(response => {
-
-    // create a parser to convert the response into JSON.
-    // You can create your own, at this point you have a fetch Response to work with
-    const parser = new ODataDefaultParser();
-
-    parser.parse(response).then(json => {
-      this.domElement.innerHTML = JSON.stringify(json);
-    });
-
-  }).catch(e => {
-    this.domElement.innerHTML = JSON.stringify(e);
-  });
-
-}
-
- - -

Manually Configure

-

This example shows setting up and using the AdalClient to make queries using information you have setup. You can review this article for more information on setting up and securing any application using AzureAD.

-

Setup and Use with Microsoft Graph

-

This sample uses a custom AzureAd app you have created and granted the appropriate permissions.

-
import { AdalClient } from "@pnp/core";
-import { graph } from "@pnp/graph";
-
-// configure the graph client
-// parameters are:
-// client id - the id of the application you created in azure ad
-// tenant - can be id or URL (shown)
-// redirect url - absolute url of a page to which your application and Azure AD app allows replies
-graph.setup({
-    graph: {
-        fetchClientFactory: () => {
-            return new AdalClient(
-                "e3e9048e-ea28-423b-aca9-3ea931cc7972",
-                "{tenant}.onmicrosoft.com",
-                "https://myapp/singlesignon.aspx");
-        },
-    },
-});
-
-try {
-
-    // call the graph API
-    const groups = await graph.groups.get();
-
-    console.log(JSON.stringify(groups, null, 4));
-
-} catch (e) {
-    console.error(e);
-}
-
- - -

Nodejs Applications

-

We have a dedicated node client in @pnp/nodejs.

- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/common/docs/collections/index.html b/docs/v3/v1/common/docs/collections/index.html deleted file mode 100644 index 6e0751122..000000000 --- a/docs/v3/v1/common/docs/collections/index.html +++ /dev/null @@ -1,1786 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - collections - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

@pnp/core/collections

-

The collections module provides typings and classes related to working with dictionaries.

-

TypedHash

-

Interface used to described an object with string keys corresponding to values of type T

-
export interface TypedHash<T> {
-    [key: string]: T;
-}
-
- - -

objectToMap

-

Converts a plain object to a Map instance

-
const map = objectToMap({ a: "b", c: "d"});
-
- - -

mergeMaps

-

Merges two or more maps, overwriting values with the same key. Last value in wins.

-
const m1 = new Map();
-const m2 = new Map();
-const m3 = new Map();
-const m4 = new Map();
-
-const m = mergeMaps(m1, m2, m3, m4);
-
- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/common/docs/custom-httpclientimpl/index.html b/docs/v3/v1/common/docs/custom-httpclientimpl/index.html deleted file mode 100644 index 0e6096316..000000000 --- a/docs/v3/v1/common/docs/custom-httpclientimpl/index.html +++ /dev/null @@ -1,1798 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Custom HttpClientImpl - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Custom HttpClientImpl

-

This should be considered an advanced topic and creating a custom HttpClientImpl is not something you will likely need to do. Also, we don't offer support beyond this article for writing your own implementation.

-

It is possible you may need complete control over the sending and receiving of requests.

-

Before you get started read and understand the fetch specification as you are essentially writing a custom fetch implementation.

-

The first step (second if you read the fetch spec as mentioned just above) is to understand the interface you need to implement, HttpClientImpl.

-
export interface HttpClientImpl {
-    fetch(url: string, options: FetchOptions): Promise<Response>;
-}
-
- - -

There is a single method "fetch" which takes a url string and a set of options. These options can be just about anything but are constrained within the library to the FetchOptions interface.

-
export interface FetchOptions {
-    method?: string;
-    headers?: HeadersInit | { [index: string]: string };
-    body?: BodyInit;
-    mode?: string | RequestMode;
-    credentials?: string | RequestCredentials;
-    cache?: string | RequestCache;
-}
-
- - -

So you will need to handle any of those options along with the provided url when sending your request. The library will expect your implementation to return a Promise that resolves to a Response defined by the fetch specification - which you've already read 👍.

-

Using Your Custom HttpClientImpl

-

Once you have written your implementation using it on your requests is done by setting it in the global library configuration:

-
import { setup } from "@pnp/core";
-import { sp, Web } from "@pnp/sp";
-import { MyAwesomeClient } from "./awesomeclient";
-
-sp.setup({
-    sp: {
-        fetchClientFactory: () => {
-            return new MyAwesomeClient();
-        }
-    }
-});
-
-let w = new Web("{site url}");
-
-// this request will use your client.
-w.select("Title").get().then(w => {
-    console.log(w);
-});
-
- - -

Subclassing is Better

-

You can of course inherit from one of the implementations available within the @pnp scope if you just need to say add a header or need to do something to every request sent. Perhaps some advanced logging. This approach will save you from needing to fully write a fetch implementation.

-

A FINAL NOTE

-

Whatever you do, do not write a client that uses a client id and secret and exposes them on the client side. Client Id and Secret should only ever be used on a server, never exposed to clients as anyone with those values has the full permissions granted to that id and secret.

- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/common/docs/index.html b/docs/v3/v1/common/docs/index.html deleted file mode 100644 index 7d5ab9f27..000000000 --- a/docs/v3/v1/common/docs/index.html +++ /dev/null @@ -1,1787 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - common - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

@pnp/core

-

npm version

-

The common modules provides a set of utilities classes and reusable building blocks used throughout the @pnp modules. They can be used within your applications as well.

-

Getting Started

-

Install the library and required dependencies

-

npm install @pnp/core --save

-

Import and use functionality, see details on modules below.

-
import { getGUID } from "@pnp/core";
-
-console.log(getGUID());
-
- - -

Exports

- -

UML

-

Graphical UML diagram

-

Graphical UML diagram of @pnp/core. Right-click the diagram and open in new tab if it is too small.

- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/common/docs/libconfig/index.html b/docs/v3/v1/common/docs/libconfig/index.html deleted file mode 100644 index 8c53dc4f3..000000000 --- a/docs/v3/v1/common/docs/libconfig/index.html +++ /dev/null @@ -1,1930 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - libconfig - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

@pnp/core/libconfig

-

Contains the shared classes and interfaces used to configure the libraries. These bases classes are expanded on in dependent libraries with the core -configuration defined here. This module exposes an instance of the RuntimeConfigImpl class: RuntimeConfig. This configuration object can be referenced and -contains the global configuration shared across the libraries. You can also extend the configuration for use within your own applications.

-

LibraryConfiguration Interface

-

Defines the shared configurable values used across the library as shown below. Each of these has a default value as shown below

-
export interface LibraryConfiguration {
-
-    /**
-     * Allows caching to be global disabled, default: false
-     */
-    globalCacheDisable?: boolean;
-
-    /**
-     * Defines the default store used by the usingCaching method, default: session
-     */
-    defaultCachingStore?: "session" | "local";
-
-    /**
-     * Defines the default timeout in seconds used by the usingCaching method, default 30
-     */
-    defaultCachingTimeoutSeconds?: number;
-
-    /**
-     * If true a timeout expired items will be removed from the cache in intervals determined by cacheTimeoutInterval
-     */
-    enableCacheExpiration?: boolean;
-
-    /**
-     * Determines the interval in milliseconds at which the cache is checked to see if items have expired (min: 100)
-     */
-    cacheExpirationIntervalMilliseconds?: number;
-
-    /**
-     * Used to supply the current context from an SPFx webpart to the library
-     */
-    spfxContext?: any;
-}
-
- - -

RuntimeConfigImpl

-

The class which implements the runtime configuration management as well as sets the default values used within the library. At its heart lies a Dictionary -used to track the configuration values. The keys will match the values in the interface or plain object passed to the extend method.

-

extend

-

The extend method is used to add configuration to the global configuration instance. You can pass it any plain object with string keys and those values will be added. Any -existing values will be overwritten based on the keys. Last value in wins. For a more detailed scenario of using the RuntimeConfig instance in your own application please -see the section below "Using RuntimeConfig within your application". Note there are no methods to remove/clear the global config as it should be considered fairly static -as frequent updates may have unpredictable side effects as it is a global shared object. Generally it should be set at the start of your application.

-
import { RuntimeConfig } from "@pnp/core";
-
-// add your custom keys to the global configuration
-// note you can use object hashes as values
-RuntimeConfig.extend({
-   "myKey1": "value 1",
-   "myKey2": {
-       "subKey": "sub value 1",
-       "subKey2": "sub value 2",
-   },
-});
-
-// read your custom values
-const v = RuntimeConfig.get("myKey1"); // "value 1"
-
- - -

Using RuntimeConfig within your Application

-

If you have a set of properties you will access very frequently it may be desirable to implement your own configuration object and expose those values as properties. To -do so you will need to create an interface for your configuration (optional) and a wrapper class for RuntimeConfig to expose your properties

-
import { LibraryConfiguration, RuntimeConfig } from "@pnp/core";
-
-// first we create our own interface by extending LibraryConfiguration. This allows your class to accept all the values with correct type checking. Note, because
-// TypeScript allows you to extend from multiple interfaces you can build a complex configuration definition from many sub definitions.
-
-// create the interface of your properties
-// by creating this separately you allows others to compose your parts into their own config
-interface MyConfigurationPart {
-
-    // you can create a grouped definition and access your settings as an object
-    // keys can be optional or required as defined by your interface
-    my?: {
-        prop1?: string;
-        prop2?: string;
-    }
-
-    // and/or define multiple top level properties (beware key collision)
-    // it is good practice to use a unique prefix
-    myProp1: string;
-    myProp2: number;
-}
-
-// now create a combined interface
-interface MyConfiguration extends LibraryConfiguration, MyConfigurationPart { }
-
-
-// now create a wrapper object and expose your properties
-class MyRuntimeConfigImpl {
-
-    // exposing a nested property
-    public get prop1(): TypedHash<string> {
-
-        const myPart = RuntimeConfig.get("my");
-        if (myPart !== null && typeof myPart !== "undefined" && typeof myPart.prop1 !== "undefined") {
-            return myPart.prop1;
-        }
-
-        return {};
-    }
-
-    // exposing a root level property
-    public get myProp1(): string | null {
-
-        let myProp1 = RuntimeConfig.get("myProp1");
-
-        if (myProp1 === null) {
-            myProp1 = "some default value";
-        }
-
-        return myProp1;
-    }
-
-    setup(config: MyConfiguration): void {
-        RuntimeConfig.extend(config);
-    }
-}
-
-// create a single static instance of your impl class
-export let MyRuntimeConfig = new MyRuntimeConfigImpl();
-
- - -

Now in other files you can use and set your configuration with a typed interface and properties

-
import { MyRuntimeConfig } from "{location of module}";
-
-
-MyRuntimeConfig.setup({
-    my: {
-        prop1: "hello",
-    },
-});
-
-const value = MyRuntimeConfig.prop1; // "hello"
-
- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/common/docs/netutil/index.html b/docs/v3/v1/common/docs/netutil/index.html deleted file mode 100644 index 6ee9e809a..000000000 --- a/docs/v3/v1/common/docs/netutil/index.html +++ /dev/null @@ -1,1860 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - netutil - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

@pnp/core/netutil

-

This module contains a set of classes and interfaces used to characterize shared http interactions and configuration of the libraries. Some of the interfaces -are described below (many have no use outside the library) as well as several classes.

-

Interfaces

-

HttpClientImpl

-

Defines an implementation of an Http Client within the context of @pnp. This being a class with a a single method "fetch" take a URL and -options and returning a Promise. Used primarily with the shared request pipeline to define the client used to make the actual request. You can -write your own custom implementation if needed.

-

RequestClient

-

An abstraction that contains specific methods related to each of the primary request methods get, post, patch, delete as well as fetch and fetchRaw. The -difference between fetch and fetchRaw is that a client may include additional logic or processing in fetch, where fetchRaw should be a direct call to the -underlying HttpClientImpl fetch method.

-

Classes

-

This module export two classes of note, FetchClient and BearerTokenFetchClient. Both implement HttpClientImpl.

-

FetchClient

-

Basic implementation that calls the global (window) fetch method with no additional processing.

-
import { FetchClient } from "@pnp/core";
-
-const client = new FetchClient();
-
-client.fetch("{url}", {});
-
- - -

BearerTokenFetchClient

-

A simple implementation that takes a provided authentication token and adds the Authentication Bearer header to the request. No other processing is done and -the token is treated as a static string.

-
import { BearerTokenFetchClient } from "@pnp/core";
-
-const client = new BearerTokenFetchClient("{authentication token}");
-
-client.fetch("{url}", {});
-
- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/common/docs/storage/index.html b/docs/v3/v1/common/docs/storage/index.html deleted file mode 100644 index 66a8ea739..000000000 --- a/docs/v3/v1/common/docs/storage/index.html +++ /dev/null @@ -1,1850 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - storage - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

@pnp/core/storage

-

This module provides a thin wrapper over the browser storage options, local and session. If neither option is available it shims storage with -a non-persistent in memory polyfill. Optionally through configuration you can activate expiration. Sample usage is shown below.

-

PnPClientStorage

-

The main export of this module, contains properties representing local and session storage.

-
import { PnPClientStorage } from "@pnp/core";
-
-const storage = new PnPClientStorage();
-const myvalue = storage.local.get("mykey");
-
- - -

PnPClientStorageWrapper

-

Each of the storage locations (session and local) are wrapped with this helper class. You can use it directly, but generally it would be used -from an instance of PnPClientStorage as shown below. These examples all use local storage, the operations are identical for session storage.

-
import { PnPClientStorage } from "@pnp/core";
-
-const storage = new PnPClientStorage();
-
-// get a value from storage
-const value = storage.local.get("mykey");
-
-// put a value into storage
-storage.local.put("mykey2", "my value");
-
-// put a value into storage with an expiration
-storage.local.put("mykey2", "my value", new Date());
-
-// put a simple object into storage
-// because JSON.stringify is used to package the object we do NOT do a deep rehydration of stored objects
-storage.local.put("mykey3", {
-    key: "value",
-    key2: "value2",
-});
-
-// remove a value from storage
-storage.local.delete("mykey3");
-
-// get an item or add it if it does not exist
-// returns a promise in case you need time to get the value for storage
-// optionally takes a third parameter specifying the expiration
-storage.local.getOrPut("mykey4", () => {
-    return Promise.resolve("value");
-});
-
-// delete expired items
-storage.local.deleteExpired();
-
- - -

Cache Expiration

-

The ability remove of expired items based on a configured timeout can help if the cache is filling up. This can be accomplished in two ways. The first is to explicitly call the new deleteExpired method on the cache you wish to clear. A suggested usage is to add this into your page init code as clearing expired items once per page load is likely sufficient.

-
import { PnPClientStorage } from "@pnp/core";
-
-const storage = new PnPClientStorage();
-
-// session storage
-storage.session.deleteExpired();
-
-// local storage
-storage.local.deleteExpired();
-
-// this returns a promise, so you can perform some activity after the expired items are removed:
-storage.local.deleteExpired().then(_ => {
-    // init my application
-});
-
- - -

The second method is to enable automated cache expiration through global config. Setting the enableCacheExpiration property to true will enable the timer. Optionally you can set the interval at which the cache is checked via the cacheExpirationIntervalMilliseconds property, by default 750 milliseconds is used. We enforce a minimum of 300 milliseconds as this functionality is enabled via setTimeout and there is little value in having an excessive number of cache checks. This method is more appropriate for a single page application where the page is infrequently reloaded and many cached operations are performed. There is no advantage to enabling cache expiration unless you are experiencing cache storage space pressure in a long running page - and you may see a performance hit due to the use of setTimeout.

-
import { setup } from "@pnp/core";
-
-setup({
-    enableCacheExpiration: true,
-    cacheExpirationIntervalMilliseconds: 1000, // optional
-});
-
- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/common/docs/util/index.html b/docs/v3/v1/common/docs/util/index.html deleted file mode 100644 index 429bb8239..000000000 --- a/docs/v3/v1/common/docs/util/index.html +++ /dev/null @@ -1,2058 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - util - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

@pnp/core/util

-

This module contains utility methods that you can import individually from the common library.

-
import {
-    getRandomString,
-} from "@pnp/core";
-
-// use from individual;y imported method
-console.log(getRandomString(10));
-
- - -

getCtxCallback

-

Gets a callback function which will maintain context across async calls.

-
import { getCtxCallback } from "@pnp/core";
-
-const contextThis = {
-    myProp: 6,
-};
-
-function theFunction() {
-    // "this" within this function will be the context object supplied
-    // in this case the variable contextThis, so myProp will exist
-    return this.myProp;
-}
-
-const callback = getCtxCallback(contextThis, theFunction);
-
-callback(); // returns 6
-
-// You can also supply additional parameters if needed
-
-function theFunction2(g: number) {
-    // "this" within this function will be the context object supplied
-    // in this case the variable contextThis, so myProp will exist
-    return this.myProp + g;
-}
-
-const callback2 = getCtxCallback(contextThis, theFunction, 4);
-
-callback2(); // returns 10 (6 + 4)
-
- - -

dateAdd

-

Manipulates a date, please see the Stackoverflow discussion from where this method was taken.

-

combine

-

Combines any number of paths, normalizing the slashes as required

-
import { combine } from "@pnp/core";
-
-// "https://microsoft.com/something/more"
-const paths = combine("https://microsoft.com", "something", "more");
-
-// "also/works/with/relative"
-const paths2 = combine("/also/", "/works", "with/", "/relative\\");
-
- - -

getRandomString

-

Gets a random string consisting of the number of characters requested.

-
import { getRandomString } from "@pnp/core";
-
-const randomString = getRandomString(10);
-
- - -

getGUID

-

Creates a random guid, please see the Stackoverflow discussion from where this method was taken.

-

isFunc

-

Determines if a supplied variable represents a function.

-

objectDefinedNotNull

-

Determines if an object is defined and not null.

-

isArray

-

Determines if a supplied variable represents an array.

-

extend

-

Merges a source object's own enumerable properties into a single target object. Similar to Object.assign, but allows control of overwriting of existing -properties.

-
import { extend } from "@pnp/core";
-
-let obj1 = {
-    prop: 1,
-    prop2: 2,
-};
-
-const obj2 = {
-    prop: 4,
-    prop3: 9,
-};
-
-const example1 = extend(obj1, obj2);
-// example1 = { prop: 4, prop2: 2, prop3: 9 }
-
-const example2 = extend(obj1, obj2, true);
-// example2 = { prop: 1, prop2: 2, prop3: 9 }
-
- - -

isUrlAbsolute

-

Determines if a supplied url is absolute and returns true; otherwise returns false.

-

stringIsNullOrEmpty

-

Determines if a supplied string is null or empty

-

Removed

-

Some methods that were no longer used internally by the @pnp libraries have been removed. You can find the source for those methods -below for use in your projects should you require.

-
/**
- * Loads a stylesheet into the current page
- *
- * @param path The url to the stylesheet
- * @param avoidCache If true a value will be appended as a query string to avoid browser caching issues
- */
-public static loadStylesheet(path: string, avoidCache: boolean): void {
-    if (avoidCache) {
-        path += "?" + encodeURIComponent((new Date()).getTime().toString());
-    }
-    const head = document.getElementsByTagName("head");
-    if (head.length > 0) {
-        const e = document.createElement("link");
-        head[0].appendChild(e);
-        e.setAttribute("type", "text/css");
-        e.setAttribute("rel", "stylesheet");
-        e.setAttribute("href", path);
-    }
-}
-
-/**
- * Tests if a url param exists
- *
- * @param name The name of the url parameter to check
- */
-public static urlParamExists(name: string): boolean {
-    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
-    const regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
-    return regex.test(location.search);
-}
-
-/**
- * Gets a url param value by name
- *
- * @param name The name of the parameter for which we want the value
- */
-public static getUrlParamByName(name: string): string {
-    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
-    const regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
-    const results = regex.exec(location.search);
-    return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
-}
-
-/**
- * Gets a url param by name and attempts to parse a bool value
- *
- * @param name The name of the parameter for which we want the boolean value
- */
-public static getUrlParamBoolByName(name: string): boolean {
-    const p = this.getUrlParamByName(name);
-    const isFalse = (p === "" || /false|0/i.test(p));
-    return !isFalse;
-}
-
-/**
- * Inserts the string s into the string target as the index specified by index
- *
- * @param target The string into which we will insert s
- * @param index The location in target to insert s (zero based)
- * @param s The string to insert into target at position index
- */
-public static stringInsert(target: string, index: number, s: string): string {
-    if (index > 0) {
-        return target.substring(0, index) + s + target.substring(index, target.length);
-    }
-    return s + target;
-}
-
- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/config-store/docs/configuration/index.html b/docs/v3/v1/config-store/docs/configuration/index.html deleted file mode 100644 index 8f8988e2c..000000000 --- a/docs/v3/v1/config-store/docs/configuration/index.html +++ /dev/null @@ -1,1730 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - configuration - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

@pnp/config-store/configuration

-

The main class exported from the config-store package is Settings. This is the class through which you will load and access your -settings via providers.

-
import { Web } from "@pnp/sp";
-import { Settings, SPListConfigurationProvider } from "@pnp/config-store";
-
-// create an instance of the settings class, could be static and shared across your application
-// or built as needed.
-const settings = new Settings();
-
-// you can add/update a single value using add
-settings.add("mykey", "myvalue");
-
-// you can also add/update a JSON value which will be stringified for you as a shorthand
-settings.addJSON("mykey2", {
-    field: 1,
-    field2: 2,
-    field3: 3,
-});
-
-// and you can apply a plain object of keys/values that will be written as single values
-// this results in each enumerable property of the supplied object being added to the settings collection
-settings.apply({
-    field: 1,
-    field2: 2,
-    field3: 3,
-});
-
-// and finally you can load values from a configuration provider
-const w = new Web("https://mytenant.sharepoint.com/sites/dev");
-const provider = new SPListConfigurationProvider(w, "myconfiglistname");
-
-// this will load values from the supplied list
-// by default the key will be from the Title field and the value from a column named Value
-await settings.load(provider);
-
-// once we have loaded values we can then read them
-const value = settings.get("mykey");
-
-// or read JSON that will be parsed for you from the store
-const value2 = settings.getJSON("mykey2");
-
- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/config-store/docs/index.html b/docs/v3/v1/config-store/docs/index.html deleted file mode 100644 index 0bd4fbd66..000000000 --- a/docs/v3/v1/config-store/docs/index.html +++ /dev/null @@ -1,1761 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - config-store - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

@pnp/config-store

-

npm version

-

This module providers a way to load application configuration from one or more providers and share it across an application in a consistent way. A provider can be anything - but we have included one to load information from a SharePoint list. This library is most helpful for larger applications where a formal configuration model is needed.

-

Getting Started

-

Install the library and required dependencies

-

npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/config-store --save

-

See the topics below for usage:

- -

UML

-

Graphical UML diagram

-

Graphical UML diagram of @pnp/config-store. Right-click the diagram and open in new tab if it is too small.

- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/config-store/docs/providers/index.html b/docs/v3/v1/config-store/docs/providers/index.html deleted file mode 100644 index 9f3e2853d..000000000 --- a/docs/v3/v1/config-store/docs/providers/index.html +++ /dev/null @@ -1,1782 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - providers - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

@pnp/config-store/providers

-

Currently there is a single provider included in the library, but contributions of additional providers are welcome.

-

SPListConfigurationProvider

-

This provider is based on a SharePoint list and read all of the rows and makes them available as a TypedHash. By default the column names used are Title for key and "Value" for value, but you can update these as needed. Additionally the settings class supports the idea of last value in wins - so you can easily load multiple configurations. This helps to support a common scenario in the enterprise where you might have one main list for global configuration but some settings can be set at the web level. In this case you would first load the global, then the local settings and any local values will take precedence.

-
import { Web } from "@pnp/sp";
-import { Settings, SPListConfigurationProvider } from "@pnp/config-store";
-
-// create a new provider instance
-const w = new Web("https://mytenant.sharepoint.com/sites/dev");
-const provider = new SPListConfigurationProvider(w, "myconfiglistname");
-
-const settings = new Settings();
-
-// load our values from the list
-await settings.load(provider);
-
- - -

CachingConfigurationProvider

-

Because making requests on each page load is very inefficient you can optionally use the caching configuration provider, which wraps a -provider and caches the configuration in local or session storage.

-
import { Web } from "@pnp/sp";
-import { Settings, SPListConfigurationProvider } from "@pnp/config-store";
-
-// create a new provider instance
-const w = new Web("https://mytenant.sharepoint.com/sites/dev");
-const provider = new SPListConfigurationProvider(w, "myconfiglistname");
-
-// get an instance of the provider wrapped
-// you can optionally provide a key that will be used in the cache to the asCaching method
-const wrappedProvider = provider.asCaching();
-
-// use that wrapped provider to populate the settings
-await settings.load(wrappedProvider);
-
- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/documentation/SPFx-On-Premesis-2016/index.html b/docs/v3/v1/documentation/SPFx-On-Premesis-2016/index.html deleted file mode 100644 index cc149679d..000000000 --- a/docs/v3/v1/documentation/SPFx-On-Premesis-2016/index.html +++ /dev/null @@ -1,1720 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SPFx On-Premises 2016 - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Workaround for SPFx TypeScript Version

-

Note this article applies to version 1.4.1 SharePoint Framework projects targetting on-premesis only.

-

When using the Yeoman generator to create a SharePoint Framework 1.4.1 project targeting on-premesis it installs TypeScript version 2.2.2. Unfortunately this library relies on 2.4.2 or later due to extensive use of default values for generic type parameters in the libraries. To work around this limitation you can follow the steps in this article.

-
    -
  1. Open package-lock.json
  2. -
  3. Search for "typescript": "2.2.2"
  4. -
  5. Replace "2.2.2" with "2.4.2"
  6. -
  7. Search for the next "typescript" occurance and replace the block with:
  8. -
-
"typescript": {
-  "version": "2.4.2",
-  "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz",
-  "integrity": "sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=",
-  "dev": true
-}
-
- - -

Replacement blocks highlighted

-
    -
  1. Remove node_modules folder rm -rf node_modules/
  2. -
  3. Run npm install
  4. -
-

This can be checked with:

-
npm list typescript
-
- - -
+-- @microsoft/sp-build-web@1.1.0
-| `-- @microsoft/gulp-core-build-typescript@3.1.1
-|   +-- @microsoft/api-extractor@2.3.8
-|   | `-- typescript@2.4.2
-|   `-- typescript@2.4.2
-
- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/documentation/beta-versions/index.html b/docs/v3/v1/documentation/beta-versions/index.html deleted file mode 100644 index 869e59475..000000000 --- a/docs/v3/v1/documentation/beta-versions/index.html +++ /dev/null @@ -1,1742 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Install Beta Versions - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Beta Versions

-

To help folks try out new features early and provide feedback prior to releases we publish beta versions of the packages. Released as a set with matching version numbers, just like when we do a normal release. Generally every Friday a new set of beta libraries will be released. While not ready for production use we encourage you to try out these pre-release packages and provide us feedback.

-

Installing

-

To install the beta packages in your project you use the @beta version number on the packages. This applies to all packages, not just the ones -shown in the example below.

-
npm install @pnp/logging@beta @pnp/core@beta @pnp/queryable@beta @pnp/sp@beta --save
-
- - -

Please remember that it is possible something may not work in a beta version, so be aware and if you find something please report an -issue.

- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/documentation/css/extra.css b/docs/v3/v1/documentation/css/extra.css deleted file mode 100644 index 6391b1ff7..000000000 --- a/docs/v3/v1/documentation/css/extra.css +++ /dev/null @@ -1,33 +0,0 @@ -.md-logo { - height: 32px; - width: 150px; - padding: 0 0.25 0.5 !important; -} - -.md-header{ - height: 75px; -} - -.md-container{ - padding-top: 70px; -} - -.md-sidebar[data-md-state="lock"]{ - padding-top: 75px; -} - -.md-logo img { - width: 100% !important; - height: auto !important; - margin-top: -0.25em; -} - -.md-footer { - margin-top: 5em; -} - -@media only screen and (max-width: 76.1875em) { - .md-nav--primary .md-nav__title--site .md-nav__button { - width: 150px; - } -} \ No newline at end of file diff --git a/docs/v3/v1/documentation/debugging/index.html b/docs/v3/v1/documentation/debugging/index.html deleted file mode 100644 index 5d43b1e41..000000000 --- a/docs/v3/v1/documentation/debugging/index.html +++ /dev/null @@ -1,2098 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Debugging - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - - - - -
-
- - - - - -

Debugging

-

Debugging Library Features in Code using Node

-

The easiest way to debug the library when working on new features is using F5 in Visual Studio Code. This uses the launch.json file to build and run the library using ./debug/launch/main.ts as the program entry. You can add any number of files to this directory and they will be ignored by git, however the debug.ts file is not, so please ensure you don't commit any login information.

-

Setup settings.js

-

If you have not already you need to create a settings.js files by copying settings.example.js and renaming it to settings.js. Then update the clientId, clientSecret, and siteUrl fields in the testing section. (See below for guidance on registering a client id and secret)

-

Test your setup

-

If you hit F5 now you should be able to see the full response from getting the web's title in the internal console window. If not, ensure that you have properly updated the settings file and registered the add-in perms correctly.

-

Create a debug module

-

Using ./debug/launch/example.ts as a reference create a debugging file in the debug folder, let's call it mydebug.ts and add this content:

-
// note we can use the actual package names for our imports
-import { sp, ListEnsureResult } from "@pnp/sp";
-import { Logger, LogLevel, ConsoleListener } from "@pnp/logging";
-
-declare var process: { exit(code?: number): void };
-
-export function MyDebug() {
-
-    // run some debugging
-    sp.web.lists.ensure("MyFirstList").then((list: ListEnsureResult) => {
-
-        Logger.log({
-            data: list.created,
-            message: "Was list created?",
-            level: LogLevel.Verbose
-        });
-
-        if (list.created) {
-
-            Logger.log({
-                data: list.data,
-                message: "Raw data from list creation.",
-                level: LogLevel.Verbose
-            });
-
-        } else {
-
-            Logger.log({
-                data: null,
-                message: "List already existed!",
-                level: LogLevel.Verbose
-            });
-        }
-
-        process.exit(0);
-    }).catch(e => {
-
-        Logger.error(e);
-        process.exit(1);
-    });
-}
-
- - -

Update main.ts to launch your module

-

First comment out the import for the default example and then add the import and function call for yours, the updated main.ts should look like this:

-
// ...
-
-// comment out the example
-// import { Example } from "./example";
-// Example();
-
-import { MyDebug } from "./mydebug"
-MyDebug();
-
-// ...
-
- - -

Debug!

-

Place a break point within the promise resolution in your debug file and hit F5. Your module should be run and your break point hit. You can then examine the contents of the objects and see the run time state. Remember you can also set breakpoints within the package src folders to see exactly how things are working during your debugging scenarios.

-

Next Steps

-

Using this pattern you can create and preserve multiple debugging scenarios in separate modules locally.

-

In Browser Debugging

-

You can also serve files locally to debug in a browser through two methods. The first will serve code using ./debug/serve/main.ts as the entry. Meaning you can easily -write code and test it in the browser. The second method allows you to serve a single package (bundled with all dependencies) for in browser testing. Both methods serve -the file from https://localhost:8080/assets/pnp.js, allowing you to create a single page in your tenant for in browser testing.

-

Start the local serve

-

This will serve a package with ./debug/serve/main.ts as the entry.

-

gulp serve

-

Add reference to library

-

Within a SharePoint page add a script editor web part and then paste in the following code. The div is to give you a place to target with visual updates should you desire.

-
<script src="https://localhost:8080/assets/pnp.js"></script>
-<div id="pnptestdiv"></div>
-
- - -

You should see an alert with the current web's title using the default main.ts. Feel free to update main.ts to do whatever you would like, but note that any changes -included as part of a PR to this file will not be allowed.

-

Serve a specific package

-

For example if you wanted to serve the @pnp/sp package for testing you would use:

-

gulp serve --p sp

-

This will serve a bundle of the sp functionality along with all dependencies and place a global variable named "pnp.{packagename}", in this case pnp.sp. This will be -true for each package, if you served just the graph package the global would be pnp.graph. This mirrors how the umd modules are built in the distributed npm packages -to allow testing with matching packages.

-

Next Steps

-

You can make changes to the library and immediately see them reflected in the browser. All files are watched regardless of which serve method you choose.

-

Register an Add-in

-

Before you can begin debugging you need to register a low-trust add-in with SharePoint. This is primarily designed for Office 365, but can work on-premises if you configure your farm accordingly.

-
    -
  1. Navigation to {site url}/_layouts/appregnew.aspx
  2. -
  3. Click "Generate" for both the Client Id and Secret values
  4. -
  5. Give you add-in a title, this can be anything but will let you locate it in the list of add-in permissions
  6. -
  7. Provide a fake value for app domain and redirect uri, you can use the values shown in the examples
  8. -
  9. Click "Create"
  10. -
  11. Copy the returned block of text containing the client id and secret as well as app name for your records and later in this article.
  12. -
-

Grant Your Add-In Permissions

-

Now that we have created an add-in registration we need to tell SharePoint what permissions it can use. Due to an update in SharePoint Online you now have to register add-ins with certain permissions in the admin site.

-
    -
  1. Navigate to {admin site url}/_layouts/appinv.aspx
  2. -
  3. Paste your client id from the above section into the Add Id box and click "Lookup"
  4. -
  5. You should see the information populated into the form from the last section, if not ensure you have the correct id value
  6. -
  7. Paste the below XML into the permissions request xml box and hit "Create"
  8. -
  9. You should get a confirmation message.
  10. -
-
  <AppPermissionRequests AllowAppOnlyPolicy="true">
-    <AppPermissionRequest Scope="http://sharepoint/content/tenant" Right="FullControl" />
-    <AppPermissionRequest Scope="http://sharepoint/social/tenant" Right="FullControl" />
-    <AppPermissionRequest Scope="http://sharepoint/search" Right="QueryAsUserIgnoreAppPrincipal" />
-  </AppPermissionRequests>
-
- - -

Note these are very broad permissions to ensure you can test any feature of the library, for production you should tailor the permissions to only those required

-

Configure the project settings file

-
    -
  1. If you have not already, make a copy of settings.example.js and name it settings.js
  2. -
  3. Edit this file to set the values on the testing.sp object to
      -
    • id: "The client id you created"
    • -
    • secret: "The client secret you created"
    • -
    • url: "{site url}"
    • -
    -
  4. -
  5. You can disable web tests at any time by setting enableWebTests to false in settings.js, this can be helpful as they take a few minutes to run
  6. -
- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/documentation/deployment/index.html b/docs/v3/v1/documentation/deployment/index.html deleted file mode 100644 index 971317f28..000000000 --- a/docs/v3/v1/documentation/deployment/index.html +++ /dev/null @@ -1,1972 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Deployment - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Deployment

-

There are two recommended ways to consume the library in a production deployment: bundle the code into your solution (such as with webpack), or reference the code from a CDN. These methods are outlined here but this is not meant to be an exhaustive guide on all the ways to package and deploy solutions.

-

Bundle

-

If you have installed the library via NPM into your application solution bundlers such as webpack can bundle the PnPjs libraries along with your solution. This can make deployment easier, but will increase the size of your application by the size of the included libraries. The PnPjs libraries are setup to support tree shaking which can help with the bundle size.

-

CDN

-

If you have public internet access you can reference the library from cdnjs or unpkg which maintains copies of all versions. This is ideal as you do not need to host the file yourself, and it is easy to update to a newer release by updating the URL in your solution. Below lists all of the library locations within cdnjs, you will need to ensure you have the full url to the file you need, such as: "https://cdnjs.cloudflare.com/ajax/libs/pnp-common/1.1.1/common.es5.umd.min.js". To use the libraries with a script tag in a page it is recommended to use the *.es5.umd.min.js versions. This will add a global pnp value with each library added as pnp.{lib name} such as pnp.sp, pnp.common, etc.

- -

CDN and SPFx

-

If you are developing in SPFx and install and import the PnPjs libraries the default behavior will be to bundle the library into your solution. You have a couple of choices on how best to work with CDNs and SPFx. Because SPFx doesn't currently respect peer dependencies it is easier to reference the pnpjs rollup package for your project. In this case you would install the package, reference it in your code, and update your config.js file externals as follows:

-

Install

-

npm install @pnp/pnpjs --save

-

In Code

-
import { sp } from "@pnp/pnpjs";
-
-sp.web.lists.getByTitle("BigList").get().then(r => {
-
-    this.domElement.innerHTML += r.Title;
-});
-
- - -

config.json

-
  "externals": {
-    "@pnp/pnpjs": {
-      "path": "https://cdnjs.cloudflare.com/ajax/libs/pnp-pnpjs/1.1.4/pnpjs.es5.umd.bundle.min.js",
-      "globalName": "pnp"
-    }
-  },
-
- - -
-

You can still work with the individual packages from a cdn, but you have a bit more work to do. First install the modules you need, update the config with the JSON externals below, and add some blind require statements into your code. These are needed because peer dependencies are not processed by SPFx so you have to "trigger" the SPFx manifest creator to include those packages.

-
-

Note this approach requires using version 1.1.5 (specifically beta 1.1.5-2) or later of the libraries as we had make a few updates to how things are packaged to make this a little easier.

-
-

Install

-

npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp --save

-

In Code

-
// blind require statements
-require("tslib");
-require("@pnp/logging");
-require("@pnp/core");
-require("@pnp/queryable");
-import { sp } from "@pnp/sp";
-
-sp.web.lists.getByTitle("BigList").get().then(r => {
-
-    this.domElement.innerHTML += r.Title;
-});
-
- - -

config.json

-
"externals": {
-  "@pnp/sp": {
-    "path": "https://unpkg.com/@pnp/sp@1.1.5-2/dist/sp.es5.umd.min.js",
-    "globalName": "pnp.sp",
-    "globalDependencies": [
-      "@pnp/logging",
-      "@pnp/core",
-      "@pnp/queryable",
-      "tslib"
-    ]
-  },
-  "@pnp/queryable": {
-    "path": "https://unpkg.com/@pnp/queryable@1.1.5-2/dist/odata.es5.umd.min.js",
-    "globalName": "pnp.odata",
-    "globalDependencies": [
-      "@pnp/core",
-      "@pnp/logging",
-      "tslib"
-    ]
-  },
-  "@pnp/core": {
-    "path": "https://unpkg.com/@pnp/core@1.1.5-2/dist/common.es5.umd.bundle.min.js",
-    "globalName": "pnp.common"     
-  },
-  "@pnp/logging": {
-    "path": "https://unpkg.com/@pnp/logging@1.1.5-2/dist/logging.es5.umd.min.js",
-    "globalName": "pnp.logging",
-    "globalDependencies": [
-      "tslib"
-    ]
-  },
-  "tslib": {
-    "path": "https://cdnjs.cloudflare.com/ajax/libs/tslib/1.9.3/tslib.min.js",
-    "globalName": "tslib"
-  }
-}
-
- - -

Don't forget to update the version number in the url to match the version you want to use. This will stop the library from being bundled directly into the solution and instead use the copy from the CDN. When a new version of the PnPjs libraries are released and you are ready to update just update this url in your SPFX project's config.js file.

- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/documentation/documentation/index.html b/docs/v3/v1/documentation/documentation/index.html deleted file mode 100644 index 2376a396c..000000000 --- a/docs/v3/v1/documentation/documentation/index.html +++ /dev/null @@ -1,1760 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Building Docs - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Building the Documentation

-

Building the documentation locally can help you visualize change you are making to the docs. What you see locally should be what you see online.

-

Building

-

Documentation is built using MkDocs. You will need to latest version of Python (tested on version 3.7.1) and pip. If you're on the Windows operating system, make sure you have added Python to your Path environment variable.

-

When executing the pip module on Windows you can prefix it with python -m. -For example:

-
python -m pip install mkdocs-material
-
- - -
    -
  • Install MkDocs
      -
    • pip install mkdocs
    • -
    -
  • -
  • Install the Material theme
      -
    • pip install mkdocs-material
    • -
    -
  • -
  • install the mkdocs-markdownextradata-plugin - this is used for the version variable
      -
    • pip install mkdocs-markdownextradata-plugin (doesn't work on Python v2.7)
    • -
    -
  • -
  • Serve it up
      -
    • mkdocs serve
    • -
    • Open a browser to http://127.0.0.1:8000/
    • -
    -
  • -
- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/documentation/getting-started-dev/index.html b/docs/v3/v1/documentation/getting-started-dev/index.html deleted file mode 100644 index 07a9dfaed..000000000 --- a/docs/v3/v1/documentation/getting-started-dev/index.html +++ /dev/null @@ -1,1799 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Getting Started Contributing - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Contribution Guide

-

Thank you for your interest in contributing to our work. This guide should help you get started, please let us know if you have any questions.

-

Contributor Guidance

-
    -
  • Target your pull requests to the dev branch
  • -
  • Add/Update any relevant docs articles in the relevant package's docs folder related to your changes
  • -
  • Include a test for any new functionality and ensure all existing tests are passing by running gulp test
  • -
  • Ensure tslint checks pass by typing gulp lint
  • -
  • Keep your PRs as simple as possible and describe the changes to help the reviewer understand your work
  • -
  • If you have an idea for a larger change to the library please open an issue and let's discuss before you invest many hours - these are very welcome but want to ensure it is something we can merge before you spend the time :)
  • -
-

Setup your development environment

-

These steps will help you get your environment setup for contributing to the core library.

-
    -
  1. -

    Install Visual Studio Code - this is the development environment we will use. It is similar to a light-weight Visual Studio designed for each editing of client file types such as .ts and .js. (Note that if you prefer you can use Visual Studio).

    -
  2. -
  3. -

    Install Node JS - this provides two key capabilities; the first is the nodejs server which will act as our development server (think iisexpress), the second is npm a package manager (think nuget).

    -
  4. -
  5. -

    On Windows: Install Python v2.7.10 - this is used by some of the plug-ins and build tools inside Node JS - (Python v3.x.x is not supported by those modules). If Visual Studio is not installed on the client in addition to this C++ runtime is required. Please see node-gyp Readme

    -
  6. -
  7. -

    Install a console emulator of your choice, for Windows Cmder is popular. If installing Cmder choosing the full option will allow you to use git for windows. Whatever option you choose we will refer in the rest of the guide to "console" as the thing you installed in this step.

    -
  8. -
  9. -

    Install the tslint extension in VS Code:

    -
      -
    1. Press Shift + Ctrl + "p" to open the command panel
    2. -
    3. Begin typing "install extension" and select the command when it appears in view
    4. -
    5. Begin typing "tslint" and select the package when it appears in view
    6. -
    7. Restart Code after installation
    8. -
    -
  10. -
  11. -

    Install the gulp command line globally by typing the following code in your console npm install -g gulp-cli

    -
  12. -
  13. -

    Now we need to fork and clone the git repository. This can be done using your console or using your preferred Git GUI tool.

    -
  14. -
  15. -

    Once you have the code locally, navigate to the root of the project in your console. Type the following command:

    -
  16. -
  17. -

    npm install - installs all of the npm package dependencies (may take awhile the first time)

    -
  18. -
  19. -

    Copy settings.example.js in the root of your project to settings.js. Edit settings.js to reflect your personal environment (usename, password, siteUrl, etc.).

    -
  20. -
  21. -

    Then you can follow the guidance in the debugging article to get started testing right away!

    -
  22. -
- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/documentation/getting-started/index.html b/docs/v3/v1/documentation/getting-started/index.html deleted file mode 100644 index 6bb4f0766..000000000 --- a/docs/v3/v1/documentation/getting-started/index.html +++ /dev/null @@ -1,2280 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Getting Started - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - - - - -
-
- - - - - -

Getting Started

-

These libraries are geared towards folks working with TypeScript but will work equally well for JavaScript projects. To get started you need to install -the libraries you need via npm. Many of the packages have a peer dependency to other packages with the @pnp namespace meaning you may need to install -more than one package. All packages are released together eliminating version confusion - all packages will depend on packages with the same version number.

-

If you need to support older browsers please review the article on polyfills for required functionality.

-

Install

-

First you will need to install those libraries you want to use in your application. Here we will install the most frequently used packages. This step applies to any -environment or project.

-

npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/graph --save

-

Next we can import and use the functionality within our application. The below is a very simple example, please see the individual package documentation -for more details.

-
import { getRandomString } from "@pnp/core";
-
-(function() {
-
-  // get and log a random string
-  console.log(getRandomString(20));
-
-})()
-
- - -

Getting Started with SharePoint Framework

-

The @pnp/sp and @pnp/graph libraries are designed to work seamlessly within SharePoint Framework projects with a small amount of upfront configuration. If you are running in 2016 on-premises please read this note on a workaround for the included TypeScript version. If you are targetting SharePoint online you do not need to take any additional steps.

-

Establish Context

-

Because SharePoint Framework provides a local context to each component we need to set that context within the library. This allows us to determine request -urls as well as use the SPFx HttpGraphClient within @pnp/graph. There are two ways to provide the spfx context to the library. Either through the setup method -imported from @pnp/core or using the setup method on either the @pnp/sp or @pnp/graph main export. All three are shown below and are equivalent, meaning if -you are already importing the sp variable from @pnp/sp or the graph variable from @pnp/graph you should use their setup method to reduce imports.

-

The setup is always done in the onInit method to ensure it runs before your other lifecycle code. You can also set any other settings at this time.

-

Using @pnp/core setup

-
import { setup as pnpSetup } from "@pnp/core";
-
-// ...
-
-public onInit(): Promise<void> {
-
-  return super.onInit().then(_ => {
-
-    // other init code may be present
-
-    pnpSetup({
-      spfxContext: this.context
-    });
-  });
-}
-
-// ...
-
- - -

Using @pnp/sp setup

-
import { sp } from "@pnp/sp";
-
-// ...
-
-public onInit(): Promise<void> {
-
-  return super.onInit().then(_ => {
-
-    // other init code may be present
-
-    sp.setup({
-      spfxContext: this.context
-    });
-  });
-}
-
-// ...
-
- - -

Using @pnp/graph setup

-
import { graph } from "@pnp/graph";
-
-// ...
-
-public onInit(): Promise<void> {
-
-  return super.onInit().then(_ => {
-
-    // other init code may be present
-
-    graph.setup({
-      spfxContext: this.context
-    });
-  });
-}
-
-// ...
-
- - -

Establish Context with an SPFx Service

-

Because you do not have access to the full context object within a service you need to setup things slightly differently. This works for the sp library, but not the graph library as we don't have access to the AAD token provider from the full context.

-
import { ServiceKey, ServiceScope } from "@microsoft/sp-core-library";
-import { PageContext } from "@microsoft/sp-page-context";
-import { AadTokenProviderFactory } from "@microsoft/sp-http";
-import { sp } from "@pnp/sp";
-
-export interface ISampleService {
-  getLists(): Promise<any[]>;
-}
-
-export class SampleService {
-
-  public static readonly serviceKey: ServiceKey<ISampleService> = ServiceKey.create<ISampleService>('SPFx:SampleService', SampleService);
-
-  constructor(serviceScope: ServiceScope) {
-
-    serviceScope.whenFinished(() => {
-
-      const pageContext = serviceScope.consume(PageContext.serviceKey);
-      const tokenProviderFactory = serviceScope.consume(AadTokenProviderFactory.serviceKey);
-
-      // we need to "spoof" the context object with the parts we need for PnPjs
-      sp.setup({
-        spfxContext: {
-          aadTokenProviderFactory: tokenProviderFactory,
-          pageContext: pageContext,
-        }
-      });
-
-      // This approach also works if you do not require AAD tokens
-      // you don't need to do both
-      // sp.setup({
-      //   sp : {
-      //     baseUrl : pageContext.web.absoluteUrl
-      //   }
-      // });
-    });
-  }
-  public getLists(): Promise<any[]> {
-    return sp.web.lists.get();
-  }
-}
-
- - -

Connect to SharePoint from Node

-

Because peer dependencies are not installed automatically you will need to list out each package to install. Don't worry if you forget one you will get a message -on the command line that a peer dependency is missing. Let's for example look at installing the required libraries to connect to SharePoint from nodejs. You can see -./debug/launch/sp.ts for a live example.

-
npm i @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/nodejs
-
- - -

This will install the logging, common, odata, sp, and nodejs packages. You can read more about what each package does starting on the packages page. -Once these are installed you need to import them into your project, to communicate with SharePoint from node we'll need the following imports:

-
import { sp } from "@pnp/sp";
-import { SPFetchClient } from "@pnp/nodejs";
-
- - -

Once you have imported the necessary resources you can update your code to setup the node fetch client as well as make a call to SharePoint.

-
// configure your node options (only once in your application)
-sp.setup({
-    sp: {
-        fetchClientFactory: () => {
-            return new SPFetchClient("{site url}", "{client id}", "{client secret}");
-        },
-    },
-});
-
-// make a call to SharePoint and log it in the console
-sp.web.select("Title", "Description").get().then(w => {
-    console.log(JSON.stringify(w, null, 4));
-});
-
- - -

Connect to Microsoft Graph From Node

-

Similar to the above you can also make calls to the Graph api from node using the libraries. Again we start with installing the required resources. You can see -./debug/launch/graph.ts for a live example.

-
npm i @pnp/logging @pnp/core @pnp/queryable @pnp/graph @pnp/nodejs
-
- - -

Now we need to import what we'll need to call graph

-
import { graph } from "@pnp/graph";
-import { AdalFetchClient } from "@pnp/nodejs";
-
- - -

Now we can make our graph calls after setting up the Adal client. Note you'll need to setup an AzureAD App registration with the necessary permissions.

-
graph.setup({
-    graph: {
-        fetchClientFactory: () => {
-            return new AdalFetchClient("{mytenant}.onmicrosoft.com", "{application id}", "{application secret}");
-        },
-    },
-});
-
-// make a call to Graph and get all the groups
-graph.v1.groups.get().then(g => {
-    console.log(JSON.stringify(g, null, 4));
-});
-
- - -

Getting Started outside SharePoint Framework

-

In some cases you may be working in a way such that we cannot determine the base url for the web. In this scenario you have two options.

-

Set baseUrl through setup:

-

Here we are setting the baseUrl via the sp.setup method. We are also setting the headers to use verbose mode, something you may have to do when -working against unpatched versions of SharePoint 2013 as discussed here. -This is optional for 2016 or SharePoint Online.

-
import { sp } from "@pnp/sp";
-
-sp.setup({
-  sp: {
-    headers: {
-      Accept: "application/json;odata=verbose",
-    },
-    baseUrl: "{Absolute SharePoint Web URL}"
-  },
-});
-
-const w = await sp.web.get();
-
- - -

Create Web instances directly

-

Using this method you create the web directly with the url you want to use as the base.

-
import { Web } from "@pnp/sp";
-
-const web = new Web("{Absolute SharePoint Web URL}");
-const w = await web.get();
-
- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/documentation/gulp-commands/index.html b/docs/v3/v1/documentation/gulp-commands/index.html deleted file mode 100644 index 623389176..000000000 --- a/docs/v3/v1/documentation/gulp-commands/index.html +++ /dev/null @@ -1,2100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Gulp Commands - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
- -
- -
- - - - - - - - - - -
-
- - -
-
-
- -
-
-
- - -
-
-
- - -
-
-
- - -
-
- - - - - -

Gulp Commands

-

This library uses Gulp to orchestrate various tasks. The tasks described below are available for your use. Please review the -getting started for development to ensure you've setup your environment correctly. The source for the gulp commands can be found in -the tools\gulptasks folder at the root of the project.

-

Basics

-

All gulp commands are run on the command line in the fashion shown below.

-
gulp <command> [optional pararms]
-
- - -

build

-

The build command transpiles the solution from TypeScript into JavaScript using our custom build system. It is controlled by the pnp-build.js file at -the project root.

-

Build all of the packages

-
gulp build
-
- - -

Building individual packages

-

Note when building a single package none of the dependencies are currently built, so you need to specify in order those packages to build which are dependencies.

-
# fails
-gulp build --p sp
-
-# works as all the dependencies are built in order
-gulp build --p logging,common,odata,sp
-
- - -

You can also build the packages and then not clean using the nc flag. So for example if you are working on the sp package you can build all the packages once, then -use the "nc" flag to leave those that aren't changing.

-
# run once
-gulp build --p logging,common,odata,sp
-
-# run on subsequent builds
-gulp build --p sp --nc
-
- - -

clean

-

The clean command removes all of the generated folders from the project and is generally used automatically before other commands to ensure there is a clean workspace.

-
gulp clean
-
- - -

To clean the build folder. This build folder is no longer included in automatic cleaning after the move to use the TypeScript project references feature that compares previous output and doesn't rebuild unchanged files. This command will erase the entire build folder ensuring you can conduct a clean build/test/etc.

-
gulp clean-build
-
- - -

lint

-

Runs the project linting based on the tslint.json rules defined at the project root. This should be done before any PR submissions as linting failures will block merging.

-
gulp lint
-
- - -

package

-

Used to create the packages in the ./dist folder as they would exist for a release.

-
gulp package
-
- - -

Packaging individual packages

-

You can also package individual packages, but as with build you must also package any dependencies at the same time.

-
gulp package --p logging,common,odata,sp
-
- - -

publish

-

This command is only for use by package authors to publish a version to npm and is not for developer use.

-

serve

-

The serve command allows you to serve either code from the ./debug/serve folder OR an individual package for testing in the browser. The file will always be served as -https://localhost:8080/assets/pnp.js so can create a static page in your tenant for easy testing of a variety of scenarios. NOTE that in most browsers this file will -be flagged as unsafe so you will need to trust it for it to execute on the page.

-

debug serve

-

When running the command with no parameters you will generate a package with the entry being based on the tsconfig.json file in ./debug/serve. By default this will -use serve.ts. This allows you to write any code you want to test to easily run it in the browser with all changes being watched and triggering a rebuild.

-
gulp serve
-
- - -

package serve

-

If instead you want to test how a particular package will work in the browser you can serve just that package. In this case you do not need to specify the dependencies -and specifying multiple packages will throw an error. Packages will be injected into the global namespace on a variable named pnp.

-
gulp serve --p sp
-
- - -

test

-

Runs the tests specified in each package's tests folder

-
gulp test
-
- - -

Verbose

-

The test command will switch to the "spec" mocha reporter if you supply the verbose flag. Doing so will list out each test's description and sucess instead of the "dot" used by default. This flag works with all other test options.

-
gulp test --verbose
-
- - -

Test individual packages

-

You can test individual packages as needed, and there is no need to include dependencies in this case

-
# test the logging and sp packages
-gulp test --p logging,sp
-
- - -

If you are working on a specific set of tests for a single module you can also use the single or s parameter to select just -a single module of tests. You specify the filename without the ".test.ts" suffix. It must be within the specified package and -this option can only be used with a single package for --p

-
# will test only the client-side pages module within the sp package
-gulp test --p sp --s clientsidepages
-
- - -

If you want you can test within the same site and avoid creating a new one, though for some tests this might cause conflicts. -This flag can be helpful if you are rapidly testing things with no conflict as you can avoid creating a site each time. Works -with both of the above options --p and --s as well as individually. The url must be absolute.

-
#testing using the specified site.
-gulp test --site https://{tenant}.sharepoint.com/sites/testing
-
-# with other options
-gulp test --p logging,sp --site https://{tenant}.sharepoint.com/sites/testing
-
-gulp test --p sp --s clientsidepages --site https://{tenant}.sharepoint.com/sites/testing
-
- - - - - - - - - -
-
-
-
- - - - -
- - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/documentation/img/Logo.png b/docs/v3/v1/documentation/img/Logo.png deleted file mode 100644 index 73dc570b38549a22788bd8911d7da860bb191ae4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3151 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 z 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 z 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

    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    Package Structure

    -

    Each of the packages is published with the same structure, so this article applies to all of the packages. We will use @pnp/core as an example for discussion.

    -

    Folders

    -

    In addition to the files in the root each package has three folders dist, docs, and src.

    -

    Root Files

    -

    These files are found at the root of each package.

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    FileDescription
    index.d.tsReferenced in package.json typings property and provides the TypeScript type information for consumers
    LICENSEPackage license
    package.jsonnpm package definition
    readme.mdBasic readme referencing the docs site
    -

    Dist

    -

    The dist folder contains the transpiled files bundled in various ways. You can choose the best file for your usage as needed. Below the {package} will be -replaced with the name of the package - in our examples case this would be "common" making the file name "{package}.es5.js" = "common.es5.js". All of the *.map -files are the debug mapping files related to the .js file of the same name.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    FileDescription
    {package}.es5.jsLibrary packaged in es5 format not wrapped as a module
    {package}.es5.umd.bundle.jsThe library bundled with all dependencies into a single UMD module. Global variable will be "pnp.{package}". Referenced in the main property of package.json
    {package}.es5.umd.bundle.min.jsMinified version of the bundled umd module
    {package}.es5.umd.jsThe library in es5 bundled as a UMD modules with no included dependencies. They are designed to work with the other *.es5.umd.js files. Referenced in the module property of package.json
    {package}.es5.umd.min.jsMinified version of the es5 umd module
    {package}.jses6 format file of the library. Referenced by es2015 property of package.json
    -

    Docs

    -

    This folder contains markdown documentation for the library. All packages will include an index.md which serves as the root of the docs. These files are also used -to build the public site. To edit these files they can be found in the packages/{package}/docs folder.

    -

    Src

    -

    Contains the TypeScript definition files refrenced by the index.d.ts in the package root. These files serve to provide typing information about the library to -consumers who can process typing information.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/documentation/packages/index.html b/docs/v3/v1/documentation/packages/index.html deleted file mode 100644 index c7e8d6a0d..000000000 --- a/docs/v3/v1/documentation/packages/index.html +++ /dev/null @@ -1,1741 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Packages - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - -
    -
    - - - - - -

    Packages

    - -

    The following packages comprise the Patterns and Practices client side libraries. All of the packages are published as a set and depend on their peers within -the @pnp scope.

    -

    The latest published version is ****.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    @pnp/
    commonProvides shared functionality across all pnp libraries
    config-storeProvides a way to manage configuration within your application
    graphProvides a fluent api for working with Microsoft Graph
    loggingLight-weight, subscribable logging framework
    nodejsProvides functionality enabling the @pnp libraries within nodejs
    odataProvides shared odata functionality and base classes
    pnpjsRollup library of core functionality (mimics sp-pnp-js)
    spProvides a fluent api for working with SharePoint REST
    sp-addinhelpersProvides functionality for working within SharePoint add-ins
    sp-clientsvcProvides base classes for working with the legacy SharePoint
    sp-taxonomyProvides a fluent api for working with SharePoint Managed Metadata
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/documentation/polyfill/index.html b/docs/v3/v1/documentation/polyfill/index.html deleted file mode 100644 index d3cf37eec..000000000 --- a/docs/v3/v1/documentation/polyfill/index.html +++ /dev/null @@ -1,1877 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Polyfills - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    Polyfills

    -

    These libraries may make use of some features not found in older browsers, mainly fetch, Map, and Proxy. This primarily affects Internet Explorer 11, which requires that we provide this missing functionality. There are several ways to include this missing functionality.

    -

    IE 11 Polyfill package

    -

    We created a package you can use to include the needed functionality without having to determine what polyfills are required. Also, this package is independent of the other @pnp/* packages and does not need to be updated monthly unless we introduce additional polyfills and publish a new version. This package is only needed if you need to support IE 11.

    -

    Install

    -

    npm install --save @pnp/polyfill-ie11

    -

    Use

    -
    import "@pnp/polyfill-ie11";
    -import { sp } from "@pnp/sp";
    -
    -sp.web.lists.getByTitle("BigList").items.filter(`ID gt 6000`).get().then(r => {
    -  this.domElement.innerHTML += r.map(l => `${l.Title}<br />`);
    -});
    -
    - - -

    SearchQueryBuilder

    -

    Because the latest version of SearchQueryBuilder uses Proxy internally you can fall back on the older version for IE 11 as shown below.

    -
    import "@pnp/polyfill-ie11";
    -import { SearchQueryBuilder } from "@pnp/polyfill-ie11/dist/searchquerybuilder";
    -import { sp, ISearchQueryBuilder } from "@pnp/sp";
    -
    -// works in IE11 and other browsers
    -const builder: ISearchQueryBuilder = SearchQueryBuilder().text("test");
    -
    -sp.search(builder).then(r => {
    -  this.domElement.innerHTML = JSON.stringify(r);
    -});
    -
    - - -

    Polyfill Service

    -

    If acceptable to your design and security requirements you can use a service to provide missing functionality. This loads scripts from a service outside of your and our -control, so please ensure you understand any associated risks.

    -

    To use this option you need to wrap the code in a function, here called "stuffisloaded". Then you need to add another script tag as shown below that will load what you need from the polyfill service. Note the parameter "callback" takes our function name.

    -
    <script src="https://cdnjs.cloudflare.com/ajax/libs/pnp-pnpjs/1.2.1/pnpjs.es5.umd.bundle.min.js" type="text/javascript"></script>
    -<script>
    -// this function will be executed once the polyfill is loaded.
    -function stuffisloaded() {
    -
    -  pnp.sp.web.select("Title").get()
    -    .then(function(data){
    -      document.getElementById("main").innerText=data.Title;
    -  })   
    -  .catch(function(err){  
    -    document.getElementById("main").innerText=err;
    -  });
    -}
    -</script>
    -<!-- This script tag loads the required polyfills from the service -->
    -<script src="https://cdn.polyfill.io/v2/polyfill.min.js?callback=stuffisloaded&features=es6,fetch,Map&flags=always,gated"></script>
    -
    - - -

    Module Loader

    -

    If you are using a module loader you need to load the following two files as well. You can do this form a CDN or your style library.

    -
      -
    1. Download the es6-promises polyfill from https://github.com/stefanpenner/es6-promise and upload it to your style library.
    2. -
    3. Download the fetch polyfill from https://github.com/github/fetch and upload it to your style library.
    4. -
    5. Download the corejs polyfill from https://github.com/zloirock/core-js and upload it to your style library.
    6. -
    7. Update your module loader to set these files as dependencies before the pnp library is opened.
    8. -
    -

    One issue you still may see is that you get errors that certain libraries are undefined when you try to run your code. This is because your code is running before -these libraries are loaded. You need to ensure that all dependencies are loaded before making use of the pnp libraries.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/documentation/theme/main.html b/docs/v3/v1/documentation/theme/main.html deleted file mode 100644 index 2f621a908..000000000 --- a/docs/v3/v1/documentation/theme/main.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "base.html" %} - -{% block analytics %} - spacer -{% endblock %} diff --git a/docs/v3/v1/documentation/transition-guide/index.html b/docs/v3/v1/documentation/transition-guide/index.html deleted file mode 100644 index 16bb9f481..000000000 --- a/docs/v3/v1/documentation/transition-guide/index.html +++ /dev/null @@ -1,1977 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Transition Guide - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    Transition Guide

    -

    These libraries are based on the sp-pnp-js library and our goal was to make transition as easy as possible. The most -obvious difference is the splitting of the library into multiple packages. We have however created a rollup library to help folks make the move - though our -recommendation is to switch to the separate packages. This article outlines transitioning your existing projects from sp-pnp-js to the new libraries, please provide -feedback on how we can improve out guidance.

    -

    Installing @pnp libraries

    -

    With the separation of the packages we needed a way to indicate how they are related, while making things easy for folks to track and update and we have used peer -dependencies between the packages to do this. With each release we will release all packages so that the version numbers move in lock-step, making it easy to ensure -you are working with compatible versions. One thing to keep in mind with peer dependencies is that they are not automatically installed. The advantage is you -will only have one copy of each library in your project.

    -

    Installing peer dependencies is easy, you can specify each of the packages in a single line, here we are installing everything required to use the @pnp/sp package.

    -
    npm i @pnp/logging @pnp/core @pnp/queryable @pnp/sp
    -
    - - -

    If you do not install all of the peer dependencies you will get a message specifying which ones are missing along with the version expected.

    -

    Import Simplification

    -

    With the separation of packages we have also simplified the imports, and allowed you more control over what you are importing. Compare these two examples showing -the same set of imports, but one is done via sp-pnp-js and the other using the @pnp libraries.

    -

    From sp-pnp-js

    -
    import pnp, {
    -  Web,
    -  Util,
    -  Logger,
    -  FunctionListener,
    -  LogLevel,
    -} from "sp-pnp-js";
    -
    - - -

    From @pnp libraries

    -
    import {
    -  Logger,
    -  LogLevel,
    -  FunctionListener
    -} from "@pnp/logging";
    -
    -import * as Util from "@pnp/core";
    -
    -import {
    -  sp,
    -  Web
    -} from "@pnp/sp";
    -
    - - -

    In the above example the "sp" import replaces "pnp" and is the root of your method chains. Once we have updated our imports we have a few small code changes to make, -depending on how you have used the library in your applications. Watch this short video discussing the most common updates:

    - - -

    Updated settings file format

    -

    If you are doing local debugging or testing you have likely created a settings.js from the supplied settings.example.js. Please note the format of that file has changed, -the new format is shown below.

    -
    var settings = {
    -
    -    spsave: {
    -        username: "develina.devsson@mydevtenant.onmicrosoft.com",
    -        password: "pass@word1",
    -        siteUrl: "https://mydevtenant.sharepoint.com/"
    -    },
    -    testing: {
    -        enableWebTests: true,
    -        sp: {
    -            id: "{ client id }",
    -            secret: "{ client secret }",
    -            url: "{ site collection url }",
    -            notificationUrl: "{ notification url }",
    -        },
    -        graph: {
    -            tenant: "{tenant.onmicrosoft.com}",
    -            id: "{your app id}",
    -            secret: "{your secret}"
    -        },
    -    }
    -}
    -
    - - -

    HttpClient Renamed

    -

    If you used HttpClient from sp-pnp-js it was renamed to SPHttpClient. A transition to @pnp/sp assumes replacement of:

    -
    import { HttpClient } from 'sp-pnp-js';
    -
    - - -

    to the following import statement:

    -
    import { SPHttpClient } from '@pnp/sp';
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/graph/docs/contacts/index.html b/docs/v3/v1/graph/docs/contacts/index.html deleted file mode 100644 index fbe018355..000000000 --- a/docs/v3/v1/graph/docs/contacts/index.html +++ /dev/null @@ -1,2080 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - contacts - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - -

    @pnp/graph/contacts

    -

    The ability to manage contacts and folders in Outlook is a capability introduced in version 1.2.2 of @pnp/graph. Through the methods described -you can add and edit both contacts and folders in a users Outlook.

    -

    Get all of the Contacts

    -

    Using the contacts() you can get the users contacts from Outlook

    -
    import { graph } from "@pnp/graph";
    -
    -const contacts = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.get();
    -
    -const contacts = await graph.me.contacts.get();
    -
    - - -

    Add a new Contact

    -

    Using the contacts.add() you can a add Contact to the users Outlook

    -
    import { graph } from "@pnp/graph";
    -
    -const addedContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.add('Pavel', 'Bansky', [<EmailAddress>{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);
    -
    -const addedContact = await graph.me.contacts.add('Pavel', 'Bansky', [<EmailAddress>{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);
    -
    - - -

    Get Contact by Id

    -

    Using the contacts.getById() you can get one of the users Contacts in Outlook

    -
    import { graph } from "@pnp/graph";
    -
    -const contact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById('userId');
    -
    -const contact = await graph.me.contacts.getById('userId');
    -
    - - -

    Delete a Contact

    -

    Using the delete you can remove one of the users Contacts in Outlook

    -
    import { graph } from "@pnp/graph";
    -
    -const delContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById('userId').delete();
    -
    -const delContact = await graph.me.contacts.getById('userId').delete();
    -
    - - -

    Update a Contact

    -

    Using the update you can update one of the users Contacts in Outlook

    -
    import { graph } from "@pnp/graph";
    -
    -const updContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById('userId').update({birthday: "1986-05-30" });
    -
    -const updContact = await graph.me.contacts.getById('userId').update({birthday: "1986-05-30" });
    -
    - - -

    Get all of the Contact Folders

    -

    Using the contactFolders() you can get the users Contact Folders from Outlook

    -
    import { graph } from "@pnp/graph";
    -
    -const contactFolders = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.get();
    -
    -const contactFolders = await graph.me.contactFolders.get();
    -
    - - -

    Add a new Contact Folder

    -

    Using the contactFolders.add() you can a add Contact Folder to the users Outlook

    -
    import { graph } from "@pnp/graph";
    -
    -const addedContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.add('displayName', '<ParentFolderId>');
    -
    -const addedContactFolder = await graph.me.contactFolders.contactFolders.add('displayName', '<ParentFolderId>');
    -
    - - -

    Get Contact Folder by Id

    -

    Using the contactFolders.getById() you can get one of the users Contact Folders in Outlook

    -
    import { graph } from "@pnp/graph";
    -
    -const contactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById('folderId');
    -
    -const contactFolder = await graph.me.contactFolders.getById('folderId');
    -
    - - -

    Delete a Contact Folder

    -

    Using the delete you can remove one of the users Contact Folders in Outlook

    -
    import { graph } from "@pnp/graph";
    -
    -const delContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById('folderId').delete();
    -
    -const delContactFolder = await graph.me.contactFolders.getById('folderId').delete();
    -
    - - -

    Update a Contact Folder

    -

    Using the update you can update one of the users Contact Folders in Outlook

    -
    import { graph } from "@pnp/graph";
    -
    -const updContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById('userId').update({displayName: "value" });
    -
    -const updContactFolder = await graph.me.contactFolders.getById('userId').update({displayName: "value" });
    -
    - - -

    Get all of the Contacts from the Contact Folder

    -

    Using the contacts() in the Contact Folder gets the users Contact from the folder.

    -
    import { graph } from "@pnp/graph";
    -
    -const contactsInContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById('folderId').contacts.get();
    -
    -const contactsInContactFolder = await graph.me.contactFolders.getById('folderId').contacts.get();
    -
    - - -

    Get Child Folders of the Contact Folder

    -

    Using the childFolders() you can get the Child Folders of the current Contact Folder from Outlook

    -
    import { graph } from "@pnp/graph";
    -
    -const childFolders = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById('<id>').childFolders.get();
    -
    -const childFolders = await graph.me.contactFolders.getById('<id>').childFolders.get();
    -
    - - -

    Add a new Child Folder

    -

    Using the childFolders.add() you can a add Child Folder in a Contact Folder

    -
    import { graph } from "@pnp/graph";
    -
    -const addedChildFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById('<id>').childFolders.add('displayName', '<ParentFolderId>');
    -
    -const addedChildFolder = await graph.me.contactFolders.getById('<id>').childFolders.add('displayName', '<ParentFolderId>');
    -
    - - -

    Get Child Folder by Id

    -

    Using the childFolders.getById() you can get one of the users Child Folders in Outlook

    -
    import { graph } from "@pnp/graph";
    -
    -const childFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById('<id>').childFolders.getById('folderId');
    -
    -const childFolder = await graph.me.contactFolders.getById('<id>').childFolders.getById('folderId');
    -
    - - -

    Add Contact in Child Folder of Contact Folder

    -

    Using contacts.add in the Child Folder of a Contact Folder, adds a new Contact to that folder

    -
    import { graph } from "@pnp/graph";
    -
    -const addedContact = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById('<id>').childFolders.getById('folderId').contacts.add('Pavel', 'Bansky', [<EmailAddress>{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);
    -
    -const addedContact = await graph.me.contactFolders.getById('<id>').childFolders.getById('folderId').contacts.add('Pavel', 'Bansky', [<EmailAddress>{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/graph/docs/directoryobjects/index.html b/docs/v3/v1/graph/docs/directoryobjects/index.html deleted file mode 100644 index 2f1fc4f1b..000000000 --- a/docs/v3/v1/graph/docs/directoryobjects/index.html +++ /dev/null @@ -1,1865 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - directory objects - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - -

    @pnp/graph/directoryObjects

    -

    The groups and directory roles for the user

    -
    import { graph } from "@pnp/graph";
    -
    -const memberOf = await graph.users.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').memberOf.get();
    -
    -const memberOf = await graph.me.memberOf.get();
    -
    - - -

    Return all the groups the user, group or directoryObject is a member of

    -
    import { graph } from "@pnp/graph";
    -
    -const memberGroups = await graph.users.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').getMemberGroups();
    -
    -const memberGroups = await graph.me.getMemberGroups();
    -
    -const memberGroups = await graph.groups.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').getMemberGroups();
    -
    -const memberGroups = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').getMemberGroups();
    -
    - - -

    Returns all the groups, administrative units and directory roles that a user, group, or directory object is a member of.

    -
    import { graph } from "@pnp/graph";
    -
    -const memberObjects = await graph.users.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').getMemberObjects();
    -
    -const memberObjects = await graph.me.getMemberObjects();
    -
    -const memberObjects = await graph.groups.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').getMemberObjects();
    -
    -const memberObjects = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').getMemberObjects();
    -
    - - -

    Check for membership in a specified list of groups

    -

    And returns from that list those groups of which the specified user, group, or directory object is a member

    -
    import { graph } from "@pnp/graph";
    -
    -const checkedMembers = await graph.users.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').checkMemberGroups(["c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741","2001bb09-1d46-40a6-8176-7bb867fb75aa"]);
    -
    -const checkedMembers = await graph.me.checkMemberGroups(["c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741","2001bb09-1d46-40a6-8176-7bb867fb75aa"]);
    -
    -const checkedMembers = await graph.groups.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').checkMemberGroups(["c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741","2001bb09-1d46-40a6-8176-7bb867fb75aa"]);
    -
    -const checkedMembers = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').checkMemberGroups(["c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741","2001bb09-1d46-40a6-8176-7bb867fb75aa"]);
    -
    - - -

    Get directoryObject by Id

    -
    import { graph } from "@pnp/graph";
    -
    -const dirObject = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').get();
    -
    - - -

    Delete directoryObject

    -
    import { graph } from "@pnp/graph";
    -
    -const deleted = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').delete()
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/graph/docs/index.html b/docs/v3/v1/graph/docs/index.html deleted file mode 100644 index d3a4e2b65..000000000 --- a/docs/v3/v1/graph/docs/index.html +++ /dev/null @@ -1,1858 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - graph - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/graph

    -

    npm version

    -

    This package contains the fluent api used to call the graph rest services.

    -

    Getting Started

    -

    Install the library and required dependencies

    -

    npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save

    -

    Import the library into your application and access the root sp object

    -
    import { graph } from "@pnp/graph";
    -
    -(function main() {
    -
    -    // here we will load the current web's properties
    -    graph.groups.get().then(g => {
    -
    -        console.log(`Groups: ${JSON.stringify(g, null, 4)}`);
    -    });
    -})()
    -
    - - -

    Getting Started with SharePoint Framework

    -

    Install the library and required dependencies

    -

    npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save

    -

    Import the library into your application, update OnInit, and access the root sp object in render

    -
    import { graph } from "@pnp/graph";
    -
    -// ...
    -
    -public onInit(): Promise<void> {
    -
    -  return super.onInit().then(_ => {
    -
    -    // other init code may be present
    -
    -    graph.setup({
    -      spfxContext: this.context
    -    });
    -  });
    -}
    -
    -// ...
    -
    -public render(): void {
    -
    -    // A simple loading message
    -    this.domElement.innerHTML = `Loading...`;
    -
    -    // here we will load the current web's properties
    -    graph.groups.get().then(groups => {
    -
    -        this.domElement.innerHTML = `Groups: <ul>${groups.map(g => `<li>${g.displayName}</li>`).join("")}</ul>`;
    -    });
    -}
    -
    - - -

    Getting Started on Nodejs

    -

    Install the library and required dependencies

    -

    npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph @pnp/nodejs --save

    -

    Import the library into your application, setup the node client, make a request

    -
    import { graph } from "@pnp/graph";
    -import { AdalFetchClient } from "@pnp/nodejs";
    -
    -// do this once per page load
    -graph.setup({
    -    graph: {
    -        fetchClientFactory: () => {
    -            return new AdalFetchClient("{tenant}.onmicrosoft.com", "AAD Application Id", "AAD Application Secret");
    -        },
    -    },
    -});
    -
    -// here we will load the groups information
    -graph.groups.get().then(g => {
    -
    -    console.log(`Groups: ${JSON.stringify(g, null, 4)}`);
    -});
    -
    - - -

    UML

    -

    Graphical UML diagram

    -

    Graphical UML diagram of @pnp/graph. Right-click the diagram and open in new tab if it is too small.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/graph/docs/insights/index.html b/docs/v3/v1/graph/docs/insights/index.html deleted file mode 100644 index 56bab1d8f..000000000 --- a/docs/v3/v1/graph/docs/insights/index.html +++ /dev/null @@ -1,1698 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @pnp/graph/insights - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/graph/insights

    -

    Insights are relationships calculated using advanced analytics and machine learning techniques. You can, for example, identify OneDrive documents trending around users.

    - -

    Using the trending() returns documents from OneDrive and from SharePoint sites trending around a user.

    -
    import { graph } from "@pnp/graph";
    -
    -const trending = await graph.users.getById('user@tenant.onmicrosoft.com').insights.trending.get();
    -
    -const trending = await graph.me.insights.trending.get();
    -
    - - -

    Get the used documents

    -

    Using the used() returns documents viewed and modified by a user. Includes documents the user used in OneDrive for Business, SharePoint, opened as email attachments, and as link attachments from sources like Box, DropBox and Google Drive.

    -
    import { graph } from "@pnp/graph";
    -
    -const used = await graph.users.getById('user@tenant.onmicrosoft.com').insights.used.get();
    -
    -const used = await graph.me.insights.used.get();
    -
    - - -

    Get the shared documents

    -

    Using the shared() returns documents shared with a user. Documents can be shared as email attachments or as OneDrive for Business links sent in emails.

    -
    import { graph } from "@pnp/graph";
    -
    -const shared = await graph.users.getById('user@tenant.onmicrosoft.com').insights.shared.get();
    -
    -const shared = await graph.me.insights.shared.get();
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/graph/docs/invitations/index.html b/docs/v3/v1/graph/docs/invitations/index.html deleted file mode 100644 index 74d50db4c..000000000 --- a/docs/v3/v1/graph/docs/invitations/index.html +++ /dev/null @@ -1,1742 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - invitations - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/graph/invitations

    -

    The ability invite an external user via the invitation manager

    -

    Create Invitation

    -

    Using the invitations.create() you can create an Invitation. -We need the email address of the user being invited and the URL user should be redirected to once the invitation is redeemed (redirect URL).

    -
    import { graph } from "@pnp/graph";
    -
    -const invitationResult = await graph.invitations.create('external.user@emailadress.com', 'https://tenant.sharepoint.com/sites/redirecturi');
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/graph/docs/onedrive/index.html b/docs/v3/v1/graph/docs/onedrive/index.html deleted file mode 100644 index c52e8090e..000000000 --- a/docs/v3/v1/graph/docs/onedrive/index.html +++ /dev/null @@ -1,2086 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - onedrive - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - -

    @pnp/graph/onedrive

    -

    The ability to manage drives and drive items in Onedrive is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described -you can manage drives and drive items in Onedrive.

    -

    Get the default drive

    -

    Using the drive() you can get the default drive from Onedrive

    -
    import { graph } from "@pnp/graph";
    -
    -const drives = await graph.users.getById('user@tenant.onmicrosoft.com').drives.get();
    -
    -const drives = await graph.me.drives.get();
    -
    - - -

    Get all of the drives

    -

    Using the drives() you can get the users available drives from Onedrive

    -
    import { graph } from "@pnp/graph";
    -
    -const drives = await graph.users.getById('user@tenant.onmicrosoft.com').drives.get();
    -
    -const drives = await graph.me.drives.get();
    -
    - - -

    Get drive by Id

    -

    Using the drives.getById() you can get one of the available drives in Outlook

    -
    import { graph } from "@pnp/graph";
    -
    -const drive = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId');
    -
    -const drive = await graph.me.drives.getById('driveId');
    -
    - - -

    Get the associated list of a drive

    -

    Using the list() you get the associated list

    -
    import { graph } from "@pnp/graph";
    -
    -const list = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').list.get();
    -
    -const list = await graph.me.drives.getById('driveId').list.get();
    -
    - - -

    Get the recent files

    -

    Using the recent() you get the recent files

    -
    import { graph } from "@pnp/graph";
    -
    -const files = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').recent.get();
    -
    -const files = await graph.me.drives.getById('driveId').recent.get();
    -
    - - -

    Get the files shared with me

    -

    Using the sharedWithMe() you get the files shared with the user

    -
    import { graph } from "@pnp/graph";
    -
    -const shared = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').sharedWithMe.get();
    -
    -const shared = await graph.me.drives.getById('driveId').sharedWithMe.get();
    -
    - - -

    Get the Root folder

    -

    Using the root() you get the root folder

    -
    import { graph } from "@pnp/graph";
    -
    -const root = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root.get();
    -
    -const root = await graph.me.drives.getById('driveId').root.get();
    -
    - - -

    Get the Children

    -

    Using the children() you get the children

    -
    import { graph } from "@pnp/graph";
    -
    -const rootChildren = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root.children.get();
    -
    -const rootChildren = await graph.me.drives.getById('driveId').root.children.get();
    -
    -const itemChildren = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').children.get();
    -
    -const itemChildren = await graph.me.drives.getById('driveId').root.items.getById('itemId').children.get();
    -
    - - -

    Add folder or item

    -

    Using the add you can add a folder or an item

    -
    import { graph } from "@pnp/graph";
    -import { DriveItem as IDriveItem } from "@microsoft/microsoft-graph-types";
    -
    -const addFolder = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root.children.add('New Folder', <IDriveItem>{folder: {}});
    -
    -const addFolder = await graph.me.drives.getById('driveId').root.children.add('New Folder', <IDriveItem>{folder: {}});
    -
    - - -

    Search items

    -

    Using the search() you can search for items, and optionally select properties

    -
    import { graph } from "@pnp/graph";
    -
    -const search = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId')root.search('queryText').get();
    -
    -const search = await graph.me.drives.getById('driveId')root.search('queryText').get();
    -
    - - -

    Get specific item in drive

    -

    Using the items.getById() you can get a specific item from the current drive

    -
    import { graph } from "@pnp/graph";
    -
    -const item = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId');
    -
    -const item = await graph.me.drives.getById('driveId').items.getById('itemId');
    -
    - - -

    Get thumbnails

    -

    Using the thumbnails() you get the thumbnails

    -
    import { graph } from "@pnp/graph";
    -
    -const thumbs = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').thumbnails.get();
    -
    -const thumbs = await graph.me.drives.getById('driveId').items.getById('itemId').thumbnails.get();
    -
    - - -

    Delete drive item

    -

    Using the delete() you delete the current item

    -
    import { graph } from "@pnp/graph";
    -
    -const thumbs = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').delete();
    -
    -const thumbs = await graph.me.drives.getById('driveId').items.getById('itemId').delete();
    -
    - - -

    Update drive item

    -

    Using the update() you update the current item

    -
    import { graph } from "@pnp/graph";
    -
    -const update = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').update({name: "New Name"});
    -
    -const update = await graph.me.drives.getById('driveId').items.getById('itemId').update({name: "New Name"});
    -
    - - -

    Move drive item

    -

    Using the move() you move the current item, and optionally update it

    -
    import { graph } from "@pnp/graph";
    -
    -// Requires a parentReference to the new folder location
    -const move = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').move({ parentReference: { id: 'itemId'}}, {name: "New Name"});
    -
    -const move = await graph.me.drives.getById('driveId').items.getById('itemId').move({ parentReference: { id: 'itemId'}}, {name: "New Name"});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/graph/docs/people/index.html b/docs/v3/v1/graph/docs/people/index.html deleted file mode 100644 index 5587fbdd1..000000000 --- a/docs/v3/v1/graph/docs/people/index.html +++ /dev/null @@ -1,1664 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @pnp/graph/people - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/graph/people

    -

    The ability to retrieve a list of person objects ordered by their relevance to the user, which is determined by the user's communication and collaboration patterns, and business relationships.

    -

    Get all of the people

    -

    Using the people() you can retrieve a list of person objects ordered by their relevance to the user.

    -
    import { graph } from "@pnp/graph";
    -
    -const people = await graph.users.getById('user@tenant.onmicrosoft.com').people.get();
    -
    -const people = await graph.me.people.get();
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/graph/docs/planner/index.html b/docs/v3/v1/graph/docs/planner/index.html deleted file mode 100644 index ea2d74a80..000000000 --- a/docs/v3/v1/graph/docs/planner/index.html +++ /dev/null @@ -1,2097 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - planner - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - -

    @pnp/graph/planner

    -

    The ability to manage plans and tasks in Planner is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described -you can add, update and delete items in Planner.

    -

    Get Plans by Id

    -

    Using the planner.plans.getById() you can get a specific Plan. -Planner.plans is not an available endpoint, you need to get a specific Plan.

    -
    import { graph } from "@pnp/graph";
    -
    -const plan = await graph.planner.plans.getById('planId');
    -
    - - -

    Add new Plan

    -

    Using the planner.plans.add() you can create a new Plan.

    -
    import { graph } from "@pnp/graph";
    -
    -const newPlan = await graph.planner.plans.add('groupObjectId', 'title');
    -
    - - -

    Get Tasks in Plan

    -

    Using the tasks() you can get the Tasks in a Plan.

    -
    import { graph } from "@pnp/graph";
    -
    -const planTasks = await graph.planner.plans.getById('planId').tasks.get();
    -
    - - -

    Get Buckets in Plan

    -

    Using the buckets() you can get the Buckets in a Plan.

    -
    import { graph } from "@pnp/graph";
    -
    -const planBuckets = await graph.planner.plans.getById('planId').buckets.get();
    -
    - - -

    Get Details in Plan

    -

    Using the details() you can get the details in a Plan.

    -
    import { graph } from "@pnp/graph";
    -
    -const planDetails = await graph.planner.plans.getById('planId').details.get();
    -
    - - -

    Delete Plan

    -

    Using the delete() you can get delete a Plan.

    -
    import { graph } from "@pnp/graph";
    -
    -const delPlan = await graph.planner.plans.getById('planId').delete();
    -
    - - -

    Update Plan

    -

    Using the update() you can get update a Plan.

    -
    import { graph } from "@pnp/graph";
    -
    -const updPlan = await graph.planner.plans.getById('planId').update({title: 'New Title'});
    -
    - - -

    Get Task by Id

    -

    Using the planner.tasks.getById() you can get a specific Task. -Planner.tasks is not an available endpoint, you need to get a specific Task.

    -
    import { graph } from "@pnp/graph";
    -
    -const task = await graph.planner.tasks.getById('taskId');
    -
    - - -

    Add new Task

    -

    Using the planner.tasks.add() you can create a new Task.

    -
    import { graph } from "@pnp/graph";
    -
    -const newTask = await graph.planner.tasks.add('planId', 'title');
    -
    - - -

    Get Details in Task

    -

    Using the details() you can get the details in a Task.

    -
    import { graph } from "@pnp/graph";
    -
    -const taskDetails = await graph.planner.tasks.getById('taskId').details.get();
    -
    - - -

    Delete Task

    -

    Using the delete() you can get delete a Task.

    -
    import { graph } from "@pnp/graph";
    -
    -const delTask = await graph.planner.tasks.getById('taskId').delete();
    -
    - - -

    Update Task

    -

    Using the update() you can get update a Task.

    -
    import { graph } from "@pnp/graph";
    -
    -const updTask = await graph.planner.tasks.getById('taskId').update({properties});
    -
    - - -

    Get Buckets by Id

    -

    Using the planner.buckets.getById() you can get a specific Bucket. -planner.buckets is not an available endpoint, you need to get a specific Bucket.

    -
    import { graph } from "@pnp/graph";
    -
    -const bucket = await graph.planner.buckets.getById('bucketId');
    -
    - - -

    Add new Bucket

    -

    Using the planner.buckets.add() you can create a new Bucket.

    -
    import { graph } from "@pnp/graph";
    -
    -const newBucket = await graph.planner.buckets.add('name', 'planId');
    -
    - - -

    Update Bucket

    -

    Using the update() you can get update a Bucket.

    -
    import { graph } from "@pnp/graph";
    -
    -const updBucket = await graph.planner.buckets.getById('bucketId').update({name: "Name"});
    -
    - - -

    Delete Bucket

    -

    Using the delete() you can get delete a Bucket.

    -
    import { graph } from "@pnp/graph";
    -
    -const delBucket = await graph.planner.buckets.getById('bucketId').delete();
    -
    - - -

    Get Bucket Tasks

    -

    Using the tasks() you can get Tasks in a Bucket.

    -
    import { graph } from "@pnp/graph";
    -
    -const bucketTasks = await graph.planner.buckets.getById('bucketId').tasks.get();
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/graph/docs/security/index.html b/docs/v3/v1/graph/docs/security/index.html deleted file mode 100644 index 5ead1658c..000000000 --- a/docs/v3/v1/graph/docs/security/index.html +++ /dev/null @@ -1,1692 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @pnp/graph/security - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/graph/security

    -

    The Microsoft Graph Security API can be used as a federated security aggregation service to submit queries to all onboarded security providers to get aggregated responses.

    -

    Get all Alerts

    -

    Using the alerts() to retrieve a list of Alert objects

    -
    import { graph } from "@pnp/graph";
    -
    -const alerts = await graph.security.alerts.get();
    -
    - - -

    Get an Alert by Id

    -

    Using the alerts.getById() to retrieve a specific Alert object

    -
    import { graph } from "@pnp/graph";
    -
    -const alert = await graph.security.alerts.getById('alertId').get();
    -
    - - -

    Update an Alert

    -

    Using the alerts.getById().update() to retrieve a specific Alert object

    -
    import { graph } from "@pnp/graph";
    -
    -const updAlert = await graph.security.alerts.getById('alertId').update({status: 'Status' });
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/graph/docs/sites/index.html b/docs/v3/v1/graph/docs/sites/index.html deleted file mode 100644 index e89c9cf52..000000000 --- a/docs/v3/v1/graph/docs/sites/index.html +++ /dev/null @@ -1,2043 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @pnp/graph/sites - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - -

    @pnp/graph/sites

    -

    The ability to manage sites, lists and listitems in SharePoint is a capability introduced in version 1.3.0 of @pnp/graph.

    -

    Get the Root Site

    -

    Using the sites.root()() you can get the tenant root site

    -
    import { graph } from "@pnp/graph";
    -
    -const tenantRootSite = await graph.sites.root.get()
    -
    - - -

    Get the Root Site by Id

    -

    Using the sites.getById()() you can get the root site as well

    -
    import { graph } from "@pnp/graph";
    -
    -const tenantRootSite = await graph.sites.getById('contoso.sharepoint.com').get()
    -
    - - -

    Access a Site by server-relative URL

    -

    Using the sites.getById()() you can get a specific site. With the combination of the base URL and a relative URL. -We are using an internal method for combining the URL in the right combination, with : ex: contoso.sharepoint.com:/sites/site1:

    -

    Here are a few url combinations that works:

    -
    import { graph } from "@pnp/graph";
    -
    -// No / in the URLs
    -const siteByRelativeUrl = await graph.sites.getById('contoso.sharepoint.com', 'sites/site1').get()
    -
    -// Both trailing / in the base URL and starting / in the relative URL
    -const siteByRelativeUrl = await graph.sites.getById('contoso.sharepoint.com/', '/sites/site1').get()
    -
    -// Both trailing / in the base URL and starting and trailing / in the relative URL
    -const siteByRelativeUrl = await graph.sites.getById('contoso.sharepoint.com/', '/sites/site1/').get()
    -
    - - -

    Get the Sub Sites in a Site

    -

    Using the sites()() you can get the sub sites of a site. As this is returned as Sites, you could use getById() for a specific site and use the operations.

    -
    import { graph } from "@pnp/graph";
    -
    -const subsites = await graph.sites.getById('contoso.sharepoint.com').sites.get();
    -
    - - -
    -

    Get Content Types

    -

    Using the contentTypes()() you can get the Content Types from a Site or from a List

    -
    import { graph } from "@pnp/graph";
    -
    -const contentTypesFromSite = await graph.sites.getById('contoso.sharepoint.com').contentTypes.get();
    -
    -const contentTypesFromList = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').contentTypes.get();
    -
    - - -

    Get Specific Content Type

    -

    Using the getById() you can get a specific Content Type from a Site or from a List

    -
    import { graph } from "@pnp/graph";
    -
    -const contentTypeFromSite = await graph.sites.getById('contoso.sharepoint.com').contentTypes.getById('contentTypeId').get();
    -
    -const contentTypeFromList = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').contentTypes.getById('contentTypeId').get();
    -
    - - -
    -

    Get the Lists in a Site

    -

    Using the lists() you can get the lists of a site.

    -
    import { graph } from "@pnp/graph";
    -
    -const lists = await graph.sites.getById('contoso.sharepoint.com').lists.get();
    -
    - - -

    Get a specific List in a Site

    -

    Using the lists.getById() you can get the lists of a site.

    -
    import { graph } from "@pnp/graph";
    -
    -const list = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').get();
    -
    - - -

    Create a Lists in a Site

    -

    Using the lists.create() you can create a list in a site.

    -
    import { graph } from "@pnp/graph";
    -
    -const newLists = await graph.sites.getById('contoso.sharepoint.com').lists.create('DisplayName', {contentTypesEnabled: true, hidden: false, template: "genericList"})
    -
    - - -
    -

    Get the default drive

    -

    Using the drive() you can get the default drive from a Site or a List

    -
    import { graph } from "@pnp/graph";
    -
    -const drive = await graph.sites.getById('contoso.sharepoint.com').drive.get();
    -
    -const drive = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').drive.get();
    -
    - - -

    Get all of the drives

    -

    Using the drives() you can get the drives from the Site

    -
    import { graph } from "@pnp/graph";
    -
    -const drives = await graph.sites.getById('contoso.sharepoint.com').drives.get();
    -
    - - -

    Get drive by Id

    -

    Using the drives.getById() you can get one specific Drive. For more operations make sure to have a look in the onedrive documentation.

    -
    import { graph } from "@pnp/graph";
    -
    -const drive = await raph.sites.getById('contoso.sharepoint.com').lists.getById('listId').drives.getById('driveId').get();
    -
    - - -
    -

    Get Columns

    -

    Using the columns() you can get the columns from a Site or from a List

    -
    import { graph } from "@pnp/graph";
    -
    -const columnsFromSite = await graph.sites.getById('contoso.sharepoint.com').columns.get();
    -
    -const columnsFromList = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').columns.get();
    -
    - - -

    Get Specific Column

    -

    Using the columns.getById() you can get a specific column from a Site or from a List

    -
    import { graph } from "@pnp/graph";
    -
    -const columnFromSite = await graph.sites.getById('contoso.sharepoint.com').columns.getById('columnId').get();
    -
    -const columnsFromList = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').columns.getById('columnId').get();
    -
    - - - -

    Using the column.columnLinks() you can get the column links for a specific column, from a Site or from a List

    -
    import { graph } from "@pnp/graph";
    -
    -const columnLinksFromSite = await graph.sites.getById('contoso.sharepoint.com').columns.getById('columnId').columnLinks.get();
    -
    -const columnLinksFromList = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').columns.getById('columnId').columnLinks.get();
    -
    - - - -

    Using the column.columnLinks().getById() you can get a specific column link for a specific column, from a Site or from a List

    -
    import { graph } from "@pnp/graph";
    -
    -const columnLinkFromSite = await graph.sites.getById('contoso.sharepoint.com').columns.getById('columnId').columnLinks.getById('columnLinkId').get();
    -
    -const columnLinkFromList = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').columns.getById('columnId').columnLinks.getById('columnLinkId').get();
    -
    - - -
    -

    Get Items

    -

    Using the items() you can get the Items from a List

    -
    import { graph } from "@pnp/graph";
    -
    -const itemsFromList = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').items.get();
    -
    - - -

    Get Specific Item

    -

    Using the getById()() you can get a specific Item from a List

    -
    import { graph } from "@pnp/graph";
    -
    -const itemFromList = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').items.getById('itemId').get();
    -
    - - -

    Create Item

    -

    Using the items.create() you can create an Item in a List.

    -
    import { graph } from "@pnp/graph";
    -
    -const newItem = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').items.create({
    -    "Title": "Widget",
    -    "Color": "Purple",
    -    "Weight": 32
    -});
    -
    - - -

    Update Item

    -

    Using the update() you can update an Item in a List.

    -
    import { graph } from "@pnp/graph";
    -
    -const Item = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').items.getById('itemId').update({
    -{
    -    "Color": "Fuchsia"
    -}
    -})
    -
    - - -

    Delete Item

    -

    Using the delete() you can delete an Item in a List.

    -
    import { graph } from "@pnp/graph";
    -
    -const Item = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').items.getById('itemId').delete()
    -
    - - -

    Get Fields from Item

    -

    Using the fields() you can the Fields in an Item

    -
    import { graph } from "@pnp/graph";
    -
    -const fieldsFromItem = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').items.getById('itemId').fields.get();
    -
    - - -

    Get Versions from Item

    -

    Using the versions() you can the Versions of an Item

    -
    import { graph } from "@pnp/graph";
    -
    -const versionsFromItem = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').items.getById('itemId').versions.get();
    -
    - - -

    Get Version from Item

    -

    Using the versions.getById()() you can the Versions of an Item

    -
    import { graph } from "@pnp/graph";
    -
    -const versionFromItem = await graph.sites.getById('contoso.sharepoint.com').lists.getById('listId').items.getById('itemId').versions.getById('versionId').get();
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/graph/docs/subscriptions/index.html b/docs/v3/v1/graph/docs/subscriptions/index.html deleted file mode 100644 index effe96e07..000000000 --- a/docs/v3/v1/graph/docs/subscriptions/index.html +++ /dev/null @@ -1,1835 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - subscriptions - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/graph/subscriptions

    -

    The ability to manage subscriptions is a capability introduced in version 1.2.9 of @pnp/graph. A subscription allows a client app to receive notifications about changes to data in Microsoft Graph. Currently, subscriptions are enabled for the following resources: - Mail, events, and contacts from Outlook. - Conversations from Office Groups. - Drive root items from OneDrive. - Users and Groups from Azure Active Directory. -* Alerts from the Microsoft Graph Security API.

    -

    Get all of the Subscriptions

    -

    Using the subscriptions(). If successful this method returns a 200 OK response code and a list of subscription objects in the response body.

    -
    import { graph } from "@pnp/graph";
    -
    -const subscriptions = await graph.subscriptions.get();
    -
    - - -

    Create a new Subscription

    -

    Using the subscriptions.add(). Creating a subscription requires read scope to the resource. For example, to get notifications messages, your app needs the Mail.Read permission. -To learn more about the scopes visit this url.

    -
    import { graph } from "@pnp/graph";
    -
    -const addedSubscription = await graph.subscriptions.add("created,updated", "https://webhook.azurewebsites.net/api/send/myNotifyClient", "me/mailFolders('Inbox')/messages", "2019-11-20T18:23:45.9356913Z");
    -
    - - -

    Get Subscription by Id

    -

    Using the subscriptions.getById() you can get one of the subscriptions

    -
    import { graph } from "@pnp/graph";
    -
    -const subscription = await graph.subscriptions.getById('subscriptionId');
    -
    - - -

    Delete a Subscription

    -

    Using the subscriptions.getById().delete() you can remove one of the Subscriptions

    -
    import { graph } from "@pnp/graph";
    -
    -const delSubscription = await graph.subscription.getById('subscriptionId').delete();
    -
    - - -

    Update a Subscription

    -

    Using the subscriptions.getById().update() you can update one of the Subscriptions

    -
    import { graph } from "@pnp/graph";
    -
    -const updSubscription = await graph.subscriptions.getById('subscriptionId').update({changeType: "created,updated,deleted" });
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/graph/docs/teams/index.html b/docs/v3/v1/graph/docs/teams/index.html deleted file mode 100644 index bc831efd9..000000000 --- a/docs/v3/v1/graph/docs/teams/index.html +++ /dev/null @@ -1,2097 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - teams - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - -

    @pnp/graph/teams

    -

    The ability to manage Team is a capability introduced in the 1.2.7 of @pnp/graph. Through the methods described -you can add, update and delete items in Teams.

    -

    Teams the user is a member of

    -
    import { graph } from "@pnp/graph";
    -
    -const joinedTeams = await graph.users.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').joinedTeams.get();
    -
    -const myJoinedTeams = await graph.me.joinedTeams.get();
    -
    - - -

    Get Teams by Id

    -

    Using the teams.getById() you can get a specific Team.

    -
    import { graph } from "@pnp/graph";
    -
    -const team = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').get();
    -
    - - -

    Create new Group and Team

    -

    When you create a new group and add a Team, the group needs to have an Owner. Or else we get an error. -So the owner Id is important, and you could just get the users Ids from

    -
    import { graph } from "@pnp/graph";
    -
    -const users = await graph.users.get();
    -
    - - -

    Then create

    -
    import { graph } from "@pnp/graph";
    -
    -const createdGroupTeam = await graph.teams.create('Groupname', 'mailNickname', 'description', 'OwnerId',{ 
    -"memberSettings": {
    -    "allowCreateUpdateChannels": true
    -},
    -"messagingSettings": {
    -        "allowUserEditMessages": true,
    -"allowUserDeleteMessages": true
    -},
    -"funSettings": {
    -    "allowGiphy": true,
    -    "giphyContentRating": "strict"
    -}});
    -
    - - -

    Create a Team via a specific group

    -

    Here we get the group via id and use createTeam

    -
    import { graph } from "@pnp/graph";
    -
    -const createdTeam = await graph.groups.getById('679c8ff4-f07d-40de-b02b-60ec332472dd').createTeam({ 
    -"memberSettings": {
    -    "allowCreateUpdateChannels": true
    -},
    -"messagingSettings": {
    -        "allowUserEditMessages": true,
    -"allowUserDeleteMessages": true
    -},
    -"funSettings": {
    -    "allowGiphy": true,
    -    "giphyContentRating": "strict"
    -}});
    -
    - - -

    Archive a Team

    -
    import { graph } from "@pnp/graph";
    -
    -const archived = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').archive();
    -
    - - -

    Unarchive a Team

    -
    import { graph } from "@pnp/graph";
    -
    -const archived = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').unarchive();
    -
    - - -

    Clone a Team

    -
    import { graph } from "@pnp/graph";
    -
    -const clonedTeam = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').cloneTeam(
    -'Cloned','mailNickname','description','apps,tabs,settings,channels,members','public');
    -
    - - -

    Get all channels of a Team

    -
    import { graph } from "@pnp/graph";
    -
    -const channels = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.get();
    -
    - - -

    Get channel by Id

    -
    import { graph } from "@pnp/graph";
    -
    -const channel = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').get();
    -
    - - -

    Create a new Channel

    -
    import { graph } from "@pnp/graph";
    -
    -const newChannel = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.create('New Channel', 'Description');
    -
    - - -

    Get installed Apps

    -
    import { graph } from "@pnp/graph";
    -
    -const installedApps = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps.get();
    -
    - - -

    Add an App

    -
    import { graph } from "@pnp/graph";
    -
    -const addedApp = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps.add('https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a');
    -
    - - -

    Remove an App

    -
    import { graph } from "@pnp/graph";
    -
    -const removedApp = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps.remove();
    -
    - - -

    Get Tabs from a Channel

    -
    import { graph } from "@pnp/graph";
    -
    -const tabs = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').
    -channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs
    -.get();
    -
    - - -

    Get Tab by Id

    -
    import { graph } from "@pnp/graph";
    -
    -const tab = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').
    -channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs
    -.getById('Id');
    -
    - - -

    Add a new Tab

    -
    import { graph } from "@pnp/graph";
    -
    -const newTab = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').
    -channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs.add('Tab','https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a',<TabsConfiguration>{});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/index.html b/docs/v3/v1/index.html deleted file mode 100644 index fb758621a..000000000 --- a/docs/v3/v1/index.html +++ /dev/null @@ -1,1889 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    Home

    - -

    SharePoint Patterns and Practices Logo

    -

    PnPjs is a collection of fluent libraries for consuming SharePoint, Graph, and Office 365 REST APIs in a type-safe way. You can use it within SharePoint Framework, Nodejs, or any JavaScript project. This an open source initiative and we encourage contributions and constructive feedback from the community.

    -

    Fluent API in action -Animation of the library in use, note intellisense help in building your queries

    -

    General Guidance

    -

    These articles provide general guidance for working with the libraries. If you are migrating from sp-pnp-js please review the transition guide.

    - -

    Packages

    -

    Patterns and Practices client side libraries (PnPjs) are comprised of the packages listed below. All of the packages are published as a set and depend on their peers within the @pnp scope.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    @pnp/
    commonProvides shared functionality across all pnp libraries
    config-storeProvides a way to manage configuration within your application
    graphProvides a fluent api for working with Microsoft Graph
    loggingLight-weight, subscribable logging framework
    nodejsProvides functionality enabling the @pnp libraries within nodejs
    odataProvides shared odata functionality and base classes
    pnpjsRollup library of core functionality (mimics sp-pnp-js)
    spProvides a fluent api for working with SharePoint REST
    sp-addinhelpersProvides functionality for working within SharePoint add-ins
    sp-clientsvcProvides based classes used to create a fluent api for working with SharePoint Managed Metadata
    sp-taxonomyProvides a fluent api for working with SharePoint Managed Metadata
    -

    Issues, Questions, Ideas

    -

    Please log an issue using our template as a guide. This will let us track your request and ensure we respond. We appreciate any contructive feedback, questions, ideas, or bug reports with our thanks for giving back to the project.

    -

    Code of Conduct

    -

    This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

    -

    "Sharing is Caring"

    -

    Please use http://aka.ms/sppnp for the latest updates around the whole SharePoint Patterns and Practices (PnP) program.

    -

    Disclaimer

    -

    THIS CODE IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - diff --git a/docs/v3/v1/logging/docs/index.html b/docs/v3/v1/logging/docs/index.html deleted file mode 100644 index 0c5bf871e..000000000 --- a/docs/v3/v1/logging/docs/index.html +++ /dev/null @@ -1,2045 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - logging - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/logging

    -

    npm version

    -

    The logging module provides light weight subscribable and extensiable logging framework which is used internally and available for use in your projects. This article outlines how to setup logging and use the various loggers.

    -

    Getting Started

    -

    Install the logging module, it has no other dependencies

    -

    npm install @pnp/logging --save

    -

    Understanding the Logging Framework

    -

    The logging framework is based on the Logger class to which any number of listeners can be subscribed. Each of these listeners will receive each of the messages logged. Each listener must implement the LogListener interface, shown below. There is only one method to implement and it takes an instance of the LogEntry interface.

    -
    /**
    - * Interface that defines a log listener
    - *
    - */
    -export interface LogListener {
    -    /**
    -     * Any associated data that a given logging listener may choose to log or ignore
    -     *
    -     * @param entry The information to be logged
    -     */
    -    log(entry: LogEntry): void;
    -}
    -
    -/**
    - * Interface that defines a log entry
    - *
    - */
    -export interface LogEntry {
    -    /**
    -     * The main message to be logged
    -     */
    -    message: string;
    -    /**
    -     * The level of information this message represents
    -     */
    -    level: LogLevel;
    -    /**
    -     * Any associated data that a given logging listener may choose to log or ignore
    -     */
    -    data?: any;
    -}
    -
    - - -

    Log Levels

    -
    export const enum LogLevel {
    -    Verbose = 0,
    -    Info = 1,
    -    Warning = 2,
    -    Error = 3,
    -    Off = 99,
    -}
    -
    - - -

    Writing to the Logger

    -

    To write information to a logger you can use either write, writeJSON, or log.

    -
    import {
    -    Logger,
    -    LogLevel
    -} from "@pnp/logging";
    -
    -// write logs a simple string as the message value of the LogEntry
    -Logger.write("This is logging a simple string");
    -
    -// optionally passing a level, default level is Verbose
    -Logger.write("This is logging a simple string", LogLevel.Error);
    -
    -// this will convert the object to a string using JSON.stringify and set the message with the result
    -Logger.writeJSON({ name: "value", name2: "value2"});
    -
    -// optionally passing a level, default level is Verbose
    -Logger.writeJSON({ name: "value", name2: "value2"}, LogLevel.Warn);
    -
    -// specify the entire LogEntry interface using log
    -Logger.log({
    -    data: { name: "value", name2: "value2"},
    -    level: LogLevel.Warning,
    -    message: "This is my message"
    -});
    -
    - - -

    Log an error

    -

    There exists a shortcut method to log an error to the Logger. This will log an entry to the subscribed loggers where the data property will be the Error -instance pased in, the level will be Error, and the message will be the Error instance message.

    -
    const e = new Error("An Error");
    -
    -Logger.error(e);
    -
    - - -

    Subscribing a Listener

    -

    By default no listeners are subscribed, so if you would like to get logging information you need to subscribe at least one listener. This is done as shown below by importing the Logger and your listener(s) of choice. Here we are using the provided ConsoleListener. We are also setting the active log level, which controls the level of logging that will be output. Be aware that Verbose produces a substantial amount of data about each request.

    -
    import {
    -    Logger,
    -    ConsoleListener,
    -    LogLevel
    -} from "@pnp/logging";
    -
    -// subscribe a listener
    -Logger.subscribe(new ConsoleListener());
    -
    -// set the active log level
    -Logger.activeLogLevel = LogLevel.Info;
    -
    - - -

    Available Listeners

    -

    There are two listeners included in the library, ConsoleListener and FunctionListener.

    -

    ConsoleListener

    -

    This listener outputs information to the console and works in Node as well as within browsers. It takes no settings and writes to the appropriate console method based on message level. For example a LogEntry with level Warning will be written to console.warn. Usage is shown in the example above.

    -

    FunctionListener

    -

    The FunctionListener allows you to wrap any functionality by creating a function that takes a LogEntry as its single argument. This produces the same result as implementing the LogListener interface, but is useful if you already have a logging method or framework to which you want to pass the messages.

    -
    import {
    -    Logger,
    -    FunctionListener,
    -    LogEntry
    -} from "@pnp/logging";
    -
    -let listener = new FunctionListener((entry: LogEntry) => {
    -
    -    // pass all logging data to an existing framework
    -    MyExistingCompanyLoggingFramework.log(entry.message);
    -});
    -
    -Logger.subscribe(listener);
    -
    - - -

    Create a Custom Listener

    -

    If desirable for your project you can create a custom listener to perform any logging action you would like. This is done by implementing the LogListener interface.

    -
    import {
    -    Logger,
    -    LogListener,
    -    LogEntry
    -} from "@pnp/logging";
    -
    -class MyListener implements LogListener {
    -
    -    log(entry: LogEntry): void {
    -        // here you would do something with the entry
    -    }    
    -}
    -
    -Logger.subscribe(new MyListener());
    -
    - - -

    UML

    -

    Graphical UML diagram

    -

    Graphical UML diagram of @pnp/logging. Right-click the diagram and open in new tab if it is too small.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/nodejs/docs/adal-certificate-fetch-client/index.html b/docs/v3/v1/nodejs/docs/adal-certificate-fetch-client/index.html deleted file mode 100644 index 3d23a4e7a..000000000 --- a/docs/v3/v1/nodejs/docs/adal-certificate-fetch-client/index.html +++ /dev/null @@ -1,1676 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @pnp/nodejs/adalcertificatefetchclient - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/nodejs/adalcertificatefetchclient

    -

    The AdalCertificateFetchClient class depends on the adal-node package to authenticate against Azure AD using the client credentials with a client certificate flow. The example below -outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected.

    -
    import { AdalCertificateFetchClient } from "@pnp/nodejs";
    -import { graph } from "@pnp/graph";
    -import * as fs from "fs";
    -import * as path from "path";
    -
    -// Get the private key from a file (Assuming it's a .pem file)
    -const keyPemFile = "/path/to/privatekey.pem";
    -const privateKey = fs.readFileSync(
    -    path.resolve(__dirname, keyPemFile), 
    -    { encoding : 'utf8'}
    -);
    -
    -// setup the client using graph setup function
    -graph.setup({
    -    graph: {
    -        fetchClientFactory: () => {
    -            return new AdalCertificateFetchClient(
    -                "{tenant id}", 
    -                "{app id}", 
    -                "{certificate thumbprint}",
    -                privateKey);
    -        },
    -    },
    -});
    -
    -// execute a library request as normal
    -graph.groups.get().then(g => {
    -
    -    console.log(JSON.stringify(g, null, 4));
    -
    -}).catch(e => {
    -
    -    console.error(e);
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/nodejs/docs/adal-fetch-client/index.html b/docs/v3/v1/nodejs/docs/adal-fetch-client/index.html deleted file mode 100644 index 9464cfdec..000000000 --- a/docs/v3/v1/nodejs/docs/adal-fetch-client/index.html +++ /dev/null @@ -1,1713 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - AdalFetchClient - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/nodejs/adalfetchclient

    -

    The AdalFetchClient class depends on the adal-node package to authenticate against Azure AD. The example below -outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected.

    -
    import { AdalFetchClient } from "@pnp/nodejs";
    -import { graph } from "@pnp/graph";
    -
    -// setup the client using graph setup function
    -graph.setup({
    -    graph: {
    -        fetchClientFactory: () => {
    -            return new AdalFetchClient("{tenant}", "{app id}", "{app secret}");
    -        },
    -    },
    -});
    -
    -// execute a library request as normal
    -graph.groups.get().then(g => {
    -
    -    console.log(JSON.stringify(g, null, 4));
    -
    -}).catch(e => {
    -
    -    console.error(e);
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/nodejs/docs/bearer-token-fetch-client/index.html b/docs/v3/v1/nodejs/docs/bearer-token-fetch-client/index.html deleted file mode 100644 index f35860890..000000000 --- a/docs/v3/v1/nodejs/docs/bearer-token-fetch-client/index.html +++ /dev/null @@ -1,1712 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BearerTokenFetchClient - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/nodejs/BearerTokenFetchClient

    -

    The BearerTokenFetchClient class allows you to easily specify your own Bearer tokens to be used in the requests. How you derive the token is up to you.

    -
    import { BearerTokenFetchClient } from "@pnp/nodejs";
    -import { graph } from "@pnp/graph";
    -
    -// setup the client using graph setup function
    -graph.setup({
    -    graph: {
    -        fetchClientFactory: () => {
    -            return new BearerTokenFetchClient("{Bearer Token}");
    -        },
    -    },
    -});
    -
    -// execute a library request as normal
    -graph.groups.get().then(g => {
    -
    -    console.log(JSON.stringify(g, null, 4));
    -
    -}).catch(e => {
    -
    -    console.error(e);
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/nodejs/docs/index.html b/docs/v3/v1/nodejs/docs/index.html deleted file mode 100644 index 5452b0aa1..000000000 --- a/docs/v3/v1/nodejs/docs/index.html +++ /dev/null @@ -1,1764 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - nodejs - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/nodejs

    -

    npm version

    -

    This package supplies helper code when using the @pnp libraries within the context of nodejs. This removes the node specific functionality from any of the packages. -Primarily these consist of clients to enable use of the libraries in nodejs.

    -

    Getting Started

    -

    Install the library and required dependencies. You will also need to install other libraries such as @pnp/sp or @pnp/graph to use the -exported functionality.

    -

    npm install @pnp/logging @pnp/core @pnp/nodejs --save

    - -

    UML

    -

    Graphical UML diagram

    -

    Graphical UML diagram of @pnp/nodejs. Right-click the diagram and open in new tab if it is too small.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/nodejs/docs/provider-hosted-app/index.html b/docs/v3/v1/nodejs/docs/provider-hosted-app/index.html deleted file mode 100644 index 81c1bd022..000000000 --- a/docs/v3/v1/nodejs/docs/provider-hosted-app/index.html +++ /dev/null @@ -1,1725 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ProviderHostedRequestContext - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/nodejs/providerhostedrequestcontext

    -

    Added in 1.2.7

    -

    The ProviderHostedRequestcontext enables the creation of provider-hosted add-ins built in node.js to use pnpjs to interact with SharePoint. The context is associated to a SharePoint user, allowing requests to be made by the add-in on the behalf of the user.

    -

    The usage of this class assumes the provider-hosted add-in is called from SharePoint with a valid SPAppToken. This is typically done by means of accessing /_layouts/15/AppRedirect.aspx with the app's client ID and app's redirect URI.

    -

    Note: To support concurrent requests by different users and/or add-ins on different tenants, do not use the SPFetchClient class. Instead, use the more generic NodeFetchClient class. The downside is that you have to manually configure each request to use the desired user/app context.

    -
    import { sp, SPRest } from "@pnp/sp";
    -import { NodeFetchClient, ProviderHostedRequestContext } from "@pnp/nodejs";
    -
    -// configure your node options
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -            return new NodeFetchClient();
    -        },
    -    },
    -});
    -
    -// get request data generated by /_layouts/15/AppRedirect.aspx
    -const spAppToken = request.body.SPAppToken;
    -const spSiteUrl = request.body.SPSiteUrl;
    -
    -// create a context based on the add-in details and SPAppToken
    -const ctx = await ProviderHostedRequestContext.create(spSiteUrl, "{client id}", "{client secret}", spAppToken);
    -
    -// create an SPRest object configured to use our context
    -// this is used in place of the global sp object
    -const userSP = new SPRest().configure(await ctx.getUserConfig(), spSiteUrl);
    -const addinSP = new SPRest().configure(await ctx.getAddInOnlyConfig(), spSiteUrl);
    -
    -// make a request on behalf of the user
    -const user = await userSP.web.currentUser.get();
    -console.log(`Hello ${user.Title}`);
    -
    -// make an add-in only request
    -const app = await addinSP.web.currentUser.get();
    -console.log(`Add-in principal: ${app.Title}`);
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/nodejs/docs/proxy/index.html b/docs/v3/v1/nodejs/docs/proxy/index.html deleted file mode 100644 index cc1cd6b03..000000000 --- a/docs/v3/v1/nodejs/docs/proxy/index.html +++ /dev/null @@ -1,1699 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @pnp/nodejs/proxy - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/nodejs/proxy

    -

    Added in 1.3.2

    -

    In some cases when deploying on node you may need to use a proxy as governed by corporate policy, or perhaps you want to examine the traffic using a tool such as Fiddler. In the 1.3.2 relesae we introduced the ability to use a proxy with the @pnp/nodejs library.

    -

    Basic Usage

    -

    You need to import the new setProxyUrl function from the library and call it with your proxy url. Once done an https-proxy-agent will be used with each request. This works across all clients within the @pnp/nodejs library.

    -
    import { SPFetchClient, SPOAuthEnv, setProxyUrl } from "@pnp/nodejs";
    -
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -
    -            // call the set proxy url function and it will be used for all requests regardless of client
    -            setProxyUrl("{your proxy url}");
    -            return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO);
    -        },
    -    },
    -});
    -
    - - -

    Use with Fiddler

    -

    To get Fiddler to work you may need to set an environment variable. This should only be done for testing!

    -
    import { SPFetchClient, SPOAuthEnv, setProxyUrl } from "@pnp/nodejs";
    -
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -
    -            // ignore certificate errors: ONLY FOR TESTING!!
    -            process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
    -
    -            // this is my fiddler url locally
    -            setProxyUrl("http://127.0.0.1:8888");
    -            return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO);
    -        },
    -    },
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/nodejs/docs/sp-fetch-client/index.html b/docs/v3/v1/nodejs/docs/sp-fetch-client/index.html deleted file mode 100644 index cb8b7d13d..000000000 --- a/docs/v3/v1/nodejs/docs/sp-fetch-client/index.html +++ /dev/null @@ -1,1895 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SPFetchClient - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/nodejs/spfetchclient

    -

    The SPFetchClient is used to authentication to SharePoint as a provider hosted add-in using a client and secret in nodejs. Remember it is not a good practice to expose client ids and secrets on the client and use of this class is intended for nodejs exclusively.

    -
    import { SPFetchClient } from "@pnp/nodejs";
    -import { sp } from "@pnp/sp";
    -
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -            return new SPFetchClient("{site url}", "{client id}", "{client secret}");
    -        },
    -    },
    -});
    -
    -// execute a library request as normal
    -sp.web.get().then(w => {
    -
    -    console.log(JSON.stringify(w, null, 4));
    -
    -}).catch(e => {
    -
    -    console.error(e);
    -});
    -
    - - -

    Set Authentication Environment

    -

    Added in 1.1.2

    -

    For some areas such as Germany, China, and US Gov clouds you need to specify a different authentication url to the service. This is done by specifying the correct SPOAuthEnv enumeration to the SPFetchClient constructor. The options are listed below. If you are not sure which option to specify the default is likely OK.

    -
      -
    • SPO : (default) for all *.sharepoint.com urls
    • -
    • China: for China hosted cloud
    • -
    • Germany: for Germany local cloud
    • -
    • USDef: USA Defense cloud
    • -
    • USGov: USA Government cloud
    • -
    -
    import { sp } from "@pnp/sp";
    -import { SPFetchClient, SPOAuthEnv } from "@pnp/nodejs";
    -
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -            return new SPFetchClient("{site url}", "{client id}", "{client secret}", SPOAuthEnv.China);
    -        },
    -    },
    -});
    -
    - - -

    Set Realm

    -

    In some cases automatically resolving the realm may not work. In this case you can set the realm parameter in the SPFetchClient constructor. You can determine the correct value for the realm by navigating to "https://{site name}-admin.sharepoint.com/_layouts/15/TA_AllAppPrincipals.aspx" and copying the GUID value that appears after the "@" - this is the realm id.

    -

    As of version 1.1.2 the realm parameter is now the 5th parameter in the constructor.

    -
    import { sp } from "@pnp/sp";
    -import { SPFetchClient, SPOAuthEnv } from "@pnp/nodejs";
    -
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -            return new SPFetchClient("{site url}", "{client id}", "{client secret}", SPOAuthEnv.SPO, "{realm}");
    -        },
    -    },
    -});
    -
    - - -

    Creating a client id and secret

    -

    This section outlines how to register for a client id and secret for use in the above code.

    -

    Register An Add-In

    -

    Before you can begin running tests you need to register a low-trust add-in with SharePoint. This is primarily designed for Office 365, but can work on-premises if you configure your farm accordingly.

    -
      -
    1. Navigation to {site url}/_layouts/appregnew.aspx
    2. -
    3. Click "Generate" for both the Client Id and Secret values
    4. -
    5. Give you add-in a title, this can be anything but will let you locate it in the list of add-in permissions
    6. -
    7. Provide a fake value for app domain and redirect uri, you can use the values shown in the examples
    8. -
    9. Click "Create"
    10. -
    11. Copy the returned block of text containing the client id and secret as well as app name for your records and later in this article.
    12. -
    -

    Grant Your Add-In Permissions

    -

    Now that we have created an add-in registration we need to tell SharePoint what permissions it can use. Due to an update in SharePoint Online you now have to register add-ins with certain permissions in the admin site.

    -
      -
    1. Navigate to {admin site url}/_layouts/appinv.aspx
    2. -
    3. Paste your client id from the above section into the Add Id box and click "Lookup"
    4. -
    5. You should see the information populated into the form from the last section, if not ensure you have the correct id value
    6. -
    7. Paste the below XML into the permissions request xml box and hit "Create"
    8. -
    9. You should get a confirmation message.
    10. -
    -
      <AppPermissionRequests AllowAppOnlyPolicy="true">
    -    <AppPermissionRequest Scope="http://sharepoint/content/tenant" Right="FullControl" />
    -    <AppPermissionRequest Scope="http://sharepoint/social/tenant" Right="FullControl" />
    -    <AppPermissionRequest Scope="http://sharepoint/search" Right="QueryAsUserIgnoreAppPrincipal" />
    -  </AppPermissionRequests>
    -
    - - -

    Note that the above XML will grant full tenant control, you should grant only those permissions necessary for your application

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/odata/docs/caching/index.html b/docs/v3/v1/odata/docs/caching/index.html deleted file mode 100644 index be871e333..000000000 --- a/docs/v3/v1/odata/docs/caching/index.html +++ /dev/null @@ -1,1988 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - caching - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    - -
    - - -
    -
    - - - - - -

    @pnp/queryable/caching

    -

    Often times data doesn't change that quickly, especially in the case of rolling up corporate news or upcoming events. These types of things can be cached for minutes if not hours. To help make caching easy you just need to insert the usingCaching method in your chain. This only applies to get requests. The usingCaching method can be used with the inBatch method as well to cache the results of batched requests.

    -

    The below examples uses the @pnp/sp library as the example - but this works equally well for any library making use of the @pnp/queryable base classes, such as @pnp/graph.

    -

    Basic example

    -

    You can use the method without any additional configuration. We have made some default choices for you and will discuss ways to override them later. The below code will get the items from the list, first checking the cache for the value. You can also use it with OData operators such as top and orderBy. The usingCaching() should always be the last method in the chain before the get() (OR if you are using [[batching]] these methods can be transposed, more details below).

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.lists.getByTitle("Tasks").items.usingCaching().get().then(r => {
    -    console.log(r)
    -});
    -
    -sp.web.lists.getByTitle("Tasks").items.top(5).orderBy("Modified").usingCaching().get().then(r => {
    -    console.log(r)
    -});
    -
    - - -

    Globally Configure Cache Settings

    -

    If you would like to not use the default values, but don't want to clutter your code by setting the caching values on each request you can configure custom options globally. These will be applied to all calls to usingCaching() throughout your application.

    -
    import { sp } from "@pnp/sp";
    -
    -sp.setup({
    -    defaultCachingStore: "session", // or "local"
    -    defaultCachingTimeoutSeconds: 30,
    -    globalCacheDisable: false // or true to disable caching in case of debugging/testing
    -});
    -
    -sp.web.lists.getByTitle("Tasks").items.top(5).orderBy("Modified").usingCaching().get().then(r => {
    -    console.log(r)
    -});
    -
    - - -

    Per Call Configuration

    -

    If you prefer more verbose code or have a need to manage the cache settings on a per request basis you can include individual caching settings for each request. These settings are passed to the usingCaching method call and are defined in the following interface. If you want to use the per-request options you must include the key.

    -
    export interface ICachingOptions {
    -    expiration?: Date;
    -    storeName?: "session" | "local";
    -    key: string;
    -}
    -
    - - -
    import { sp } from "@pnp/sp";
    -import { dateAdd } from "@pnp/core";
    -
    -sp.web.lists.getByTitle("Tasks").items.top(5).orderBy("Modified").usingCaching({
    -    expiration: dateAdd(new Date(), "minute", 20),
    -    key: "My Key",
    -    storeName: "local"
    -}).get().then(r => {
    -    console.log(r)
    -});
    -
    - - -

    Using Batching with Caching

    -

    You can use batching and caching together, but remember caching is only applied to get requests. When you use them together the methods can be transposed, the below example is valid.

    -
    import { sp } from "@pnp/sp";
    -
    -let batch = sp.createBatch();
    -
    -sp.web.lists.inBatch(batch).usingCaching().get().then(r => {
    -    console.log(r)
    -});
    -
    -sp.web.lists.getByTitle("Tasks").items.usingCaching().inBatch(batch).get().then(r => {
    -    console.log(r)
    -});
    -
    -batch.execute().then(() => console.log("All done!"));
    -
    - - -

    Implement Custom Caching

    -

    You may desire to use a different caching strategy than the one we implemented within the library. The easiest way to achieve this is to wrap the request in your custom caching functionality using the unresolved promise as needed. Here we show how to implement the Stale While Revalidate pattern as discussed here.

    -

    Implement caching helper method:

    -

    We create a map to act as our cache storage and a function to wrap the request caching logic

    -
    const map = new Map<string, any>();
    -
    -async function staleWhileRevalidate<T>(key: string, p: Promise<T>): Promise<T> {
    -
    -    if (map.has(key)) {
    -
    -        // In Cache
    -        p.then(u => {
    -            // Update Cache once we have a result
    -            map.set(key, u);
    -        });
    -
    -        // Return from Cache
    -        return map.get(key);
    -    }
    -
    -    // Not In Cache so we need to wait for the value
    -    const r = await p;
    -
    -    // Set Cache
    -    map.set(key, r);
    -
    -    // Return from Promise
    -    return r;
    -}
    -
    - - -

    Usage

    -
    -

    Don't call usingCaching just apply the helper method

    -
    -
    // this one will wait for the request to finish
    -const r1 = await staleWhileRevalidate("test1", sp.web.select("Title", "Description").get());
    -
    -console.log(JSON.stringify(r1, null, 2));
    -
    -// this one will return the result from cache and then update the cache in the background
    -const r2 = await staleWhileRevalidate("test1", sp.web.select("Title", "Description").get());
    -
    -console.log(JSON.stringify(r2, null, 2));
    -
    - - -

    Wrapper Function

    -

    You can wrap this call into a single function you can reuse within your application each time you need the web data for example. You can update the select and interface to match your needs as well.

    -
    interface WebData {
    -    Title: string;
    -    Description: string;
    -}
    -
    -function getWebData(): Promise<WebData> {
    -
    -    return staleWhileRevalidate("test1", sp.web.select("Title", "Description").get());
    -}
    -
    -
    -// this one will wait for the request to finish
    -const r1 = await getWebData();
    -
    -console.log(JSON.stringify(r1, null, 2));
    -
    -// this one will return the result from cache and then update the cache in the background
    -const r2 = await getWebData();
    -
    -console.log(JSON.stringify(r2, null, 2));
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/odata/docs/core/index.html b/docs/v3/v1/odata/docs/core/index.html deleted file mode 100644 index ac5184726..000000000 --- a/docs/v3/v1/odata/docs/core/index.html +++ /dev/null @@ -1,1829 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - core - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/queryable/core

    -

    This modules contains shared interfaces and abstract classes used within, and by inheritors of, the @pnp/queryable package.

    -

    ProcessHttpClientResponseException

    -

    The exception thrown when a response is returned and cannot be processed.

    -

    interface ODataParser

    -

    Base interface used to describe a class that that will parse incoming responses. It takes a single type parameter representing the type of the -value to be returned. It has two methods, one is optional:

    -
      -
    • parse(r: Response): Promise - main method use to parse a response and return a Promise resolving to an object of type T
    • -
    • hydrate?: (d: any) => T - optional method used when getting an object from the cache if it requires calling a constructor
    • -
    -

    ODataParserBase

    -

    The base class used by all parsers in the @pnp libraries. It is optional to use when creating your own custom parsers, but does contain several helper -methods.

    -

    Create a custom parser from ODataParserBase

    -

    You can always create custom parsers for your projects, however it is likely you will not require this step as the default parsers should work for most -cases.

    -
    class MyParser extends ODataParserBase<any> {
    -
    -    // we need to override the parse method to do our custom stuff
    -    public parse(r: Response): Promise<T> {
    -
    -        // we wrap everything in a promise
    -        return new Promise((resolve, reject) => {
    -
    -            // lets use the default error handling which returns true for no error
    -            // and will call reject with an error if one exists
    -            if (this.handleError(r, reject)) {
    -
    -                // now we add our custom parsing here
    -                r.text().then(txt => {
    -                    // here we call a madeup function to parse the result
    -                    // this is where we would do our parsing as required
    -                    myCustomerUnencode(txt).then(v => {
    -                        resolve(v);
    -                    });
    -                });
    -            }
    -        });
    -    }
    -}
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/odata/docs/index.html b/docs/v3/v1/odata/docs/index.html deleted file mode 100644 index c62404aba..000000000 --- a/docs/v3/v1/odata/docs/index.html +++ /dev/null @@ -1,1781 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - odata - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/queryable

    -

    npm version

    -

    This modules contains the abstract core classes used to process odata requests. They can also be used to build your own odata -library should you wish to. By sharing the core functionality across libraries we can provide a consistent API as well as ensure -the core code is solid and well tested, with any updates benefitting all inheriting libraries.

    -

    Getting Started

    -

    Install the library and required dependencies

    -

    npm install @pnp/logging @pnp/core @pnp/queryable --save

    -

    Library Topics

    - -

    UML

    -

    Graphical UML diagram

    -

    Graphical UML diagram of @pnp/queryable. Right-click the diagram and open in new tab if it is too small.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/odata/docs/odata-batch/index.html b/docs/v3/v1/odata/docs/odata-batch/index.html deleted file mode 100644 index 44d98f193..000000000 --- a/docs/v3/v1/odata/docs/odata-batch/index.html +++ /dev/null @@ -1,1755 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - OData Batching - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/queryable/odatabatch

    -

    This module contains an abstract class used as a base when inheriting libraries support batching.

    -

    ODataBatchRequestInfo

    -

    This interface defines what each batch needs to know about each request. It is generic in that any library can provide the information but will -be responsible for processing that info by implementing the abstract executeImpl method.

    -

    ODataBatch

    -

    Base class for building batching support for a library inheriting from @pnp/queryable. You can see implementations of this abstract class in the @pnp/sp -and @pnp/graph modules.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/odata/docs/parsers/index.html b/docs/v3/v1/odata/docs/parsers/index.html deleted file mode 100644 index 0f9240c30..000000000 --- a/docs/v3/v1/odata/docs/parsers/index.html +++ /dev/null @@ -1,1862 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Parsers - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/queryable/parsers

    -

    This modules contains a set of generic parsers. These can be used or extended as needed, though it is likely in most cases the default parser will be all you need.

    -

    ODataDefaultParser

    -

    The simplest parser used to transform a Response into its JSON representation. The default parser will handle errors in a consistent manner throwing an HttpRequestError instance. This class extends Error and adds the response, status, and statusText properties. The response object is unread. You can use this custom error as shown below to gather more information about what went wrong in the request.

    -
    import { sp } from "@pnp/sp";
    -import { JSONParser } from "@pnp/queryable";
    -
    -try {
    -
    -    const parser = new JSONParser();
    -
    -    // this always throws a 404 error
    -    await sp.web.getList("doesn't exist").get(parser);
    -
    -} catch (e) {
    -
    -    // we can check for the property "isHttpRequestError" to see if this is an instance of our class
    -    // this gets by all the many limitations of subclassing Error and type detection in JavaScript
    -    if (e.hasOwnProperty("isHttpRequestError")) {
    -
    -        console.log("e is HttpRequestError");
    -
    -        // now we can access the various properties and make use of the response object.
    -        // at this point the body is unread
    -        console.log(`status: ${e.status}`);
    -        console.log(`statusText: ${e.statusText}`);
    -
    -        const json = await e.response.clone().json();
    -        console.log(JSON.stringify(json));
    -        const text = await e.response.clone().text();
    -        console.log(text);
    -        const headers = e.response.headers;
    -    }
    -
    -    console.error(e);
    -}
    -
    - - -

    TextParser

    -

    Specialized parser used to parse the response using the .text() method with no other processing. Used primarily for files.

    -

    BlobParser

    -

    Specialized parser used to parse the response using the .blob() method with no other processing. Used primarily for files.

    -

    JSONParser

    -

    Specialized parser used to parse the response using the .json() method with no other processing. Used primarily for files.

    -

    BufferParser

    -

    Specialized parser used to parse the response using the .arrayBuffer() [node] for .buffer() [browser] method with no other processing. Used primarily for files.

    -

    LambdaParser

    -

    Allows you to pass in any handler function you want, called if the request does not result in an error that transforms the raw, unread request into the result type.

    -
    import { LambdaParser } from "@pnp/queryable";
    -import { sp } from "@pnp/sp";
    -
    -// here a simple parser duplicating the functionality of the JSONParser
    -const parser = new LambdaParser((r: Response) => r.json());
    -
    -const webDataJson = await sp.web.get(parser);
    -
    -console.log(webDataJson);
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/odata/docs/pipeline/index.html b/docs/v3/v1/odata/docs/pipeline/index.html deleted file mode 100644 index 31d683c35..000000000 --- a/docs/v3/v1/odata/docs/pipeline/index.html +++ /dev/null @@ -1,1811 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Pipeline - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/queryable/pipeline

    -

    All of the odata requests processed by @pnp/queryable pass through an extensible request pipeline. Each request is executed in a specific request context defined by -the RequestContext interface with the type parameter representing the type ultimately returned at the end a successful processing through the -pipeline. Unless you are writing a pipeline method it is unlikely you will ever interact directly with the request pipeline.

    -

    interface RequestContext

    -

    The interface that defines the context within which all requests are executed. Note that the pipeline methods to be executed are part of the context. This -allows full control over the methods called during a request, and allows for the insertion of any custom methods required.

    -
    interface RequestContext<T> {
    -    batch: ODataBatch;
    -    batchDependency: () => void;
    -    cachingOptions: ICachingOptions;
    -    hasResult?: boolean;
    -    isBatched: boolean;
    -    isCached: boolean;
    -    options: FetchOptions;
    -    parser: ODataParser<T>;
    -    pipeline: Array<(c: RequestContext<T>) => Promise<RequestContext<T>>>;
    -    requestAbsoluteUrl: string;
    -    requestId: string;
    -    result?: T;
    -    verb: string;
    -    clientFactory: () => RequestClient;
    -}
    -
    - - -

    requestPipelineMethod decorator

    -

    The requestPipelineMethod decorator is used to tag a pipeline method and add functionality to bypass processing if a result is already present in the pipeline. If you -would like your method to always run regardless of the existance of a result you can pass true to ensure it will always run. Each pipeline method takes a single argument -of the current RequestContext and returns a promise resolving to the RequestContext updated as needed.

    -
    @requestPipelineMethod(true)
    -public static myPipelineMethod<T>(context: RequestContext<T>): Promise<RequestContext<T>> {
    -
    -    return new Promise<RequestContext<T>>(resolve => {
    -
    -        // do something
    -
    -        resolve(context);
    -    });
    -}
    -
    - - -

    Default Pipeline

    -
      -
    1. logs the start of the request
    2. -
    3. checks the cache for a value based on the context's cache settings
    4. -
    5. sends the request if no value from found in the cache
    6. -
    7. logs the end of the request
    8. -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/odata/docs/queryable/index.html b/docs/v3/v1/odata/docs/queryable/index.html deleted file mode 100644 index 468188ac4..000000000 --- a/docs/v3/v1/odata/docs/queryable/index.html +++ /dev/null @@ -1,1967 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Queryable - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/queryable/queryable

    -

    The Queryable class is the base class for all of the libraries building fluent request apis.

    -

    abstract class ODataQueryable

    -

    This class takes a single type parameter representing the type of the batch implementation object. If your api will not support batching -you can create a dummy class here and simply not use the batching calls.

    -

    properties

    -

    query

    -

    Provides access to the query string builder for this url

    -

    public methods

    -

    concat

    -

    Directly concatenates the supplied string to the current url, not normalizing "/" chars

    -

    configure

    -

    Sets custom options for current object and all derived objects accessible via chaining

    -
    import { ConfigOptions } from "@pnp/queryable";
    -import { sp } from "@pnp/sp";
    -
    -const headers: ConfigOptions = {
    -    Accept: 'application/json;odata=nometadata'
    -};
    -
    -// here we use configure to set the headers value for all child requests of the list instance
    -const list = sp.web.lists.getByTitle("List1").configure({ headers });
    -
    -// this will use the values set in configure
    -list.items.get().then(items => console.log(JSON.stringify(items, null, 2));
    -
    - - -

    For reference the ConfigOptions interface is shown below:

    -
    export interface ConfigOptions {
    -    headers?: string[][] | { [key: string]: string } | Headers;
    -    mode?: "navigate" | "same-origin" | "no-cors" | "cors";
    -    credentials?: "omit" | "same-origin" | "include";
    -    cache?: "default" | "no-store" | "reload" | "no-cache" | "force-cache" | "only-if-cached";
    -}
    -
    - - -

    configureFrom

    -

    Sets custom options from another queryable instance's options. Identical to configure except the options are derived from the supplied instance.

    -

    usingCaching

    -

    Enables caching for this request. See caching for more details.

    -
    import { sp } from "@pnp/sp"
    -
    -sp.web.usingCaching().get().then(...);
    -
    - - -

    inBatch

    -

    Adds this query to the supplied batch

    -

    toUrl

    -

    Gets the current url

    -

    abstract toUrlAndQuery()

    -

    When implemented by an inheriting class will build the full url with appropriate query string used to make the actual request

    -

    get

    -

    Execute the current request. Takes an optional type parameter allowing for the typing of the value or the user of parsers that will create specific object instances.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/pnpjs/docs/index.html b/docs/v3/v1/pnpjs/docs/index.html deleted file mode 100644 index e659ea737..000000000 --- a/docs/v3/v1/pnpjs/docs/index.html +++ /dev/null @@ -1,1814 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - pnpjs - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/pnpjs

    -

    npm version

    -

    The pnpjs library is a rollup of the core libraries across the @pnp scope and is designed only as a bridge to help folks transition from sp-pnp-js, primarily -in scenarios where a single file is being imported via a script tag. It is recommended to not use this rollup library where possible and migrate to the -individual libraries.

    -

    Getting Started

    -

    There are two approaches to using this library: the first is to import, the second is to manually extract the bundled file for use in your project.

    -

    Install

    -

    npm install @pnp/pnpjs --save

    -

    You can then make use of the pnpjs rollup library within your application. It's structure matches sp-pnp-js, though some things may have changed based on the rolled-up dependencies.

    -
    import pnp from "@pnp/pnpjs";
    -
    -pnp.sp.web.get().then(w => {
    -
    -    console.log(JSON.stringify(w, null, 4));
    -});
    -
    - - -

    Grab Bundle File

    -

    This method is useful if you are primarily working within a script editor web part or similar case where you are not using a build pipeline to bundle your application.

    -

    Install only this library.

    -

    npm install @pnp/pnpjs

    -

    Browse to ./node_modules/@pnp/pnpjs/dist and grab either pnpjs.es5.umd.bundle.js or pnpjs.es5.umd.bundle.min.js depending on your needs. You can then add a script tag referencing this file and you will have a global variable "pnp".

    -

    For example you could paste the following into a script editor web part:

    -
    <p>Script Editor is on page.</p>
    -<script src="https://mysite/site_assets/pnpjs.es5.umd.bundle.min.js" type="text/javascript"></script>
    -<script type="text/javascript">
    -
    -    pnp.Logger.subscribe(new pnp.ConsoleListener());
    -    pnp.Logger.activeLogLevel = pnp.LogLevel.Info;
    -
    -    pnp.sp.web.get().then(w => {
    -
    -        console.log(JSON.stringify(w, null, 4));
    -    });
    -</script>
    -
    - - -

    Alternatively to serve the script from the project at "https://localhost:8080/assets/pnp.js" you can use:

    -

    gulp serve --p pnpjs

    -

    This will allow you to test your changes to the entire bundle live while making updates.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/search/search_index.json b/docs/v3/v1/search/search_index.json deleted file mode 100644 index 586b32d18..000000000 --- a/docs/v3/v1/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"PnPjs is a collection of fluent libraries for consuming SharePoint, Graph, and Office 365 REST APIs in a type-safe way. You can use it within SharePoint Framework, Nodejs, or any JavaScript project. This an open source initiative and we encourage contributions and constructive feedback from the community. Animation of the library in use, note intellisense help in building your queries General Guidance \u00b6 These articles provide general guidance for working with the libraries. If you are migrating from sp-pnp-js please review the transition guide . Getting Started Getting Started Contributing Documentation Gulp Commands Debugging Deployment Install Beta Versions Polyfills Package Structure Packages \u00b6 Patterns and Practices client side libraries (PnPjs) are comprised of the packages listed below. All of the packages are published as a set and depend on their peers within the @pnp scope. @pnp/ common Provides shared functionality across all pnp libraries config-store Provides a way to manage configuration within your application graph Provides a fluent api for working with Microsoft Graph logging Light-weight, subscribable logging framework nodejs Provides functionality enabling the @pnp libraries within nodejs odata Provides shared odata functionality and base classes pnpjs Rollup library of core functionality (mimics sp-pnp-js) sp Provides a fluent api for working with SharePoint REST sp-addinhelpers Provides functionality for working within SharePoint add-ins sp-clientsvc Provides based classes used to create a fluent api for working with SharePoint Managed Metadata sp-taxonomy Provides a fluent api for working with SharePoint Managed Metadata Issues, Questions, Ideas \u00b6 Please log an issue using our template as a guide. This will let us track your request and ensure we respond. We appreciate any contructive feedback, questions, ideas, or bug reports with our thanks for giving back to the project. Code of Conduct \u00b6 This project has adopted the Microsoft Open Source Code of Conduct . For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments. \"Sharing is Caring\" \u00b6 Please use http://aka.ms/sppnp for the latest updates around the whole SharePoint Patterns and Practices (PnP) program . Disclaimer \u00b6 THIS CODE IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.","title":"Home"},{"location":"#general-guidance","text":"These articles provide general guidance for working with the libraries. If you are migrating from sp-pnp-js please review the transition guide . Getting Started Getting Started Contributing Documentation Gulp Commands Debugging Deployment Install Beta Versions Polyfills Package Structure","title":"General Guidance"},{"location":"#packages","text":"Patterns and Practices client side libraries (PnPjs) are comprised of the packages listed below. All of the packages are published as a set and depend on their peers within the @pnp scope. @pnp/ common Provides shared functionality across all pnp libraries config-store Provides a way to manage configuration within your application graph Provides a fluent api for working with Microsoft Graph logging Light-weight, subscribable logging framework nodejs Provides functionality enabling the @pnp libraries within nodejs odata Provides shared odata functionality and base classes pnpjs Rollup library of core functionality (mimics sp-pnp-js) sp Provides a fluent api for working with SharePoint REST sp-addinhelpers Provides functionality for working within SharePoint add-ins sp-clientsvc Provides based classes used to create a fluent api for working with SharePoint Managed Metadata sp-taxonomy Provides a fluent api for working with SharePoint Managed Metadata","title":"Packages"},{"location":"#issues-questions-ideas","text":"Please log an issue using our template as a guide. This will let us track your request and ensure we respond. We appreciate any contructive feedback, questions, ideas, or bug reports with our thanks for giving back to the project.","title":"Issues, Questions, Ideas"},{"location":"#code-of-conduct","text":"This project has adopted the Microsoft Open Source Code of Conduct . For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.","title":"Code of Conduct"},{"location":"#sharing-is-caring","text":"Please use http://aka.ms/sppnp for the latest updates around the whole SharePoint Patterns and Practices (PnP) program .","title":"\"Sharing is Caring\""},{"location":"#disclaimer","text":"THIS CODE IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.","title":"Disclaimer"},{"location":"common/docs/","text":"@pnp/core \u00b6 The common modules provides a set of utilities classes and reusable building blocks used throughout the @pnp modules. They can be used within your applications as well. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/core --save Import and use functionality, see details on modules below. import { getGUID } from \"@pnp/core\" ; console . log ( getGUID ()); Exports \u00b6 adalclient collections libconfig netutil storage util Custom HttpClient UML \u00b6 Graphical UML diagram of @pnp/core. Right-click the diagram and open in new tab if it is too small.","title":"common"},{"location":"common/docs/#pnpcommon","text":"The common modules provides a set of utilities classes and reusable building blocks used throughout the @pnp modules. They can be used within your applications as well.","title":"@pnp/core"},{"location":"common/docs/#getting-started","text":"Install the library and required dependencies npm install @pnp/core --save Import and use functionality, see details on modules below. import { getGUID } from \"@pnp/core\" ; console . log ( getGUID ());","title":"Getting Started"},{"location":"common/docs/#exports","text":"adalclient collections libconfig netutil storage util Custom HttpClient","title":"Exports"},{"location":"common/docs/#uml","text":"Graphical UML diagram of @pnp/core. Right-click the diagram and open in new tab if it is too small.","title":"UML"},{"location":"common/docs/adalclient/","text":"@pnp/core/adalclient \u00b6 Added in 1.0.4 This module contains the AdalClient class which can be used to authenticate to any AzureAD secured resource. It is designed to work seamlessly with SharePoint Framework's permissions. Setup and Use inside SharePoint Framework \u00b6 Using the SharePoint Framework is the preferred way to make use of the AdalClient as we can use the AADTokenProvider to efficiently get tokens on your behalf. You can also read more about how this process works and the necessary SPFx configurations in the SharePoint Framework 1.6 release notes . This method only work for SharePoint Framework >= 1.6. For earlier versions of SharePoint Framework you can still use the AdalClient as outlined above using the constructor to specify the values for an AAD Application you have setup. Calling the graph api \u00b6 By providing the context in the onInit we can create the adal client from known information. import { graph } from \"@pnp/graph\" ; import { getRandomString } from \"@pnp/core\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present graph . setup ({ spfxContext : this.context }); }); } public render () : void { // here we are creating a team with a random name, required Group ReadWrite All permissions const teamName = `ATeam. ${ getRandomString ( 4 ) } ` ; this . domElement . innerHTML = `Hello, I am creating a team named \" ${ teamName } \" for you...` ; graph . teams . create ( teamName , \"This is a description\" ). then ( t => { this . domElement . innerHTML += \"done!\" ; }). catch ( e => { this . domElement . innerHTML = `Oops, I ran into a problem... ${ JSON . stringify ( e , null , 4 ) } ` ; }); } Calling the SharePoint API \u00b6 This example shows how to use the ADALClient with the @pnp/sp library to call import { sp } from \"@pnp/sp\" ; import { AdalClient } from \"@pnp/core\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present sp . setup ({ spfxContext : this.context , sp : { fetchClientFactory : () => , }, }); }); } public render () : void { sp . web . get (). then ( t => { this . domElement . innerHTML = JSON . stringify ( t ); }). catch ( e => { this . domElement . innerHTML = JSON . stringify ( e ); }); } Calling the any API \u00b6 You can also use the AdalClient to execute AAD authenticated requests to any API which is properly configured to accept the incoming tokens. This approach will only work within SharePoint Framework >= 1.6. Here we call the SharePoint REST API without the sp library as an example. import { AdalClient , FetchOptions } from \"@pnp/core\" ; import { ODataDefaultParser } from \"@pnp/queryable\" ; // ... public render () : void { // create an ADAL Client const client = AdalClient . fromSPFxContext ( this . context ); // setup the request options const opts : FetchOptions = { method : \"GET\" , headers : { \"Accept\" : \"application/json\" , }, }; // execute the request client . fetch ( \"https://tenant.sharepoint.com/_api/web\" , opts ). then ( response => { // create a parser to convert the response into JSON. // You can create your own, at this point you have a fetch Response to work with const parser = new ODataDefaultParser (); parser . parse ( response ). then ( json => { this . domElement . innerHTML = JSON . stringify ( json ); }); }). catch ( e => { this . domElement . innerHTML = JSON . stringify ( e ); }); } Manually Configure \u00b6 This example shows setting up and using the AdalClient to make queries using information you have setup. You can review this article for more information on setting up and securing any application using AzureAD. Setup and Use with Microsoft Graph \u00b6 This sample uses a custom AzureAd app you have created and granted the appropriate permissions. import { AdalClient } from \"@pnp/core\" ; import { graph } from \"@pnp/graph\" ; // configure the graph client // parameters are: // client id - the id of the application you created in azure ad // tenant - can be id or URL (shown) // redirect url - absolute url of a page to which your application and Azure AD app allows replies graph . setup ({ graph : { fetchClientFactory : () => { return new AdalClient ( \"e3e9048e-ea28-423b-aca9-3ea931cc7972\" , \"{tenant}.onmicrosoft.com\" , \"https://myapp/singlesignon.aspx\" ); }, }, }); try { // call the graph API const groups = await graph . groups . get (); console . log ( JSON . stringify ( groups , null , 4 )); } catch ( e ) { console . error ( e ); } Nodejs Applications \u00b6 We have a dedicated node client in @pnp/nodejs.","title":"adalclient"},{"location":"common/docs/adalclient/#pnpcommonadalclient","text":"Added in 1.0.4 This module contains the AdalClient class which can be used to authenticate to any AzureAD secured resource. It is designed to work seamlessly with SharePoint Framework's permissions.","title":"@pnp/core/adalclient"},{"location":"common/docs/adalclient/#setup-and-use-inside-sharepoint-framework","text":"Using the SharePoint Framework is the preferred way to make use of the AdalClient as we can use the AADTokenProvider to efficiently get tokens on your behalf. You can also read more about how this process works and the necessary SPFx configurations in the SharePoint Framework 1.6 release notes . This method only work for SharePoint Framework >= 1.6. For earlier versions of SharePoint Framework you can still use the AdalClient as outlined above using the constructor to specify the values for an AAD Application you have setup.","title":"Setup and Use inside SharePoint Framework"},{"location":"common/docs/adalclient/#calling-the-graph-api","text":"By providing the context in the onInit we can create the adal client from known information. import { graph } from \"@pnp/graph\" ; import { getRandomString } from \"@pnp/core\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present graph . setup ({ spfxContext : this.context }); }); } public render () : void { // here we are creating a team with a random name, required Group ReadWrite All permissions const teamName = `ATeam. ${ getRandomString ( 4 ) } ` ; this . domElement . innerHTML = `Hello, I am creating a team named \" ${ teamName } \" for you...` ; graph . teams . create ( teamName , \"This is a description\" ). then ( t => { this . domElement . innerHTML += \"done!\" ; }). catch ( e => { this . domElement . innerHTML = `Oops, I ran into a problem... ${ JSON . stringify ( e , null , 4 ) } ` ; }); }","title":"Calling the graph api"},{"location":"common/docs/adalclient/#calling-the-sharepoint-api","text":"This example shows how to use the ADALClient with the @pnp/sp library to call import { sp } from \"@pnp/sp\" ; import { AdalClient } from \"@pnp/core\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present sp . setup ({ spfxContext : this.context , sp : { fetchClientFactory : () => , }, }); }); } public render () : void { sp . web . get (). then ( t => { this . domElement . innerHTML = JSON . stringify ( t ); }). catch ( e => { this . domElement . innerHTML = JSON . stringify ( e ); }); }","title":"Calling the SharePoint API"},{"location":"common/docs/adalclient/#calling-the-any-api","text":"You can also use the AdalClient to execute AAD authenticated requests to any API which is properly configured to accept the incoming tokens. This approach will only work within SharePoint Framework >= 1.6. Here we call the SharePoint REST API without the sp library as an example. import { AdalClient , FetchOptions } from \"@pnp/core\" ; import { ODataDefaultParser } from \"@pnp/queryable\" ; // ... public render () : void { // create an ADAL Client const client = AdalClient . fromSPFxContext ( this . context ); // setup the request options const opts : FetchOptions = { method : \"GET\" , headers : { \"Accept\" : \"application/json\" , }, }; // execute the request client . fetch ( \"https://tenant.sharepoint.com/_api/web\" , opts ). then ( response => { // create a parser to convert the response into JSON. // You can create your own, at this point you have a fetch Response to work with const parser = new ODataDefaultParser (); parser . parse ( response ). then ( json => { this . domElement . innerHTML = JSON . stringify ( json ); }); }). catch ( e => { this . domElement . innerHTML = JSON . stringify ( e ); }); }","title":"Calling the any API"},{"location":"common/docs/adalclient/#manually-configure","text":"This example shows setting up and using the AdalClient to make queries using information you have setup. You can review this article for more information on setting up and securing any application using AzureAD.","title":"Manually Configure"},{"location":"common/docs/adalclient/#setup-and-use-with-microsoft-graph","text":"This sample uses a custom AzureAd app you have created and granted the appropriate permissions. import { AdalClient } from \"@pnp/core\" ; import { graph } from \"@pnp/graph\" ; // configure the graph client // parameters are: // client id - the id of the application you created in azure ad // tenant - can be id or URL (shown) // redirect url - absolute url of a page to which your application and Azure AD app allows replies graph . setup ({ graph : { fetchClientFactory : () => { return new AdalClient ( \"e3e9048e-ea28-423b-aca9-3ea931cc7972\" , \"{tenant}.onmicrosoft.com\" , \"https://myapp/singlesignon.aspx\" ); }, }, }); try { // call the graph API const groups = await graph . groups . get (); console . log ( JSON . stringify ( groups , null , 4 )); } catch ( e ) { console . error ( e ); }","title":"Setup and Use with Microsoft Graph"},{"location":"common/docs/adalclient/#nodejs-applications","text":"We have a dedicated node client in @pnp/nodejs.","title":"Nodejs Applications"},{"location":"common/docs/collections/","text":"@pnp/core/collections \u00b6 The collections module provides typings and classes related to working with dictionaries. TypedHash \u00b6 Interface used to described an object with string keys corresponding to values of type T export interface TypedHash < T > { [ key : string ] : T ; } objectToMap \u00b6 Converts a plain object to a Map instance const map = objectToMap ({ a : \"b\" , c : \"d\" }); mergeMaps \u00b6 Merges two or more maps, overwriting values with the same key. Last value in wins. const m1 = new Map (); const m2 = new Map (); const m3 = new Map (); const m4 = new Map (); const m = mergeMaps ( m1 , m2 , m3 , m4 );","title":"collections"},{"location":"common/docs/collections/#pnpcommoncollections","text":"The collections module provides typings and classes related to working with dictionaries.","title":"@pnp/core/collections"},{"location":"common/docs/collections/#typedhash","text":"Interface used to described an object with string keys corresponding to values of type T export interface TypedHash < T > { [ key : string ] : T ; }","title":"TypedHash"},{"location":"common/docs/collections/#objecttomap","text":"Converts a plain object to a Map instance const map = objectToMap ({ a : \"b\" , c : \"d\" });","title":"objectToMap"},{"location":"common/docs/collections/#mergemaps","text":"Merges two or more maps, overwriting values with the same key. Last value in wins. const m1 = new Map (); const m2 = new Map (); const m3 = new Map (); const m4 = new Map (); const m = mergeMaps ( m1 , m2 , m3 , m4 );","title":"mergeMaps"},{"location":"common/docs/custom-httpclientimpl/","text":"Custom HttpClientImpl \u00b6 This should be considered an advanced topic and creating a custom HttpClientImpl is not something you will likely need to do. Also, we don't offer support beyond this article for writing your own implementation. It is possible you may need complete control over the sending and receiving of requests. Before you get started read and understand the fetch specification as you are essentially writing a custom fetch implementation. The first step (second if you read the fetch spec as mentioned just above) is to understand the interface you need to implement, HttpClientImpl. export interface HttpClientImpl { fetch ( url : string , options : FetchOptions ) : Promise < Response > ; } There is a single method \"fetch\" which takes a url string and a set of options. These options can be just about anything but are constrained within the library to the FetchOptions interface. export interface FetchOptions { method? : string ; headers? : HeadersInit | { [ index : string ] : string }; body? : BodyInit ; mode? : string | RequestMode ; credentials? : string | RequestCredentials ; cache? : string | RequestCache ; } So you will need to handle any of those options along with the provided url when sending your request. The library will expect your implementation to return a Promise that resolves to a Response defined by the fetch specification - which you've already read \ud83d\udc4d. Using Your Custom HttpClientImpl \u00b6 Once you have written your implementation using it on your requests is done by setting it in the global library configuration: import { setup } from \"@pnp/core\" ; import { sp , Web } from \"@pnp/sp\" ; import { MyAwesomeClient } from \"./awesomeclient\" ; sp . setup ({ sp : { fetchClientFactory : () => { return new MyAwesomeClient (); } } }); let w = new Web ( \"{site url}\" ); // this request will use your client. w . select ( \"Title\" ). get (). then ( w => { console . log ( w ); }); Subclassing is Better \u00b6 You can of course inherit from one of the implementations available within the @pnp scope if you just need to say add a header or need to do something to every request sent. Perhaps some advanced logging. This approach will save you from needing to fully write a fetch implementation. A FINAL NOTE \u00b6 Whatever you do, do not write a client that uses a client id and secret and exposes them on the client side. Client Id and Secret should only ever be used on a server, never exposed to clients as anyone with those values has the full permissions granted to that id and secret.","title":"Custom HttpClientImpl"},{"location":"common/docs/custom-httpclientimpl/#custom-httpclientimpl","text":"This should be considered an advanced topic and creating a custom HttpClientImpl is not something you will likely need to do. Also, we don't offer support beyond this article for writing your own implementation. It is possible you may need complete control over the sending and receiving of requests. Before you get started read and understand the fetch specification as you are essentially writing a custom fetch implementation. The first step (second if you read the fetch spec as mentioned just above) is to understand the interface you need to implement, HttpClientImpl. export interface HttpClientImpl { fetch ( url : string , options : FetchOptions ) : Promise < Response > ; } There is a single method \"fetch\" which takes a url string and a set of options. These options can be just about anything but are constrained within the library to the FetchOptions interface. export interface FetchOptions { method? : string ; headers? : HeadersInit | { [ index : string ] : string }; body? : BodyInit ; mode? : string | RequestMode ; credentials? : string | RequestCredentials ; cache? : string | RequestCache ; } So you will need to handle any of those options along with the provided url when sending your request. The library will expect your implementation to return a Promise that resolves to a Response defined by the fetch specification - which you've already read \ud83d\udc4d.","title":"Custom HttpClientImpl"},{"location":"common/docs/custom-httpclientimpl/#using-your-custom-httpclientimpl","text":"Once you have written your implementation using it on your requests is done by setting it in the global library configuration: import { setup } from \"@pnp/core\" ; import { sp , Web } from \"@pnp/sp\" ; import { MyAwesomeClient } from \"./awesomeclient\" ; sp . setup ({ sp : { fetchClientFactory : () => { return new MyAwesomeClient (); } } }); let w = new Web ( \"{site url}\" ); // this request will use your client. w . select ( \"Title\" ). get (). then ( w => { console . log ( w ); });","title":"Using Your Custom HttpClientImpl"},{"location":"common/docs/custom-httpclientimpl/#subclassing-is-better","text":"You can of course inherit from one of the implementations available within the @pnp scope if you just need to say add a header or need to do something to every request sent. Perhaps some advanced logging. This approach will save you from needing to fully write a fetch implementation.","title":"Subclassing is Better"},{"location":"common/docs/custom-httpclientimpl/#a-final-note","text":"Whatever you do, do not write a client that uses a client id and secret and exposes them on the client side. Client Id and Secret should only ever be used on a server, never exposed to clients as anyone with those values has the full permissions granted to that id and secret.","title":"A FINAL NOTE"},{"location":"common/docs/libconfig/","text":"@pnp/core/libconfig \u00b6 Contains the shared classes and interfaces used to configure the libraries. These bases classes are expanded on in dependent libraries with the core configuration defined here. This module exposes an instance of the RuntimeConfigImpl class: RuntimeConfig. This configuration object can be referenced and contains the global configuration shared across the libraries. You can also extend the configuration for use within your own applications. LibraryConfiguration Interface \u00b6 Defines the shared configurable values used across the library as shown below. Each of these has a default value as shown below export interface LibraryConfiguration { /** * Allows caching to be global disabled, default: false */ globalCacheDisable? : boolean ; /** * Defines the default store used by the usingCaching method, default: session */ defaultCachingStore ?: \"session\" | \"local\" ; /** * Defines the default timeout in seconds used by the usingCaching method, default 30 */ defaultCachingTimeoutSeconds? : number ; /** * If true a timeout expired items will be removed from the cache in intervals determined by cacheTimeoutInterval */ enableCacheExpiration? : boolean ; /** * Determines the interval in milliseconds at which the cache is checked to see if items have expired (min: 100) */ cacheExpirationIntervalMilliseconds? : number ; /** * Used to supply the current context from an SPFx webpart to the library */ spfxContext? : any ; } RuntimeConfigImpl \u00b6 The class which implements the runtime configuration management as well as sets the default values used within the library. At its heart lies a Dictionary used to track the configuration values. The keys will match the values in the interface or plain object passed to the extend method. extend \u00b6 The extend method is used to add configuration to the global configuration instance. You can pass it any plain object with string keys and those values will be added. Any existing values will be overwritten based on the keys. Last value in wins. For a more detailed scenario of using the RuntimeConfig instance in your own application please see the section below \"Using RuntimeConfig within your application\". Note there are no methods to remove/clear the global config as it should be considered fairly static as frequent updates may have unpredictable side effects as it is a global shared object. Generally it should be set at the start of your application. import { RuntimeConfig } from \"@pnp/core\" ; // add your custom keys to the global configuration // note you can use object hashes as values RuntimeConfig . extend ({ \"myKey1\" : \"value 1\" , \"myKey2\" : { \"subKey\" : \"sub value 1\" , \"subKey2\" : \"sub value 2\" , }, }); // read your custom values const v = RuntimeConfig . get ( \"myKey1\" ); // \"value 1\" Using RuntimeConfig within your Application \u00b6 If you have a set of properties you will access very frequently it may be desirable to implement your own configuration object and expose those values as properties. To do so you will need to create an interface for your configuration (optional) and a wrapper class for RuntimeConfig to expose your properties import { LibraryConfiguration , RuntimeConfig } from \"@pnp/core\" ; // first we create our own interface by extending LibraryConfiguration. This allows your class to accept all the values with correct type checking. Note, because // TypeScript allows you to extend from multiple interfaces you can build a complex configuration definition from many sub definitions. // create the interface of your properties // by creating this separately you allows others to compose your parts into their own config interface MyConfigurationPart { // you can create a grouped definition and access your settings as an object // keys can be optional or required as defined by your interface my ?: { prop1? : string ; prop2? : string ; } // and/or define multiple top level properties (beware key collision) // it is good practice to use a unique prefix myProp1 : string ; myProp2 : number ; } // now create a combined interface interface MyConfiguration extends LibraryConfiguration , MyConfigurationPart { } // now create a wrapper object and expose your properties class MyRuntimeConfigImpl { // exposing a nested property public get prop1 () : TypedHash < string > { const myPart = RuntimeConfig . get ( \"my\" ); if ( myPart !== null && typeof myPart !== \"undefined\" && typeof myPart . prop1 !== \"undefined\" ) { return myPart . prop1 ; } return {}; } // exposing a root level property public get myProp1 () : string | null { let myProp1 = RuntimeConfig . get ( \"myProp1\" ); if ( myProp1 === null ) { myProp1 = \"some default value\" ; } return myProp1 ; } setup ( config : MyConfiguration ) : void { RuntimeConfig . extend ( config ); } } // create a single static instance of your impl class export let MyRuntimeConfig = new MyRuntimeConfigImpl (); Now in other files you can use and set your configuration with a typed interface and properties import { MyRuntimeConfig } from \"{location of module}\" ; MyRuntimeConfig . setup ({ my : { prop1 : \"hello\" , }, }); const value = MyRuntimeConfig . prop1 ; // \"hello\"","title":"libconfig"},{"location":"common/docs/libconfig/#pnpcommonlibconfig","text":"Contains the shared classes and interfaces used to configure the libraries. These bases classes are expanded on in dependent libraries with the core configuration defined here. This module exposes an instance of the RuntimeConfigImpl class: RuntimeConfig. This configuration object can be referenced and contains the global configuration shared across the libraries. You can also extend the configuration for use within your own applications.","title":"@pnp/core/libconfig"},{"location":"common/docs/libconfig/#libraryconfiguration-interface","text":"Defines the shared configurable values used across the library as shown below. Each of these has a default value as shown below export interface LibraryConfiguration { /** * Allows caching to be global disabled, default: false */ globalCacheDisable? : boolean ; /** * Defines the default store used by the usingCaching method, default: session */ defaultCachingStore ?: \"session\" | \"local\" ; /** * Defines the default timeout in seconds used by the usingCaching method, default 30 */ defaultCachingTimeoutSeconds? : number ; /** * If true a timeout expired items will be removed from the cache in intervals determined by cacheTimeoutInterval */ enableCacheExpiration? : boolean ; /** * Determines the interval in milliseconds at which the cache is checked to see if items have expired (min: 100) */ cacheExpirationIntervalMilliseconds? : number ; /** * Used to supply the current context from an SPFx webpart to the library */ spfxContext? : any ; }","title":"LibraryConfiguration Interface"},{"location":"common/docs/libconfig/#runtimeconfigimpl","text":"The class which implements the runtime configuration management as well as sets the default values used within the library. At its heart lies a Dictionary used to track the configuration values. The keys will match the values in the interface or plain object passed to the extend method.","title":"RuntimeConfigImpl"},{"location":"common/docs/libconfig/#extend","text":"The extend method is used to add configuration to the global configuration instance. You can pass it any plain object with string keys and those values will be added. Any existing values will be overwritten based on the keys. Last value in wins. For a more detailed scenario of using the RuntimeConfig instance in your own application please see the section below \"Using RuntimeConfig within your application\". Note there are no methods to remove/clear the global config as it should be considered fairly static as frequent updates may have unpredictable side effects as it is a global shared object. Generally it should be set at the start of your application. import { RuntimeConfig } from \"@pnp/core\" ; // add your custom keys to the global configuration // note you can use object hashes as values RuntimeConfig . extend ({ \"myKey1\" : \"value 1\" , \"myKey2\" : { \"subKey\" : \"sub value 1\" , \"subKey2\" : \"sub value 2\" , }, }); // read your custom values const v = RuntimeConfig . get ( \"myKey1\" ); // \"value 1\"","title":"extend"},{"location":"common/docs/libconfig/#using-runtimeconfig-within-your-application","text":"If you have a set of properties you will access very frequently it may be desirable to implement your own configuration object and expose those values as properties. To do so you will need to create an interface for your configuration (optional) and a wrapper class for RuntimeConfig to expose your properties import { LibraryConfiguration , RuntimeConfig } from \"@pnp/core\" ; // first we create our own interface by extending LibraryConfiguration. This allows your class to accept all the values with correct type checking. Note, because // TypeScript allows you to extend from multiple interfaces you can build a complex configuration definition from many sub definitions. // create the interface of your properties // by creating this separately you allows others to compose your parts into their own config interface MyConfigurationPart { // you can create a grouped definition and access your settings as an object // keys can be optional or required as defined by your interface my ?: { prop1? : string ; prop2? : string ; } // and/or define multiple top level properties (beware key collision) // it is good practice to use a unique prefix myProp1 : string ; myProp2 : number ; } // now create a combined interface interface MyConfiguration extends LibraryConfiguration , MyConfigurationPart { } // now create a wrapper object and expose your properties class MyRuntimeConfigImpl { // exposing a nested property public get prop1 () : TypedHash < string > { const myPart = RuntimeConfig . get ( \"my\" ); if ( myPart !== null && typeof myPart !== \"undefined\" && typeof myPart . prop1 !== \"undefined\" ) { return myPart . prop1 ; } return {}; } // exposing a root level property public get myProp1 () : string | null { let myProp1 = RuntimeConfig . get ( \"myProp1\" ); if ( myProp1 === null ) { myProp1 = \"some default value\" ; } return myProp1 ; } setup ( config : MyConfiguration ) : void { RuntimeConfig . extend ( config ); } } // create a single static instance of your impl class export let MyRuntimeConfig = new MyRuntimeConfigImpl (); Now in other files you can use and set your configuration with a typed interface and properties import { MyRuntimeConfig } from \"{location of module}\" ; MyRuntimeConfig . setup ({ my : { prop1 : \"hello\" , }, }); const value = MyRuntimeConfig . prop1 ; // \"hello\"","title":"Using RuntimeConfig within your Application"},{"location":"common/docs/netutil/","text":"@pnp/core/netutil \u00b6 This module contains a set of classes and interfaces used to characterize shared http interactions and configuration of the libraries. Some of the interfaces are described below (many have no use outside the library) as well as several classes. Interfaces \u00b6 HttpClientImpl \u00b6 Defines an implementation of an Http Client within the context of @pnp. This being a class with a a single method \"fetch\" take a URL and options and returning a Promise . Used primarily with the shared request pipeline to define the client used to make the actual request. You can write your own custom implementation if needed. RequestClient \u00b6 An abstraction that contains specific methods related to each of the primary request methods get, post, patch, delete as well as fetch and fetchRaw. The difference between fetch and fetchRaw is that a client may include additional logic or processing in fetch, where fetchRaw should be a direct call to the underlying HttpClientImpl fetch method. Classes \u00b6 This module export two classes of note, FetchClient and BearerTokenFetchClient. Both implement HttpClientImpl. FetchClient \u00b6 Basic implementation that calls the global (window) fetch method with no additional processing. import { FetchClient } from \"@pnp/core\" ; const client = new FetchClient (); client . fetch ( \"{url}\" , {}); BearerTokenFetchClient \u00b6 A simple implementation that takes a provided authentication token and adds the Authentication Bearer header to the request. No other processing is done and the token is treated as a static string. import { BearerTokenFetchClient } from \"@pnp/core\" ; const client = new BearerTokenFetchClient ( \"{authentication token}\" ); client . fetch ( \"{url}\" , {});","title":"netutil"},{"location":"common/docs/netutil/#pnpcommonnetutil","text":"This module contains a set of classes and interfaces used to characterize shared http interactions and configuration of the libraries. Some of the interfaces are described below (many have no use outside the library) as well as several classes.","title":"@pnp/core/netutil"},{"location":"common/docs/netutil/#interfaces","text":"","title":"Interfaces"},{"location":"common/docs/netutil/#httpclientimpl","text":"Defines an implementation of an Http Client within the context of @pnp. This being a class with a a single method \"fetch\" take a URL and options and returning a Promise . Used primarily with the shared request pipeline to define the client used to make the actual request. You can write your own custom implementation if needed.","title":"HttpClientImpl"},{"location":"common/docs/netutil/#requestclient","text":"An abstraction that contains specific methods related to each of the primary request methods get, post, patch, delete as well as fetch and fetchRaw. The difference between fetch and fetchRaw is that a client may include additional logic or processing in fetch, where fetchRaw should be a direct call to the underlying HttpClientImpl fetch method.","title":"RequestClient"},{"location":"common/docs/netutil/#classes","text":"This module export two classes of note, FetchClient and BearerTokenFetchClient. Both implement HttpClientImpl.","title":"Classes"},{"location":"common/docs/netutil/#fetchclient","text":"Basic implementation that calls the global (window) fetch method with no additional processing. import { FetchClient } from \"@pnp/core\" ; const client = new FetchClient (); client . fetch ( \"{url}\" , {});","title":"FetchClient"},{"location":"common/docs/netutil/#bearertokenfetchclient","text":"A simple implementation that takes a provided authentication token and adds the Authentication Bearer header to the request. No other processing is done and the token is treated as a static string. import { BearerTokenFetchClient } from \"@pnp/core\" ; const client = new BearerTokenFetchClient ( \"{authentication token}\" ); client . fetch ( \"{url}\" , {});","title":"BearerTokenFetchClient"},{"location":"common/docs/storage/","text":"@pnp/core/storage \u00b6 This module provides a thin wrapper over the browser storage options, local and session. If neither option is available it shims storage with a non-persistent in memory polyfill. Optionally through configuration you can activate expiration. Sample usage is shown below. PnPClientStorage \u00b6 The main export of this module, contains properties representing local and session storage. import { PnPClientStorage } from \"@pnp/core\" ; const storage = new PnPClientStorage (); const myvalue = storage . local . get ( \"mykey\" ); PnPClientStorageWrapper \u00b6 Each of the storage locations (session and local) are wrapped with this helper class. You can use it directly, but generally it would be used from an instance of PnPClientStorage as shown below. These examples all use local storage, the operations are identical for session storage. import { PnPClientStorage } from \"@pnp/core\" ; const storage = new PnPClientStorage (); // get a value from storage const value = storage . local . get ( \"mykey\" ); // put a value into storage storage . local . put ( \"mykey2\" , \"my value\" ); // put a value into storage with an expiration storage . local . put ( \"mykey2\" , \"my value\" , new Date ()); // put a simple object into storage // because JSON.stringify is used to package the object we do NOT do a deep rehydration of stored objects storage . local . put ( \"mykey3\" , { key : \"value\" , key2 : \"value2\" , }); // remove a value from storage storage . local . delete ( \"mykey3\" ); // get an item or add it if it does not exist // returns a promise in case you need time to get the value for storage // optionally takes a third parameter specifying the expiration storage . local . getOrPut ( \"mykey4\" , () => { return Promise . resolve ( \"value\" ); }); // delete expired items storage . local . deleteExpired (); Cache Expiration \u00b6 The ability remove of expired items based on a configured timeout can help if the cache is filling up. This can be accomplished in two ways. The first is to explicitly call the new deleteExpired method on the cache you wish to clear. A suggested usage is to add this into your page init code as clearing expired items once per page load is likely sufficient. import { PnPClientStorage } from \"@pnp/core\" ; const storage = new PnPClientStorage (); // session storage storage . session . deleteExpired (); // local storage storage . local . deleteExpired (); // this returns a promise, so you can perform some activity after the expired items are removed: storage . local . deleteExpired (). then ( _ => { // init my application }); The second method is to enable automated cache expiration through global config. Setting the enableCacheExpiration property to true will enable the timer. Optionally you can set the interval at which the cache is checked via the cacheExpirationIntervalMilliseconds property, by default 750 milliseconds is used. We enforce a minimum of 300 milliseconds as this functionality is enabled via setTimeout and there is little value in having an excessive number of cache checks. This method is more appropriate for a single page application where the page is infrequently reloaded and many cached operations are performed. There is no advantage to enabling cache expiration unless you are experiencing cache storage space pressure in a long running page - and you may see a performance hit due to the use of setTimeout. import { setup } from \"@pnp/core\" ; setup ({ enableCacheExpiration : true , cacheExpirationIntervalMilliseconds : 1000 , // optional });","title":"storage"},{"location":"common/docs/storage/#pnpcommonstorage","text":"This module provides a thin wrapper over the browser storage options, local and session. If neither option is available it shims storage with a non-persistent in memory polyfill. Optionally through configuration you can activate expiration. Sample usage is shown below.","title":"@pnp/core/storage"},{"location":"common/docs/storage/#pnpclientstorage","text":"The main export of this module, contains properties representing local and session storage. import { PnPClientStorage } from \"@pnp/core\" ; const storage = new PnPClientStorage (); const myvalue = storage . local . get ( \"mykey\" );","title":"PnPClientStorage"},{"location":"common/docs/storage/#pnpclientstoragewrapper","text":"Each of the storage locations (session and local) are wrapped with this helper class. You can use it directly, but generally it would be used from an instance of PnPClientStorage as shown below. These examples all use local storage, the operations are identical for session storage. import { PnPClientStorage } from \"@pnp/core\" ; const storage = new PnPClientStorage (); // get a value from storage const value = storage . local . get ( \"mykey\" ); // put a value into storage storage . local . put ( \"mykey2\" , \"my value\" ); // put a value into storage with an expiration storage . local . put ( \"mykey2\" , \"my value\" , new Date ()); // put a simple object into storage // because JSON.stringify is used to package the object we do NOT do a deep rehydration of stored objects storage . local . put ( \"mykey3\" , { key : \"value\" , key2 : \"value2\" , }); // remove a value from storage storage . local . delete ( \"mykey3\" ); // get an item or add it if it does not exist // returns a promise in case you need time to get the value for storage // optionally takes a third parameter specifying the expiration storage . local . getOrPut ( \"mykey4\" , () => { return Promise . resolve ( \"value\" ); }); // delete expired items storage . local . deleteExpired ();","title":"PnPClientStorageWrapper"},{"location":"common/docs/storage/#cache-expiration","text":"The ability remove of expired items based on a configured timeout can help if the cache is filling up. This can be accomplished in two ways. The first is to explicitly call the new deleteExpired method on the cache you wish to clear. A suggested usage is to add this into your page init code as clearing expired items once per page load is likely sufficient. import { PnPClientStorage } from \"@pnp/core\" ; const storage = new PnPClientStorage (); // session storage storage . session . deleteExpired (); // local storage storage . local . deleteExpired (); // this returns a promise, so you can perform some activity after the expired items are removed: storage . local . deleteExpired (). then ( _ => { // init my application }); The second method is to enable automated cache expiration through global config. Setting the enableCacheExpiration property to true will enable the timer. Optionally you can set the interval at which the cache is checked via the cacheExpirationIntervalMilliseconds property, by default 750 milliseconds is used. We enforce a minimum of 300 milliseconds as this functionality is enabled via setTimeout and there is little value in having an excessive number of cache checks. This method is more appropriate for a single page application where the page is infrequently reloaded and many cached operations are performed. There is no advantage to enabling cache expiration unless you are experiencing cache storage space pressure in a long running page - and you may see a performance hit due to the use of setTimeout. import { setup } from \"@pnp/core\" ; setup ({ enableCacheExpiration : true , cacheExpirationIntervalMilliseconds : 1000 , // optional });","title":"Cache Expiration"},{"location":"common/docs/util/","text":"@pnp/core/util \u00b6 This module contains utility methods that you can import individually from the common library. import { getRandomString , } from \"@pnp/core\" ; // use from individual;y imported method console . log ( getRandomString ( 10 )); getCtxCallback \u00b6 Gets a callback function which will maintain context across async calls. import { getCtxCallback } from \"@pnp/core\" ; const contextThis = { myProp : 6 , }; function theFunction() { // \"this\" within this function will be the context object supplied // in this case the variable contextThis, so myProp will exist return this . myProp ; } const callback = getCtxCallback ( contextThis , theFunction ); callback (); // returns 6 // You can also supply additional parameters if needed function theFunction2 ( g : number ) { // \"this\" within this function will be the context object supplied // in this case the variable contextThis, so myProp will exist return this . myProp + g ; } const callback2 = getCtxCallback ( contextThis , theFunction , 4 ); callback2 (); // returns 10 (6 + 4) dateAdd \u00b6 Manipulates a date, please see the Stackoverflow discussion from where this method was taken. combine \u00b6 Combines any number of paths, normalizing the slashes as required import { combine } from \"@pnp/core\" ; // \"https://microsoft.com/something/more\" const paths = combine ( \"https://microsoft.com\" , \"something\" , \"more\" ); // \"also/works/with/relative\" const paths2 = combine ( \"/also/\" , \"/works\" , \"with/\" , \"/relative\\\\\" ); getRandomString \u00b6 Gets a random string consisting of the number of characters requested. import { getRandomString } from \"@pnp/core\" ; const randomString = getRandomString ( 10 ); getGUID \u00b6 Creates a random guid, please see the Stackoverflow discussion from where this method was taken. isFunc \u00b6 Determines if a supplied variable represents a function. objectDefinedNotNull \u00b6 Determines if an object is defined and not null. isArray \u00b6 Determines if a supplied variable represents an array. extend \u00b6 Merges a source object's own enumerable properties into a single target object. Similar to Object.assign, but allows control of overwriting of existing properties. import { extend } from \"@pnp/core\" ; let obj1 = { prop : 1 , prop2 : 2 , }; const obj2 = { prop : 4 , prop3 : 9 , }; const example1 = extend ( obj1 , obj2 ); // example1 = { prop: 4, prop2: 2, prop3: 9 } const example2 = extend ( obj1 , obj2 , true ); // example2 = { prop: 1, prop2: 2, prop3: 9 } isUrlAbsolute \u00b6 Determines if a supplied url is absolute and returns true; otherwise returns false. stringIsNullOrEmpty \u00b6 Determines if a supplied string is null or empty Removed \u00b6 Some methods that were no longer used internally by the @pnp libraries have been removed. You can find the source for those methods below for use in your projects should you require. /** * Loads a stylesheet into the current page * * @param path The url to the stylesheet * @param avoidCache If true a value will be appended as a query string to avoid browser caching issues */ public static loadStylesheet ( path : string , avoidCache : boolean ) : void { if ( avoidCache ) { path += \"?\" + encodeURIComponent (( new Date ()). getTime (). toString ()); } const head = document . getElementsByTagName ( \"head\" ); if ( head . length > 0 ) { const e = document . createElement ( \"link\" ); head [ 0 ]. appendChild ( e ); e . setAttribute ( \"type\" , \"text/css\" ); e . setAttribute ( \"rel\" , \"stylesheet\" ); e . setAttribute ( \"href\" , path ); } } /** * Tests if a url param exists * * @param name The name of the url parameter to check */ public static urlParamExists ( name : string ) : boolean { name = name . replace ( /[\\[]/ , \"\\\\[\" ). replace ( /[\\]]/ , \"\\\\]\" ); const regex = new RegExp ( \"[\\\\?&]\" + name + \"=([^&#]*)\" ); return regex . test ( location . search ); } /** * Gets a url param value by name * * @param name The name of the parameter for which we want the value */ public static getUrlParamByName ( name : string ) : string { name = name . replace ( /[\\[]/ , \"\\\\[\" ). replace ( /[\\]]/ , \"\\\\]\" ); const regex = new RegExp ( \"[\\\\?&]\" + name + \"=([^&#]*)\" ); const results = regex . exec ( location . search ); return results == null ? \"\" : decodeURIComponent ( results [ 1 ]. replace ( /\\+/g , \" \" )); } /** * Gets a url param by name and attempts to parse a bool value * * @param name The name of the parameter for which we want the boolean value */ public static getUrlParamBoolByName ( name : string ) : boolean { const p = this . getUrlParamByName ( name ); const isFalse = ( p === \"\" || /false|0/i . test ( p )); return ! isFalse ; } /** * Inserts the string s into the string target as the index specified by index * * @param target The string into which we will insert s * @param index The location in target to insert s (zero based) * @param s The string to insert into target at position index */ public static stringInsert ( target : string , index : number , s : string ) : string { if ( index > 0 ) { return target . substring ( 0 , index ) + s + target . substring ( index , target . length ); } return s + target ; }","title":"util"},{"location":"common/docs/util/#pnpcommonutil","text":"This module contains utility methods that you can import individually from the common library. import { getRandomString , } from \"@pnp/core\" ; // use from individual;y imported method console . log ( getRandomString ( 10 ));","title":"@pnp/core/util"},{"location":"common/docs/util/#getctxcallback","text":"Gets a callback function which will maintain context across async calls. import { getCtxCallback } from \"@pnp/core\" ; const contextThis = { myProp : 6 , }; function theFunction() { // \"this\" within this function will be the context object supplied // in this case the variable contextThis, so myProp will exist return this . myProp ; } const callback = getCtxCallback ( contextThis , theFunction ); callback (); // returns 6 // You can also supply additional parameters if needed function theFunction2 ( g : number ) { // \"this\" within this function will be the context object supplied // in this case the variable contextThis, so myProp will exist return this . myProp + g ; } const callback2 = getCtxCallback ( contextThis , theFunction , 4 ); callback2 (); // returns 10 (6 + 4)","title":"getCtxCallback"},{"location":"common/docs/util/#dateadd","text":"Manipulates a date, please see the Stackoverflow discussion from where this method was taken.","title":"dateAdd"},{"location":"common/docs/util/#combine","text":"Combines any number of paths, normalizing the slashes as required import { combine } from \"@pnp/core\" ; // \"https://microsoft.com/something/more\" const paths = combine ( \"https://microsoft.com\" , \"something\" , \"more\" ); // \"also/works/with/relative\" const paths2 = combine ( \"/also/\" , \"/works\" , \"with/\" , \"/relative\\\\\" );","title":"combine"},{"location":"common/docs/util/#getrandomstring","text":"Gets a random string consisting of the number of characters requested. import { getRandomString } from \"@pnp/core\" ; const randomString = getRandomString ( 10 );","title":"getRandomString"},{"location":"common/docs/util/#getguid","text":"Creates a random guid, please see the Stackoverflow discussion from where this method was taken.","title":"getGUID"},{"location":"common/docs/util/#isfunc","text":"Determines if a supplied variable represents a function.","title":"isFunc"},{"location":"common/docs/util/#objectdefinednotnull","text":"Determines if an object is defined and not null.","title":"objectDefinedNotNull"},{"location":"common/docs/util/#isarray","text":"Determines if a supplied variable represents an array.","title":"isArray"},{"location":"common/docs/util/#extend","text":"Merges a source object's own enumerable properties into a single target object. Similar to Object.assign, but allows control of overwriting of existing properties. import { extend } from \"@pnp/core\" ; let obj1 = { prop : 1 , prop2 : 2 , }; const obj2 = { prop : 4 , prop3 : 9 , }; const example1 = extend ( obj1 , obj2 ); // example1 = { prop: 4, prop2: 2, prop3: 9 } const example2 = extend ( obj1 , obj2 , true ); // example2 = { prop: 1, prop2: 2, prop3: 9 }","title":"extend"},{"location":"common/docs/util/#isurlabsolute","text":"Determines if a supplied url is absolute and returns true; otherwise returns false.","title":"isUrlAbsolute"},{"location":"common/docs/util/#stringisnullorempty","text":"Determines if a supplied string is null or empty","title":"stringIsNullOrEmpty"},{"location":"common/docs/util/#removed","text":"Some methods that were no longer used internally by the @pnp libraries have been removed. You can find the source for those methods below for use in your projects should you require. /** * Loads a stylesheet into the current page * * @param path The url to the stylesheet * @param avoidCache If true a value will be appended as a query string to avoid browser caching issues */ public static loadStylesheet ( path : string , avoidCache : boolean ) : void { if ( avoidCache ) { path += \"?\" + encodeURIComponent (( new Date ()). getTime (). toString ()); } const head = document . getElementsByTagName ( \"head\" ); if ( head . length > 0 ) { const e = document . createElement ( \"link\" ); head [ 0 ]. appendChild ( e ); e . setAttribute ( \"type\" , \"text/css\" ); e . setAttribute ( \"rel\" , \"stylesheet\" ); e . setAttribute ( \"href\" , path ); } } /** * Tests if a url param exists * * @param name The name of the url parameter to check */ public static urlParamExists ( name : string ) : boolean { name = name . replace ( /[\\[]/ , \"\\\\[\" ). replace ( /[\\]]/ , \"\\\\]\" ); const regex = new RegExp ( \"[\\\\?&]\" + name + \"=([^&#]*)\" ); return regex . test ( location . search ); } /** * Gets a url param value by name * * @param name The name of the parameter for which we want the value */ public static getUrlParamByName ( name : string ) : string { name = name . replace ( /[\\[]/ , \"\\\\[\" ). replace ( /[\\]]/ , \"\\\\]\" ); const regex = new RegExp ( \"[\\\\?&]\" + name + \"=([^&#]*)\" ); const results = regex . exec ( location . search ); return results == null ? \"\" : decodeURIComponent ( results [ 1 ]. replace ( /\\+/g , \" \" )); } /** * Gets a url param by name and attempts to parse a bool value * * @param name The name of the parameter for which we want the boolean value */ public static getUrlParamBoolByName ( name : string ) : boolean { const p = this . getUrlParamByName ( name ); const isFalse = ( p === \"\" || /false|0/i . test ( p )); return ! isFalse ; } /** * Inserts the string s into the string target as the index specified by index * * @param target The string into which we will insert s * @param index The location in target to insert s (zero based) * @param s The string to insert into target at position index */ public static stringInsert ( target : string , index : number , s : string ) : string { if ( index > 0 ) { return target . substring ( 0 , index ) + s + target . substring ( index , target . length ); } return s + target ; }","title":"Removed"},{"location":"config-store/docs/","text":"@pnp/config-store \u00b6 This module providers a way to load application configuration from one or more providers and share it across an application in a consistent way. A provider can be anything - but we have included one to load information from a SharePoint list. This library is most helpful for larger applications where a formal configuration model is needed. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/config-store --save See the topics below for usage: configuration providers UML \u00b6 Graphical UML diagram of @pnp/config-store. Right-click the diagram and open in new tab if it is too small.","title":"config-store"},{"location":"config-store/docs/#pnpconfig-store","text":"This module providers a way to load application configuration from one or more providers and share it across an application in a consistent way. A provider can be anything - but we have included one to load information from a SharePoint list. This library is most helpful for larger applications where a formal configuration model is needed.","title":"@pnp/config-store"},{"location":"config-store/docs/#getting-started","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/config-store --save See the topics below for usage: configuration providers","title":"Getting Started"},{"location":"config-store/docs/#uml","text":"Graphical UML diagram of @pnp/config-store. Right-click the diagram and open in new tab if it is too small.","title":"UML"},{"location":"config-store/docs/configuration/","text":"@pnp/config-store/configuration \u00b6 The main class exported from the config-store package is Settings. This is the class through which you will load and access your settings via providers . import { Web } from \"@pnp/sp\" ; import { Settings , SPListConfigurationProvider } from \"@pnp/config-store\" ; // create an instance of the settings class, could be static and shared across your application // or built as needed. const settings = new Settings (); // you can add/update a single value using add settings . add ( \"mykey\" , \"myvalue\" ); // you can also add/update a JSON value which will be stringified for you as a shorthand settings . addJSON ( \"mykey2\" , { field : 1 , field2 : 2 , field3 : 3 , }); // and you can apply a plain object of keys/values that will be written as single values // this results in each enumerable property of the supplied object being added to the settings collection settings . apply ({ field : 1 , field2 : 2 , field3 : 3 , }); // and finally you can load values from a configuration provider const w = new Web ( \"https://mytenant.sharepoint.com/sites/dev\" ); const provider = new SPListConfigurationProvider ( w , \"myconfiglistname\" ); // this will load values from the supplied list // by default the key will be from the Title field and the value from a column named Value await settings . load ( provider ); // once we have loaded values we can then read them const value = settings . get ( \"mykey\" ); // or read JSON that will be parsed for you from the store const value2 = settings . getJSON ( \"mykey2\" );","title":"configuration"},{"location":"config-store/docs/configuration/#pnpconfig-storeconfiguration","text":"The main class exported from the config-store package is Settings. This is the class through which you will load and access your settings via providers . import { Web } from \"@pnp/sp\" ; import { Settings , SPListConfigurationProvider } from \"@pnp/config-store\" ; // create an instance of the settings class, could be static and shared across your application // or built as needed. const settings = new Settings (); // you can add/update a single value using add settings . add ( \"mykey\" , \"myvalue\" ); // you can also add/update a JSON value which will be stringified for you as a shorthand settings . addJSON ( \"mykey2\" , { field : 1 , field2 : 2 , field3 : 3 , }); // and you can apply a plain object of keys/values that will be written as single values // this results in each enumerable property of the supplied object being added to the settings collection settings . apply ({ field : 1 , field2 : 2 , field3 : 3 , }); // and finally you can load values from a configuration provider const w = new Web ( \"https://mytenant.sharepoint.com/sites/dev\" ); const provider = new SPListConfigurationProvider ( w , \"myconfiglistname\" ); // this will load values from the supplied list // by default the key will be from the Title field and the value from a column named Value await settings . load ( provider ); // once we have loaded values we can then read them const value = settings . get ( \"mykey\" ); // or read JSON that will be parsed for you from the store const value2 = settings . getJSON ( \"mykey2\" );","title":"@pnp/config-store/configuration"},{"location":"config-store/docs/providers/","text":"@pnp/config-store/providers \u00b6 Currently there is a single provider included in the library, but contributions of additional providers are welcome. SPListConfigurationProvider \u00b6 This provider is based on a SharePoint list and read all of the rows and makes them available as a TypedHash . By default the column names used are Title for key and \"Value\" for value, but you can update these as needed. Additionally the settings class supports the idea of last value in wins - so you can easily load multiple configurations. This helps to support a common scenario in the enterprise where you might have one main list for global configuration but some settings can be set at the web level. In this case you would first load the global, then the local settings and any local values will take precedence. import { Web } from \"@pnp/sp\" ; import { Settings , SPListConfigurationProvider } from \"@pnp/config-store\" ; // create a new provider instance const w = new Web ( \"https://mytenant.sharepoint.com/sites/dev\" ); const provider = new SPListConfigurationProvider ( w , \"myconfiglistname\" ); const settings = new Settings (); // load our values from the list await settings . load ( provider ); CachingConfigurationProvider \u00b6 Because making requests on each page load is very inefficient you can optionally use the caching configuration provider, which wraps a provider and caches the configuration in local or session storage. import { Web } from \"@pnp/sp\" ; import { Settings , SPListConfigurationProvider } from \"@pnp/config-store\" ; // create a new provider instance const w = new Web ( \"https://mytenant.sharepoint.com/sites/dev\" ); const provider = new SPListConfigurationProvider ( w , \"myconfiglistname\" ); // get an instance of the provider wrapped // you can optionally provide a key that will be used in the cache to the asCaching method const wrappedProvider = provider . asCaching (); // use that wrapped provider to populate the settings await settings . load ( wrappedProvider );","title":"providers"},{"location":"config-store/docs/providers/#pnpconfig-storeproviders","text":"Currently there is a single provider included in the library, but contributions of additional providers are welcome.","title":"@pnp/config-store/providers"},{"location":"config-store/docs/providers/#splistconfigurationprovider","text":"This provider is based on a SharePoint list and read all of the rows and makes them available as a TypedHash . By default the column names used are Title for key and \"Value\" for value, but you can update these as needed. Additionally the settings class supports the idea of last value in wins - so you can easily load multiple configurations. This helps to support a common scenario in the enterprise where you might have one main list for global configuration but some settings can be set at the web level. In this case you would first load the global, then the local settings and any local values will take precedence. import { Web } from \"@pnp/sp\" ; import { Settings , SPListConfigurationProvider } from \"@pnp/config-store\" ; // create a new provider instance const w = new Web ( \"https://mytenant.sharepoint.com/sites/dev\" ); const provider = new SPListConfigurationProvider ( w , \"myconfiglistname\" ); const settings = new Settings (); // load our values from the list await settings . load ( provider );","title":"SPListConfigurationProvider"},{"location":"config-store/docs/providers/#cachingconfigurationprovider","text":"Because making requests on each page load is very inefficient you can optionally use the caching configuration provider, which wraps a provider and caches the configuration in local or session storage. import { Web } from \"@pnp/sp\" ; import { Settings , SPListConfigurationProvider } from \"@pnp/config-store\" ; // create a new provider instance const w = new Web ( \"https://mytenant.sharepoint.com/sites/dev\" ); const provider = new SPListConfigurationProvider ( w , \"myconfiglistname\" ); // get an instance of the provider wrapped // you can optionally provide a key that will be used in the cache to the asCaching method const wrappedProvider = provider . asCaching (); // use that wrapped provider to populate the settings await settings . load ( wrappedProvider );","title":"CachingConfigurationProvider"},{"location":"documentation/SPFx-On-Premesis-2016/","text":"Workaround for SPFx TypeScript Version \u00b6 Note this article applies to version 1.4.1 SharePoint Framework projects targetting on-premesis only. When using the Yeoman generator to create a SharePoint Framework 1.4.1 project targeting on-premesis it installs TypeScript version 2.2.2. Unfortunately this library relies on 2.4.2 or later due to extensive use of default values for generic type parameters in the libraries. To work around this limitation you can follow the steps in this article. Open package-lock.json Search for \"typescript\": \"2.2.2\" Replace \"2.2.2\" with \"2.4.2\" Search for the next \"typescript\" occurance and replace the block with: \"typescript\" : { \"version\" : \"2.4.2\" , \"resolved\" : \"https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz\" , \"integrity\" : \"sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=\" , \"dev\" : true } Remove node_modules folder rm -rf node_modules/ Run npm install This can be checked with: npm list typescript +-- @microsoft/sp-build-web@1.1.0 | `-- @microsoft/gulp-core-build-typescript@3.1.1 | +-- @microsoft/api-extractor@2.3.8 | | `-- typescript@2.4.2 | `-- typescript@2.4.2","title":"SPFx On-Premises 2016"},{"location":"documentation/SPFx-On-Premesis-2016/#workaround-for-spfx-typescript-version","text":"Note this article applies to version 1.4.1 SharePoint Framework projects targetting on-premesis only. When using the Yeoman generator to create a SharePoint Framework 1.4.1 project targeting on-premesis it installs TypeScript version 2.2.2. Unfortunately this library relies on 2.4.2 or later due to extensive use of default values for generic type parameters in the libraries. To work around this limitation you can follow the steps in this article. Open package-lock.json Search for \"typescript\": \"2.2.2\" Replace \"2.2.2\" with \"2.4.2\" Search for the next \"typescript\" occurance and replace the block with: \"typescript\" : { \"version\" : \"2.4.2\" , \"resolved\" : \"https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz\" , \"integrity\" : \"sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=\" , \"dev\" : true } Remove node_modules folder rm -rf node_modules/ Run npm install This can be checked with: npm list typescript +-- @microsoft/sp-build-web@1.1.0 | `-- @microsoft/gulp-core-build-typescript@3.1.1 | +-- @microsoft/api-extractor@2.3.8 | | `-- typescript@2.4.2 | `-- typescript@2.4.2","title":"Workaround for SPFx TypeScript Version"},{"location":"documentation/beta-versions/","text":"Beta Versions \u00b6 To help folks try out new features early and provide feedback prior to releases we publish beta versions of the packages. Released as a set with matching version numbers, just like when we do a normal release. Generally every Friday a new set of beta libraries will be released. While not ready for production use we encourage you to try out these pre-release packages and provide us feedback. Installing \u00b6 To install the beta packages in your project you use the @beta version number on the packages. This applies to all packages, not just the ones shown in the example below. npm install @pnp/logging@beta @pnp/core@beta @pnp/queryable@beta @pnp/sp@beta --save Please remember that it is possible something may not work in a beta version, so be aware and if you find something please report an issue .","title":"Install Beta Versions"},{"location":"documentation/beta-versions/#beta-versions","text":"To help folks try out new features early and provide feedback prior to releases we publish beta versions of the packages. Released as a set with matching version numbers, just like when we do a normal release. Generally every Friday a new set of beta libraries will be released. While not ready for production use we encourage you to try out these pre-release packages and provide us feedback.","title":"Beta Versions"},{"location":"documentation/beta-versions/#installing","text":"To install the beta packages in your project you use the @beta version number on the packages. This applies to all packages, not just the ones shown in the example below. npm install @pnp/logging@beta @pnp/core@beta @pnp/queryable@beta @pnp/sp@beta --save Please remember that it is possible something may not work in a beta version, so be aware and if you find something please report an issue .","title":"Installing"},{"location":"documentation/debugging/","text":"Debugging \u00b6 Debugging Library Features in Code using Node \u00b6 The easiest way to debug the library when working on new features is using F5 in Visual Studio Code. This uses the launch.json file to build and run the library using ./debug/launch/main.ts as the program entry. You can add any number of files to this directory and they will be ignored by git, however the debug.ts file is not, so please ensure you don't commit any login information. Setup settings.js \u00b6 If you have not already you need to create a settings.js files by copying settings.example.js and renaming it to settings.js. Then update the clientId, clientSecret, and siteUrl fields in the testing section. (See below for guidance on registering a client id and secret) Test your setup \u00b6 If you hit F5 now you should be able to see the full response from getting the web's title in the internal console window. If not, ensure that you have properly updated the settings file and registered the add-in perms correctly. Create a debug module \u00b6 Using ./debug/launch/example.ts as a reference create a debugging file in the debug folder, let's call it mydebug.ts and add this content: // note we can use the actual package names for our imports import { sp , ListEnsureResult } from \"@pnp/sp\" ; import { Logger , LogLevel , ConsoleListener } from \"@pnp/logging\" ; declare var process : { exit ( code? : number ) : void }; export function MyDebug() { // run some debugging sp . web . lists . ensure ( \"MyFirstList\" ). then (( list : ListEnsureResult ) => { Logger . log ({ data : list.created , message : \"Was list created?\" , level : LogLevel.Verbose }); if ( list . created ) { Logger . log ({ data : list.data , message : \"Raw data from list creation.\" , level : LogLevel.Verbose }); } else { Logger . log ({ data : null , message : \"List already existed!\" , level : LogLevel.Verbose }); } process . exit ( 0 ); }). catch ( e => { Logger . error ( e ); process . exit ( 1 ); }); } Update main.ts to launch your module \u00b6 First comment out the import for the default example and then add the import and function call for yours, the updated main.ts should look like this: // ... // comment out the example // import { Example } from \"./example\"; // Example(); import { MyDebug } from \"./mydebug\" MyDebug (); // ... Debug! \u00b6 Place a break point within the promise resolution in your debug file and hit F5. Your module should be run and your break point hit. You can then examine the contents of the objects and see the run time state. Remember you can also set breakpoints within the package src folders to see exactly how things are working during your debugging scenarios. Next Steps \u00b6 Using this pattern you can create and preserve multiple debugging scenarios in separate modules locally. In Browser Debugging \u00b6 You can also serve files locally to debug in a browser through two methods. The first will serve code using ./debug/serve/main.ts as the entry. Meaning you can easily write code and test it in the browser. The second method allows you to serve a single package (bundled with all dependencies) for in browser testing. Both methods serve the file from https://localhost:8080/assets/pnp.js, allowing you to create a single page in your tenant for in browser testing. Start the local serve \u00b6 This will serve a package with ./debug/serve/main.ts as the entry. gulp serve Add reference to library \u00b6 Within a SharePoint page add a script editor web part and then paste in the following code. The div is to give you a place to target with visual updates should you desire. < script src = \"https://localhost:8080/assets/pnp.js\" > < div id = \"pnptestdiv\" > You should see an alert with the current web's title using the default main.ts. Feel free to update main.ts to do whatever you would like, but note that any changes included as part of a PR to this file will not be allowed. Serve a specific package \u00b6 For example if you wanted to serve the @pnp/sp package for testing you would use: gulp serve --p sp This will serve a bundle of the sp functionality along with all dependencies and place a global variable named \"pnp.{packagename}\", in this case pnp.sp. This will be true for each package, if you served just the graph package the global would be pnp.graph. This mirrors how the umd modules are built in the distributed npm packages to allow testing with matching packages. Next Steps \u00b6 You can make changes to the library and immediately see them reflected in the browser. All files are watched regardless of which serve method you choose. Register an Add-in \u00b6 Before you can begin debugging you need to register a low-trust add-in with SharePoint. This is primarily designed for Office 365, but can work on-premises if you configure your farm accordingly . Navigation to {site url}/_layouts/appregnew.aspx Click \"Generate\" for both the Client Id and Secret values Give you add-in a title, this can be anything but will let you locate it in the list of add-in permissions Provide a fake value for app domain and redirect uri, you can use the values shown in the examples Click \"Create\" Copy the returned block of text containing the client id and secret as well as app name for your records and later in this article. Grant Your Add-In Permissions \u00b6 Now that we have created an add-in registration we need to tell SharePoint what permissions it can use. Due to an update in SharePoint Online you now have to register add-ins with certain permissions in the admin site . Navigate to {admin site url}/_layouts/appinv.aspx Paste your client id from the above section into the Add Id box and click \"Lookup\" You should see the information populated into the form from the last section, if not ensure you have the correct id value Paste the below XML into the permissions request xml box and hit \"Create\" You should get a confirmation message. Note these are very broad permissions to ensure you can test any feature of the library, for production you should tailor the permissions to only those required Configure the project settings file \u00b6 If you have not already, make a copy of settings.example.js and name it settings.js Edit this file to set the values on the testing.sp object to id: \"The client id you created\" secret: \"The client secret you created\" url: \"{site url}\" You can disable web tests at any time by setting enableWebTests to false in settings.js, this can be helpful as they take a few minutes to run","title":"Debugging"},{"location":"documentation/debugging/#debugging","text":"","title":"Debugging"},{"location":"documentation/debugging/#debugging-library-features-in-code-using-node","text":"The easiest way to debug the library when working on new features is using F5 in Visual Studio Code. This uses the launch.json file to build and run the library using ./debug/launch/main.ts as the program entry. You can add any number of files to this directory and they will be ignored by git, however the debug.ts file is not, so please ensure you don't commit any login information.","title":"Debugging Library Features in Code using Node"},{"location":"documentation/debugging/#setup-settingsjs","text":"If you have not already you need to create a settings.js files by copying settings.example.js and renaming it to settings.js. Then update the clientId, clientSecret, and siteUrl fields in the testing section. (See below for guidance on registering a client id and secret)","title":"Setup settings.js"},{"location":"documentation/debugging/#test-your-setup","text":"If you hit F5 now you should be able to see the full response from getting the web's title in the internal console window. If not, ensure that you have properly updated the settings file and registered the add-in perms correctly.","title":"Test your setup"},{"location":"documentation/debugging/#create-a-debug-module","text":"Using ./debug/launch/example.ts as a reference create a debugging file in the debug folder, let's call it mydebug.ts and add this content: // note we can use the actual package names for our imports import { sp , ListEnsureResult } from \"@pnp/sp\" ; import { Logger , LogLevel , ConsoleListener } from \"@pnp/logging\" ; declare var process : { exit ( code? : number ) : void }; export function MyDebug() { // run some debugging sp . web . lists . ensure ( \"MyFirstList\" ). then (( list : ListEnsureResult ) => { Logger . log ({ data : list.created , message : \"Was list created?\" , level : LogLevel.Verbose }); if ( list . created ) { Logger . log ({ data : list.data , message : \"Raw data from list creation.\" , level : LogLevel.Verbose }); } else { Logger . log ({ data : null , message : \"List already existed!\" , level : LogLevel.Verbose }); } process . exit ( 0 ); }). catch ( e => { Logger . error ( e ); process . exit ( 1 ); }); }","title":"Create a debug module"},{"location":"documentation/debugging/#update-maints-to-launch-your-module","text":"First comment out the import for the default example and then add the import and function call for yours, the updated main.ts should look like this: // ... // comment out the example // import { Example } from \"./example\"; // Example(); import { MyDebug } from \"./mydebug\" MyDebug (); // ...","title":"Update main.ts to launch your module"},{"location":"documentation/debugging/#debug","text":"Place a break point within the promise resolution in your debug file and hit F5. Your module should be run and your break point hit. You can then examine the contents of the objects and see the run time state. Remember you can also set breakpoints within the package src folders to see exactly how things are working during your debugging scenarios.","title":"Debug!"},{"location":"documentation/debugging/#next-steps","text":"Using this pattern you can create and preserve multiple debugging scenarios in separate modules locally.","title":"Next Steps"},{"location":"documentation/debugging/#in-browser-debugging","text":"You can also serve files locally to debug in a browser through two methods. The first will serve code using ./debug/serve/main.ts as the entry. Meaning you can easily write code and test it in the browser. The second method allows you to serve a single package (bundled with all dependencies) for in browser testing. Both methods serve the file from https://localhost:8080/assets/pnp.js, allowing you to create a single page in your tenant for in browser testing.","title":"In Browser Debugging"},{"location":"documentation/debugging/#start-the-local-serve","text":"This will serve a package with ./debug/serve/main.ts as the entry. gulp serve","title":"Start the local serve"},{"location":"documentation/debugging/#add-reference-to-library","text":"Within a SharePoint page add a script editor web part and then paste in the following code. The div is to give you a place to target with visual updates should you desire. < script src = \"https://localhost:8080/assets/pnp.js\" > < div id = \"pnptestdiv\" > You should see an alert with the current web's title using the default main.ts. Feel free to update main.ts to do whatever you would like, but note that any changes included as part of a PR to this file will not be allowed.","title":"Add reference to library"},{"location":"documentation/debugging/#serve-a-specific-package","text":"For example if you wanted to serve the @pnp/sp package for testing you would use: gulp serve --p sp This will serve a bundle of the sp functionality along with all dependencies and place a global variable named \"pnp.{packagename}\", in this case pnp.sp. This will be true for each package, if you served just the graph package the global would be pnp.graph. This mirrors how the umd modules are built in the distributed npm packages to allow testing with matching packages.","title":"Serve a specific package"},{"location":"documentation/debugging/#next-steps_1","text":"You can make changes to the library and immediately see them reflected in the browser. All files are watched regardless of which serve method you choose.","title":"Next Steps"},{"location":"documentation/debugging/#register-an-add-in","text":"Before you can begin debugging you need to register a low-trust add-in with SharePoint. This is primarily designed for Office 365, but can work on-premises if you configure your farm accordingly . Navigation to {site url}/_layouts/appregnew.aspx Click \"Generate\" for both the Client Id and Secret values Give you add-in a title, this can be anything but will let you locate it in the list of add-in permissions Provide a fake value for app domain and redirect uri, you can use the values shown in the examples Click \"Create\" Copy the returned block of text containing the client id and secret as well as app name for your records and later in this article.","title":"Register an Add-in"},{"location":"documentation/debugging/#grant-your-add-in-permissions","text":"Now that we have created an add-in registration we need to tell SharePoint what permissions it can use. Due to an update in SharePoint Online you now have to register add-ins with certain permissions in the admin site . Navigate to {admin site url}/_layouts/appinv.aspx Paste your client id from the above section into the Add Id box and click \"Lookup\" You should see the information populated into the form from the last section, if not ensure you have the correct id value Paste the below XML into the permissions request xml box and hit \"Create\" You should get a confirmation message. Note these are very broad permissions to ensure you can test any feature of the library, for production you should tailor the permissions to only those required","title":"Grant Your Add-In Permissions"},{"location":"documentation/debugging/#configure-the-project-settings-file","text":"If you have not already, make a copy of settings.example.js and name it settings.js Edit this file to set the values on the testing.sp object to id: \"The client id you created\" secret: \"The client secret you created\" url: \"{site url}\" You can disable web tests at any time by setting enableWebTests to false in settings.js, this can be helpful as they take a few minutes to run","title":"Configure the project settings file"},{"location":"documentation/deployment/","text":"Deployment \u00b6 There are two recommended ways to consume the library in a production deployment: bundle the code into your solution (such as with webpack), or reference the code from a CDN. These methods are outlined here but this is not meant to be an exhaustive guide on all the ways to package and deploy solutions. Bundle \u00b6 If you have installed the library via NPM into your application solution bundlers such as webpack can bundle the PnPjs libraries along with your solution. This can make deployment easier, but will increase the size of your application by the size of the included libraries. The PnPjs libraries are setup to support tree shaking which can help with the bundle size. CDN \u00b6 If you have public internet access you can reference the library from cdnjs or unpkg which maintains copies of all versions. This is ideal as you do not need to host the file yourself, and it is easy to update to a newer release by updating the URL in your solution. Below lists all of the library locations within cdnjs, you will need to ensure you have the full url to the file you need, such as: \"https://cdnjs.cloudflare.com/ajax/libs/pnp-common/1.1.1/common.es5.umd.min.js\". To use the libraries with a script tag in a page it is recommended to use the *.es5.umd.min.js versions. This will add a global pnp value with each library added as pnp.{lib name} such as pnp.sp, pnp.common, etc. https://cdnjs.com/libraries/pnp-common https://cdnjs.com/libraries/pnp-config-store https://cdnjs.com/libraries/pnp-graph https://cdnjs.com/libraries/pnp-logging https://cdnjs.com/libraries/pnp-odata https://cdnjs.com/libraries/pnp-pnpjs https://cdnjs.com/libraries/pnp-sp https://cdnjs.com/libraries/pnp-sp-addinhelpers https://cdnjs.com/libraries/pnp-sp-clientsvc https://cdnjs.com/libraries/pnp-sp-taxonomy CDN and SPFx \u00b6 If you are developing in SPFx and install and import the PnPjs libraries the default behavior will be to bundle the library into your solution. You have a couple of choices on how best to work with CDNs and SPFx. Because SPFx doesn't currently respect peer dependencies it is easier to reference the pnpjs rollup package for your project. In this case you would install the package, reference it in your code, and update your config.js file externals as follows: Install \u00b6 npm install @pnp/pnpjs --save In Code \u00b6 import { sp } from \"@pnp/pnpjs\" ; sp . web . lists . getByTitle ( \"BigList\" ). get (). then ( r => { this . domElement . innerHTML += r . Title ; }); config.json \u00b6 \"externals\" : { \"@pnp/pnpjs\" : { \"path\" : \"https://cdnjs.cloudflare.com/ajax/libs/pnp-pnpjs/1.1.4/pnpjs.es5.umd.bundle.min.js\" , \"globalName\" : \"pnp\" } } , You can still work with the individual packages from a cdn, but you have a bit more work to do. First install the modules you need, update the config with the JSON externals below, and add some blind require statements into your code. These are needed because peer dependencies are not processed by SPFx so you have to \"trigger\" the SPFx manifest creator to include those packages. Note this approach requires using version 1.1.5 (specifically beta 1.1.5-2) or later of the libraries as we had make a few updates to how things are packaged to make this a little easier. Install \u00b6 npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp --save In Code \u00b6 // blind require statements require ( \"tslib\" ); require ( \"@pnp/logging\" ); require ( \"@pnp/core\" ); require ( \"@pnp/queryable\" ); import { sp } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( \"BigList\" ). get (). then ( r => { this . domElement . innerHTML += r . Title ; }); config.json \u00b6 \"externals\" : { \"@pnp/sp\" : { \"path\" : \"https://unpkg.com/@pnp/sp@1.1.5-2/dist/sp.es5.umd.min.js\" , \"globalName\" : \"pnp.sp\" , \"globalDependencies\" : [ \"@pnp/logging\" , \"@pnp/core\" , \"@pnp/queryable\" , \"tslib\" ] }, \"@pnp/queryable\" : { \"path\" : \"https://unpkg.com/@pnp/queryable@1.1.5-2/dist/odata.es5.umd.min.js\" , \"globalName\" : \"pnp.odata\" , \"globalDependencies\" : [ \"@pnp/core\" , \"@pnp/logging\" , \"tslib\" ] }, \"@pnp/core\" : { \"path\" : \"https://unpkg.com/@pnp/core@1.1.5-2/dist/common.es5.umd.bundle.min.js\" , \"globalName\" : \"pnp.common\" }, \"@pnp/logging\" : { \"path\" : \"https://unpkg.com/@pnp/logging@1.1.5-2/dist/logging.es5.umd.min.js\" , \"globalName\" : \"pnp.logging\" , \"globalDependencies\" : [ \"tslib\" ] }, \"tslib\" : { \"path\" : \"https://cdnjs.cloudflare.com/ajax/libs/tslib/1.9.3/tslib.min.js\" , \"globalName\" : \"tslib\" } } Don't forget to update the version number in the url to match the version you want to use. This will stop the library from being bundled directly into the solution and instead use the copy from the CDN. When a new version of the PnPjs libraries are released and you are ready to update just update this url in your SPFX project's config.js file.","title":"Deployment"},{"location":"documentation/deployment/#deployment","text":"There are two recommended ways to consume the library in a production deployment: bundle the code into your solution (such as with webpack), or reference the code from a CDN. These methods are outlined here but this is not meant to be an exhaustive guide on all the ways to package and deploy solutions.","title":"Deployment"},{"location":"documentation/deployment/#bundle","text":"If you have installed the library via NPM into your application solution bundlers such as webpack can bundle the PnPjs libraries along with your solution. This can make deployment easier, but will increase the size of your application by the size of the included libraries. The PnPjs libraries are setup to support tree shaking which can help with the bundle size.","title":"Bundle"},{"location":"documentation/deployment/#cdn","text":"If you have public internet access you can reference the library from cdnjs or unpkg which maintains copies of all versions. This is ideal as you do not need to host the file yourself, and it is easy to update to a newer release by updating the URL in your solution. Below lists all of the library locations within cdnjs, you will need to ensure you have the full url to the file you need, such as: \"https://cdnjs.cloudflare.com/ajax/libs/pnp-common/1.1.1/common.es5.umd.min.js\". To use the libraries with a script tag in a page it is recommended to use the *.es5.umd.min.js versions. This will add a global pnp value with each library added as pnp.{lib name} such as pnp.sp, pnp.common, etc. https://cdnjs.com/libraries/pnp-common https://cdnjs.com/libraries/pnp-config-store https://cdnjs.com/libraries/pnp-graph https://cdnjs.com/libraries/pnp-logging https://cdnjs.com/libraries/pnp-odata https://cdnjs.com/libraries/pnp-pnpjs https://cdnjs.com/libraries/pnp-sp https://cdnjs.com/libraries/pnp-sp-addinhelpers https://cdnjs.com/libraries/pnp-sp-clientsvc https://cdnjs.com/libraries/pnp-sp-taxonomy","title":"CDN"},{"location":"documentation/deployment/#cdn-and-spfx","text":"If you are developing in SPFx and install and import the PnPjs libraries the default behavior will be to bundle the library into your solution. You have a couple of choices on how best to work with CDNs and SPFx. Because SPFx doesn't currently respect peer dependencies it is easier to reference the pnpjs rollup package for your project. In this case you would install the package, reference it in your code, and update your config.js file externals as follows:","title":"CDN and SPFx"},{"location":"documentation/deployment/#install","text":"npm install @pnp/pnpjs --save","title":"Install"},{"location":"documentation/deployment/#in-code","text":"import { sp } from \"@pnp/pnpjs\" ; sp . web . lists . getByTitle ( \"BigList\" ). get (). then ( r => { this . domElement . innerHTML += r . Title ; });","title":"In Code"},{"location":"documentation/deployment/#configjson","text":"\"externals\" : { \"@pnp/pnpjs\" : { \"path\" : \"https://cdnjs.cloudflare.com/ajax/libs/pnp-pnpjs/1.1.4/pnpjs.es5.umd.bundle.min.js\" , \"globalName\" : \"pnp\" } } , You can still work with the individual packages from a cdn, but you have a bit more work to do. First install the modules you need, update the config with the JSON externals below, and add some blind require statements into your code. These are needed because peer dependencies are not processed by SPFx so you have to \"trigger\" the SPFx manifest creator to include those packages. Note this approach requires using version 1.1.5 (specifically beta 1.1.5-2) or later of the libraries as we had make a few updates to how things are packaged to make this a little easier.","title":"config.json"},{"location":"documentation/deployment/#install_1","text":"npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp --save","title":"Install"},{"location":"documentation/deployment/#in-code_1","text":"// blind require statements require ( \"tslib\" ); require ( \"@pnp/logging\" ); require ( \"@pnp/core\" ); require ( \"@pnp/queryable\" ); import { sp } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( \"BigList\" ). get (). then ( r => { this . domElement . innerHTML += r . Title ; });","title":"In Code"},{"location":"documentation/deployment/#configjson_1","text":"\"externals\" : { \"@pnp/sp\" : { \"path\" : \"https://unpkg.com/@pnp/sp@1.1.5-2/dist/sp.es5.umd.min.js\" , \"globalName\" : \"pnp.sp\" , \"globalDependencies\" : [ \"@pnp/logging\" , \"@pnp/core\" , \"@pnp/queryable\" , \"tslib\" ] }, \"@pnp/queryable\" : { \"path\" : \"https://unpkg.com/@pnp/queryable@1.1.5-2/dist/odata.es5.umd.min.js\" , \"globalName\" : \"pnp.odata\" , \"globalDependencies\" : [ \"@pnp/core\" , \"@pnp/logging\" , \"tslib\" ] }, \"@pnp/core\" : { \"path\" : \"https://unpkg.com/@pnp/core@1.1.5-2/dist/common.es5.umd.bundle.min.js\" , \"globalName\" : \"pnp.common\" }, \"@pnp/logging\" : { \"path\" : \"https://unpkg.com/@pnp/logging@1.1.5-2/dist/logging.es5.umd.min.js\" , \"globalName\" : \"pnp.logging\" , \"globalDependencies\" : [ \"tslib\" ] }, \"tslib\" : { \"path\" : \"https://cdnjs.cloudflare.com/ajax/libs/tslib/1.9.3/tslib.min.js\" , \"globalName\" : \"tslib\" } } Don't forget to update the version number in the url to match the version you want to use. This will stop the library from being bundled directly into the solution and instead use the copy from the CDN. When a new version of the PnPjs libraries are released and you are ready to update just update this url in your SPFX project's config.js file.","title":"config.json"},{"location":"documentation/documentation/","text":"Building the Documentation \u00b6 Building the documentation locally can help you visualize change you are making to the docs. What you see locally should be what you see online. Building \u00b6 Documentation is built using MkDocs. You will need to latest version of Python (tested on version 3.7.1) and pip. If you're on the Windows operating system, make sure you have added Python to your Path environment variable . When executing the pip module on Windows you can prefix it with python -m . For example: python -m pip install mkdocs-material Install MkDocs pip install mkdocs Install the Material theme pip install mkdocs-material install the mkdocs-markdownextradata-plugin - this is used for the version variable pip install mkdocs-markdownextradata-plugin (doesn't work on Python v2.7) Serve it up mkdocs serve Open a browser to http://127.0.0.1:8000/","title":"Building Docs"},{"location":"documentation/documentation/#building-the-documentation","text":"Building the documentation locally can help you visualize change you are making to the docs. What you see locally should be what you see online.","title":"Building the Documentation"},{"location":"documentation/documentation/#building","text":"Documentation is built using MkDocs. You will need to latest version of Python (tested on version 3.7.1) and pip. If you're on the Windows operating system, make sure you have added Python to your Path environment variable . When executing the pip module on Windows you can prefix it with python -m . For example: python -m pip install mkdocs-material Install MkDocs pip install mkdocs Install the Material theme pip install mkdocs-material install the mkdocs-markdownextradata-plugin - this is used for the version variable pip install mkdocs-markdownextradata-plugin (doesn't work on Python v2.7) Serve it up mkdocs serve Open a browser to http://127.0.0.1:8000/","title":"Building"},{"location":"documentation/getting-started-dev/","text":"Contribution Guide \u00b6 Thank you for your interest in contributing to our work. This guide should help you get started, please let us know if you have any questions. Contributor Guidance \u00b6 Target your pull requests to the dev branch Add/Update any relevant docs articles in the relevant package's docs folder related to your changes Include a test for any new functionality and ensure all existing tests are passing by running gulp test Ensure tslint checks pass by typing gulp lint Keep your PRs as simple as possible and describe the changes to help the reviewer understand your work If you have an idea for a larger change to the library please open an issue and let's discuss before you invest many hours - these are very welcome but want to ensure it is something we can merge before you spend the time :) Setup your development environment \u00b6 These steps will help you get your environment setup for contributing to the core library. Install Visual Studio Code - this is the development environment we will use. It is similar to a light-weight Visual Studio designed for each editing of client file types such as .ts and .js. (Note that if you prefer you can use Visual Studio). Install Node JS - this provides two key capabilities; the first is the nodejs server which will act as our development server (think iisexpress), the second is npm a package manager (think nuget). On Windows: Install Python v2.7.10 - this is used by some of the plug-ins and build tools inside Node JS - (Python v3.x.x is not supported by those modules). If Visual Studio is not installed on the client in addition to this C++ runtime is required. Please see node-gyp Readme Install a console emulator of your choice, for Windows Cmder is popular. If installing Cmder choosing the full option will allow you to use git for windows. Whatever option you choose we will refer in the rest of the guide to \"console\" as the thing you installed in this step. Install the tslint extension in VS Code: Press Shift + Ctrl + \"p\" to open the command panel Begin typing \"install extension\" and select the command when it appears in view Begin typing \"tslint\" and select the package when it appears in view Restart Code after installation Install the gulp command line globally by typing the following code in your console npm install -g gulp-cli Now we need to fork and clone the git repository. This can be done using your console or using your preferred Git GUI tool. Once you have the code locally, navigate to the root of the project in your console. Type the following command: npm install - installs all of the npm package dependencies (may take awhile the first time) Copy settings.example.js in the root of your project to settings.js. Edit settings.js to reflect your personal environment (usename, password, siteUrl, etc.). Then you can follow the guidance in the debugging article to get started testing right away!","title":"Getting Started Contributing"},{"location":"documentation/getting-started-dev/#contribution-guide","text":"Thank you for your interest in contributing to our work. This guide should help you get started, please let us know if you have any questions.","title":"Contribution Guide"},{"location":"documentation/getting-started-dev/#contributor-guidance","text":"Target your pull requests to the dev branch Add/Update any relevant docs articles in the relevant package's docs folder related to your changes Include a test for any new functionality and ensure all existing tests are passing by running gulp test Ensure tslint checks pass by typing gulp lint Keep your PRs as simple as possible and describe the changes to help the reviewer understand your work If you have an idea for a larger change to the library please open an issue and let's discuss before you invest many hours - these are very welcome but want to ensure it is something we can merge before you spend the time :)","title":"Contributor Guidance"},{"location":"documentation/getting-started-dev/#setup-your-development-environment","text":"These steps will help you get your environment setup for contributing to the core library. Install Visual Studio Code - this is the development environment we will use. It is similar to a light-weight Visual Studio designed for each editing of client file types such as .ts and .js. (Note that if you prefer you can use Visual Studio). Install Node JS - this provides two key capabilities; the first is the nodejs server which will act as our development server (think iisexpress), the second is npm a package manager (think nuget). On Windows: Install Python v2.7.10 - this is used by some of the plug-ins and build tools inside Node JS - (Python v3.x.x is not supported by those modules). If Visual Studio is not installed on the client in addition to this C++ runtime is required. Please see node-gyp Readme Install a console emulator of your choice, for Windows Cmder is popular. If installing Cmder choosing the full option will allow you to use git for windows. Whatever option you choose we will refer in the rest of the guide to \"console\" as the thing you installed in this step. Install the tslint extension in VS Code: Press Shift + Ctrl + \"p\" to open the command panel Begin typing \"install extension\" and select the command when it appears in view Begin typing \"tslint\" and select the package when it appears in view Restart Code after installation Install the gulp command line globally by typing the following code in your console npm install -g gulp-cli Now we need to fork and clone the git repository. This can be done using your console or using your preferred Git GUI tool. Once you have the code locally, navigate to the root of the project in your console. Type the following command: npm install - installs all of the npm package dependencies (may take awhile the first time) Copy settings.example.js in the root of your project to settings.js. Edit settings.js to reflect your personal environment (usename, password, siteUrl, etc.). Then you can follow the guidance in the debugging article to get started testing right away!","title":"Setup your development environment"},{"location":"documentation/getting-started/","text":"Getting Started \u00b6 These libraries are geared towards folks working with TypeScript but will work equally well for JavaScript projects. To get started you need to install the libraries you need via npm. Many of the packages have a peer dependency to other packages with the @pnp namespace meaning you may need to install more than one package. All packages are released together eliminating version confusion - all packages will depend on packages with the same version number. If you need to support older browsers please review the article on polyfills for required functionality. Install \u00b6 First you will need to install those libraries you want to use in your application. Here we will install the most frequently used packages. This step applies to any environment or project. npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/graph --save Next we can import and use the functionality within our application. The below is a very simple example, please see the individual package documentation for more details. import { getRandomString } from \"@pnp/core\" ; ( function () { // get and log a random string console . log ( getRandomString ( 20 )); })() Getting Started with SharePoint Framework \u00b6 The @pnp/sp and @pnp/graph libraries are designed to work seamlessly within SharePoint Framework projects with a small amount of upfront configuration. If you are running in 2016 on-premises please read this note on a workaround for the included TypeScript version. If you are targetting SharePoint online you do not need to take any additional steps. Establish Context \u00b6 Because SharePoint Framework provides a local context to each component we need to set that context within the library. This allows us to determine request urls as well as use the SPFx HttpGraphClient within @pnp/graph. There are two ways to provide the spfx context to the library. Either through the setup method imported from @pnp/core or using the setup method on either the @pnp/sp or @pnp/graph main export. All three are shown below and are equivalent, meaning if you are already importing the sp variable from @pnp/sp or the graph variable from @pnp/graph you should use their setup method to reduce imports. The setup is always done in the onInit method to ensure it runs before your other lifecycle code. You can also set any other settings at this time. Using @pnp/core setup \u00b6 import { setup as pnpSetup } from \"@pnp/core\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present pnpSetup ({ spfxContext : this.context }); }); } // ... Using @pnp/sp setup \u00b6 import { sp } from \"@pnp/sp\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present sp . setup ({ spfxContext : this.context }); }); } // ... Using @pnp/graph setup \u00b6 import { graph } from \"@pnp/graph\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present graph . setup ({ spfxContext : this.context }); }); } // ... Establish Context with an SPFx Service \u00b6 Because you do not have access to the full context object within a service you need to setup things slightly differently. This works for the sp library, but not the graph library as we don't have access to the AAD token provider from the full context. import { ServiceKey , ServiceScope } from \"@microsoft/sp-core-library\" ; import { PageContext } from \"@microsoft/sp-page-context\" ; import { AadTokenProviderFactory } from \"@microsoft/sp-http\" ; import { sp } from \"@pnp/sp\" ; export interface ISampleService { getLists () : Promise < any [] > ; } export class SampleService { public static readonly serviceKey : ServiceKey < ISampleService > = ServiceKey . create < ISampleService > ( 'SPFx:SampleService' , SampleService ); constructor ( serviceScope : ServiceScope ) { serviceScope . whenFinished (() => { const pageContext = serviceScope . consume ( PageContext . serviceKey ); const tokenProviderFactory = serviceScope . consume ( AadTokenProviderFactory . serviceKey ); // we need to \"spoof\" the context object with the parts we need for PnPjs sp . setup ({ spfxContext : { aadTokenProviderFactory : tokenProviderFactory , pageContext : pageContext , } }); // This approach also works if you do not require AAD tokens // you don't need to do both // sp.setup({ // sp : { // baseUrl : pageContext.web.absoluteUrl // } // }); }); } public getLists () : Promise < any [] > { return sp . web . lists . get (); } } Connect to SharePoint from Node \u00b6 Because peer dependencies are not installed automatically you will need to list out each package to install. Don't worry if you forget one you will get a message on the command line that a peer dependency is missing. Let's for example look at installing the required libraries to connect to SharePoint from nodejs. You can see ./debug/launch/sp.ts for a live example. npm i @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/nodejs This will install the logging, common, odata, sp, and nodejs packages. You can read more about what each package does starting on the packages page. Once these are installed you need to import them into your project, to communicate with SharePoint from node we'll need the following imports: import { sp } from \"@pnp/sp\" ; import { SPFetchClient } from \"@pnp/nodejs\" ; Once you have imported the necessary resources you can update your code to setup the node fetch client as well as make a call to SharePoint. // configure your node options (only once in your application) sp . setup ({ sp : { fetchClientFactory : () => { return new SPFetchClient ( \"{site url}\" , \"{client id}\" , \"{client secret}\" ); }, }, }); // make a call to SharePoint and log it in the console sp . web . select ( \"Title\" , \"Description\" ). get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); }); Connect to Microsoft Graph From Node \u00b6 Similar to the above you can also make calls to the Graph api from node using the libraries. Again we start with installing the required resources. You can see ./debug/launch/graph.ts for a live example. npm i @pnp/logging @pnp/core @pnp/queryable @pnp/graph @pnp/nodejs Now we need to import what we'll need to call graph import { graph } from \"@pnp/graph\" ; import { AdalFetchClient } from \"@pnp/nodejs\" ; Now we can make our graph calls after setting up the Adal client. Note you'll need to setup an AzureAD App registration with the necessary permissions. graph . setup ({ graph : { fetchClientFactory : () => { return new AdalFetchClient ( \"{mytenant}.onmicrosoft.com\" , \"{application id}\" , \"{application secret}\" ); }, }, }); // make a call to Graph and get all the groups graph . v1 . groups . get (). then ( g => { console . log ( JSON . stringify ( g , null , 4 )); }); Getting Started outside SharePoint Framework \u00b6 In some cases you may be working in a way such that we cannot determine the base url for the web. In this scenario you have two options. Set baseUrl through setup: \u00b6 Here we are setting the baseUrl via the sp.setup method. We are also setting the headers to use verbose mode, something you may have to do when working against unpatched versions of SharePoint 2013 as discussed here . This is optional for 2016 or SharePoint Online. import { sp } from \"@pnp/sp\" ; sp . setup ({ sp : { headers : { Accept : \"application/json;odata=verbose\" , }, baseUrl : \"{Absolute SharePoint Web URL}\" }, }); const w = await sp . web . get (); Create Web instances directly \u00b6 Using this method you create the web directly with the url you want to use as the base. import { Web } from \"@pnp/sp\" ; const web = new Web ( \"{Absolute SharePoint Web URL}\" ); const w = await web . get ();","title":"Getting Started"},{"location":"documentation/getting-started/#getting-started","text":"These libraries are geared towards folks working with TypeScript but will work equally well for JavaScript projects. To get started you need to install the libraries you need via npm. Many of the packages have a peer dependency to other packages with the @pnp namespace meaning you may need to install more than one package. All packages are released together eliminating version confusion - all packages will depend on packages with the same version number. If you need to support older browsers please review the article on polyfills for required functionality.","title":"Getting Started"},{"location":"documentation/getting-started/#install","text":"First you will need to install those libraries you want to use in your application. Here we will install the most frequently used packages. This step applies to any environment or project. npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/graph --save Next we can import and use the functionality within our application. The below is a very simple example, please see the individual package documentation for more details. import { getRandomString } from \"@pnp/core\" ; ( function () { // get and log a random string console . log ( getRandomString ( 20 )); })()","title":"Install"},{"location":"documentation/getting-started/#getting-started-with-sharepoint-framework","text":"The @pnp/sp and @pnp/graph libraries are designed to work seamlessly within SharePoint Framework projects with a small amount of upfront configuration. If you are running in 2016 on-premises please read this note on a workaround for the included TypeScript version. If you are targetting SharePoint online you do not need to take any additional steps.","title":"Getting Started with SharePoint Framework"},{"location":"documentation/getting-started/#establish-context","text":"Because SharePoint Framework provides a local context to each component we need to set that context within the library. This allows us to determine request urls as well as use the SPFx HttpGraphClient within @pnp/graph. There are two ways to provide the spfx context to the library. Either through the setup method imported from @pnp/core or using the setup method on either the @pnp/sp or @pnp/graph main export. All three are shown below and are equivalent, meaning if you are already importing the sp variable from @pnp/sp or the graph variable from @pnp/graph you should use their setup method to reduce imports. The setup is always done in the onInit method to ensure it runs before your other lifecycle code. You can also set any other settings at this time.","title":"Establish Context"},{"location":"documentation/getting-started/#using-pnpcommon-setup","text":"import { setup as pnpSetup } from \"@pnp/core\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present pnpSetup ({ spfxContext : this.context }); }); } // ...","title":"Using @pnp/core setup"},{"location":"documentation/getting-started/#using-pnpsp-setup","text":"import { sp } from \"@pnp/sp\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present sp . setup ({ spfxContext : this.context }); }); } // ...","title":"Using @pnp/sp setup"},{"location":"documentation/getting-started/#using-pnpgraph-setup","text":"import { graph } from \"@pnp/graph\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present graph . setup ({ spfxContext : this.context }); }); } // ...","title":"Using @pnp/graph setup"},{"location":"documentation/getting-started/#establish-context-with-an-spfx-service","text":"Because you do not have access to the full context object within a service you need to setup things slightly differently. This works for the sp library, but not the graph library as we don't have access to the AAD token provider from the full context. import { ServiceKey , ServiceScope } from \"@microsoft/sp-core-library\" ; import { PageContext } from \"@microsoft/sp-page-context\" ; import { AadTokenProviderFactory } from \"@microsoft/sp-http\" ; import { sp } from \"@pnp/sp\" ; export interface ISampleService { getLists () : Promise < any [] > ; } export class SampleService { public static readonly serviceKey : ServiceKey < ISampleService > = ServiceKey . create < ISampleService > ( 'SPFx:SampleService' , SampleService ); constructor ( serviceScope : ServiceScope ) { serviceScope . whenFinished (() => { const pageContext = serviceScope . consume ( PageContext . serviceKey ); const tokenProviderFactory = serviceScope . consume ( AadTokenProviderFactory . serviceKey ); // we need to \"spoof\" the context object with the parts we need for PnPjs sp . setup ({ spfxContext : { aadTokenProviderFactory : tokenProviderFactory , pageContext : pageContext , } }); // This approach also works if you do not require AAD tokens // you don't need to do both // sp.setup({ // sp : { // baseUrl : pageContext.web.absoluteUrl // } // }); }); } public getLists () : Promise < any [] > { return sp . web . lists . get (); } }","title":"Establish Context with an SPFx Service"},{"location":"documentation/getting-started/#connect-to-sharepoint-from-node","text":"Because peer dependencies are not installed automatically you will need to list out each package to install. Don't worry if you forget one you will get a message on the command line that a peer dependency is missing. Let's for example look at installing the required libraries to connect to SharePoint from nodejs. You can see ./debug/launch/sp.ts for a live example. npm i @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/nodejs This will install the logging, common, odata, sp, and nodejs packages. You can read more about what each package does starting on the packages page. Once these are installed you need to import them into your project, to communicate with SharePoint from node we'll need the following imports: import { sp } from \"@pnp/sp\" ; import { SPFetchClient } from \"@pnp/nodejs\" ; Once you have imported the necessary resources you can update your code to setup the node fetch client as well as make a call to SharePoint. // configure your node options (only once in your application) sp . setup ({ sp : { fetchClientFactory : () => { return new SPFetchClient ( \"{site url}\" , \"{client id}\" , \"{client secret}\" ); }, }, }); // make a call to SharePoint and log it in the console sp . web . select ( \"Title\" , \"Description\" ). get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); });","title":"Connect to SharePoint from Node"},{"location":"documentation/getting-started/#connect-to-microsoft-graph-from-node","text":"Similar to the above you can also make calls to the Graph api from node using the libraries. Again we start with installing the required resources. You can see ./debug/launch/graph.ts for a live example. npm i @pnp/logging @pnp/core @pnp/queryable @pnp/graph @pnp/nodejs Now we need to import what we'll need to call graph import { graph } from \"@pnp/graph\" ; import { AdalFetchClient } from \"@pnp/nodejs\" ; Now we can make our graph calls after setting up the Adal client. Note you'll need to setup an AzureAD App registration with the necessary permissions. graph . setup ({ graph : { fetchClientFactory : () => { return new AdalFetchClient ( \"{mytenant}.onmicrosoft.com\" , \"{application id}\" , \"{application secret}\" ); }, }, }); // make a call to Graph and get all the groups graph . v1 . groups . get (). then ( g => { console . log ( JSON . stringify ( g , null , 4 )); });","title":"Connect to Microsoft Graph From Node"},{"location":"documentation/getting-started/#getting-started-outside-sharepoint-framework","text":"In some cases you may be working in a way such that we cannot determine the base url for the web. In this scenario you have two options.","title":"Getting Started outside SharePoint Framework"},{"location":"documentation/getting-started/#set-baseurl-through-setup","text":"Here we are setting the baseUrl via the sp.setup method. We are also setting the headers to use verbose mode, something you may have to do when working against unpatched versions of SharePoint 2013 as discussed here . This is optional for 2016 or SharePoint Online. import { sp } from \"@pnp/sp\" ; sp . setup ({ sp : { headers : { Accept : \"application/json;odata=verbose\" , }, baseUrl : \"{Absolute SharePoint Web URL}\" }, }); const w = await sp . web . get ();","title":"Set baseUrl through setup:"},{"location":"documentation/getting-started/#create-web-instances-directly","text":"Using this method you create the web directly with the url you want to use as the base. import { Web } from \"@pnp/sp\" ; const web = new Web ( \"{Absolute SharePoint Web URL}\" ); const w = await web . get ();","title":"Create Web instances directly"},{"location":"documentation/gulp-commands/","text":"Gulp Commands \u00b6 This library uses Gulp to orchestrate various tasks. The tasks described below are available for your use. Please review the getting started for development to ensure you've setup your environment correctly. The source for the gulp commands can be found in the tools\\gulptasks folder at the root of the project. Basics \u00b6 All gulp commands are run on the command line in the fashion shown below. gulp [optional pararms] build \u00b6 The build command transpiles the solution from TypeScript into JavaScript using our custom build system . It is controlled by the pnp-build.js file at the project root. Build all of the packages \u00b6 gulp build Building individual packages \u00b6 Note when building a single package none of the dependencies are currently built, so you need to specify in order those packages to build which are dependencies. # fails gulp build --p sp # works as all the dependencies are built in order gulp build --p logging,common,odata,sp You can also build the packages and then not clean using the nc flag. So for example if you are working on the sp package you can build all the packages once, then use the \"nc\" flag to leave those that aren't changing. # run once gulp build --p logging,common,odata,sp # run on subsequent builds gulp build --p sp --nc clean \u00b6 The clean command removes all of the generated folders from the project and is generally used automatically before other commands to ensure there is a clean workspace. gulp clean To clean the build folder. This build folder is no longer included in automatic cleaning after the move to use the TypeScript project references feature that compares previous output and doesn't rebuild unchanged files. This command will erase the entire build folder ensuring you can conduct a clean build/test/etc. gulp clean-build lint \u00b6 Runs the project linting based on the tslint.json rules defined at the project root. This should be done before any PR submissions as linting failures will block merging. gulp lint package \u00b6 Used to create the packages in the ./dist folder as they would exist for a release. gulp package Packaging individual packages \u00b6 You can also package individual packages, but as with build you must also package any dependencies at the same time. gulp package --p logging,common,odata,sp publish \u00b6 This command is only for use by package authors to publish a version to npm and is not for developer use. serve \u00b6 The serve command allows you to serve either code from the ./debug/serve folder OR an individual package for testing in the browser. The file will always be served as https://localhost:8080/assets/pnp.js so can create a static page in your tenant for easy testing of a variety of scenarios. NOTE that in most browsers this file will be flagged as unsafe so you will need to trust it for it to execute on the page. debug serve \u00b6 When running the command with no parameters you will generate a package with the entry being based on the tsconfig.json file in ./debug/serve. By default this will use serve.ts. This allows you to write any code you want to test to easily run it in the browser with all changes being watched and triggering a rebuild. gulp serve package serve \u00b6 If instead you want to test how a particular package will work in the browser you can serve just that package. In this case you do not need to specify the dependencies and specifying multiple packages will throw an error. Packages will be injected into the global namespace on a variable named pnp. gulp serve --p sp test \u00b6 Runs the tests specified in each package's tests folder gulp test Verbose \u00b6 The test command will switch to the \"spec\" mocha reporter if you supply the verbose flag. Doing so will list out each test's description and sucess instead of the \"dot\" used by default. This flag works with all other test options. gulp test --verbose Test individual packages \u00b6 You can test individual packages as needed, and there is no need to include dependencies in this case # test the logging and sp packages gulp test --p logging,sp If you are working on a specific set of tests for a single module you can also use the single or s parameter to select just a single module of tests. You specify the filename without the \".test.ts\" suffix. It must be within the specified package and this option can only be used with a single package for --p # will test only the client-side pages module within the sp package gulp test --p sp --s clientsidepages If you want you can test within the same site and avoid creating a new one, though for some tests this might cause conflicts. This flag can be helpful if you are rapidly testing things with no conflict as you can avoid creating a site each time. Works with both of the above options --p and --s as well as individually. The url must be absolute. #testing using the specified site. gulp test --site https://{tenant}.sharepoint.com/sites/testing # with other options gulp test --p logging,sp --site https://{tenant}.sharepoint.com/sites/testing gulp test --p sp --s clientsidepages --site https://{tenant}.sharepoint.com/sites/testing","title":"Gulp Commands"},{"location":"documentation/gulp-commands/#gulp-commands","text":"This library uses Gulp to orchestrate various tasks. The tasks described below are available for your use. Please review the getting started for development to ensure you've setup your environment correctly. The source for the gulp commands can be found in the tools\\gulptasks folder at the root of the project.","title":"Gulp Commands"},{"location":"documentation/gulp-commands/#basics","text":"All gulp commands are run on the command line in the fashion shown below. gulp [optional pararms]","title":"Basics"},{"location":"documentation/gulp-commands/#build","text":"The build command transpiles the solution from TypeScript into JavaScript using our custom build system . It is controlled by the pnp-build.js file at the project root.","title":"build"},{"location":"documentation/gulp-commands/#build-all-of-the-packages","text":"gulp build","title":"Build all of the packages"},{"location":"documentation/gulp-commands/#building-individual-packages","text":"Note when building a single package none of the dependencies are currently built, so you need to specify in order those packages to build which are dependencies. # fails gulp build --p sp # works as all the dependencies are built in order gulp build --p logging,common,odata,sp You can also build the packages and then not clean using the nc flag. So for example if you are working on the sp package you can build all the packages once, then use the \"nc\" flag to leave those that aren't changing. # run once gulp build --p logging,common,odata,sp # run on subsequent builds gulp build --p sp --nc","title":"Building individual packages"},{"location":"documentation/gulp-commands/#clean","text":"The clean command removes all of the generated folders from the project and is generally used automatically before other commands to ensure there is a clean workspace. gulp clean To clean the build folder. This build folder is no longer included in automatic cleaning after the move to use the TypeScript project references feature that compares previous output and doesn't rebuild unchanged files. This command will erase the entire build folder ensuring you can conduct a clean build/test/etc. gulp clean-build","title":"clean"},{"location":"documentation/gulp-commands/#lint","text":"Runs the project linting based on the tslint.json rules defined at the project root. This should be done before any PR submissions as linting failures will block merging. gulp lint","title":"lint"},{"location":"documentation/gulp-commands/#package","text":"Used to create the packages in the ./dist folder as they would exist for a release. gulp package","title":"package"},{"location":"documentation/gulp-commands/#packaging-individual-packages","text":"You can also package individual packages, but as with build you must also package any dependencies at the same time. gulp package --p logging,common,odata,sp","title":"Packaging individual packages"},{"location":"documentation/gulp-commands/#publish","text":"This command is only for use by package authors to publish a version to npm and is not for developer use.","title":"publish"},{"location":"documentation/gulp-commands/#serve","text":"The serve command allows you to serve either code from the ./debug/serve folder OR an individual package for testing in the browser. The file will always be served as https://localhost:8080/assets/pnp.js so can create a static page in your tenant for easy testing of a variety of scenarios. NOTE that in most browsers this file will be flagged as unsafe so you will need to trust it for it to execute on the page.","title":"serve"},{"location":"documentation/gulp-commands/#debug-serve","text":"When running the command with no parameters you will generate a package with the entry being based on the tsconfig.json file in ./debug/serve. By default this will use serve.ts. This allows you to write any code you want to test to easily run it in the browser with all changes being watched and triggering a rebuild. gulp serve","title":"debug serve"},{"location":"documentation/gulp-commands/#package-serve","text":"If instead you want to test how a particular package will work in the browser you can serve just that package. In this case you do not need to specify the dependencies and specifying multiple packages will throw an error. Packages will be injected into the global namespace on a variable named pnp. gulp serve --p sp","title":"package serve"},{"location":"documentation/gulp-commands/#test","text":"Runs the tests specified in each package's tests folder gulp test","title":"test"},{"location":"documentation/gulp-commands/#verbose","text":"The test command will switch to the \"spec\" mocha reporter if you supply the verbose flag. Doing so will list out each test's description and sucess instead of the \"dot\" used by default. This flag works with all other test options. gulp test --verbose","title":"Verbose"},{"location":"documentation/gulp-commands/#test-individual-packages","text":"You can test individual packages as needed, and there is no need to include dependencies in this case # test the logging and sp packages gulp test --p logging,sp If you are working on a specific set of tests for a single module you can also use the single or s parameter to select just a single module of tests. You specify the filename without the \".test.ts\" suffix. It must be within the specified package and this option can only be used with a single package for --p # will test only the client-side pages module within the sp package gulp test --p sp --s clientsidepages If you want you can test within the same site and avoid creating a new one, though for some tests this might cause conflicts. This flag can be helpful if you are rapidly testing things with no conflict as you can avoid creating a site each time. Works with both of the above options --p and --s as well as individually. The url must be absolute. #testing using the specified site. gulp test --site https://{tenant}.sharepoint.com/sites/testing # with other options gulp test --p logging,sp --site https://{tenant}.sharepoint.com/sites/testing gulp test --p sp --s clientsidepages --site https://{tenant}.sharepoint.com/sites/testing","title":"Test individual packages"},{"location":"documentation/package-structure/","text":"Package Structure \u00b6 Each of the packages is published with the same structure, so this article applies to all of the packages. We will use @pnp/core as an example for discussion. Folders \u00b6 In addition to the files in the root each package has three folders dist, docs, and src. Root Files \u00b6 These files are found at the root of each package. File Description index.d.ts Referenced in package.json typings property and provides the TypeScript type information for consumers LICENSE Package license package.json npm package definition readme.md Basic readme referencing the docs site Dist \u00b6 The dist folder contains the transpiled files bundled in various ways. You can choose the best file for your usage as needed. Below the {package} will be replaced with the name of the package - in our examples case this would be \"common\" making the file name \"{package}.es5.js\" = \"common.es5.js\". All of the *.map files are the debug mapping files related to the .js file of the same name. File Description {package}.es5.js Library packaged in es5 format not wrapped as a module {package}.es5.umd.bundle.js The library bundled with all dependencies into a single UMD module. Global variable will be \"pnp.{package}\". Referenced in the main property of package.json {package}.es5.umd.bundle.min.js Minified version of the bundled umd module {package}.es5.umd.js The library in es5 bundled as a UMD modules with no included dependencies. They are designed to work with the other *.es5.umd.js files. Referenced in the module property of package.json {package}.es5.umd.min.js Minified version of the es5 umd module {package}.js es6 format file of the library. Referenced by es2015 property of package.json Docs \u00b6 This folder contains markdown documentation for the library. All packages will include an index.md which serves as the root of the docs. These files are also used to build the public site . To edit these files they can be found in the packages/{package}/docs folder. Src \u00b6 Contains the TypeScript definition files refrenced by the index.d.ts in the package root. These files serve to provide typing information about the library to consumers who can process typing information.","title":"Package Structure"},{"location":"documentation/package-structure/#package-structure","text":"Each of the packages is published with the same structure, so this article applies to all of the packages. We will use @pnp/core as an example for discussion.","title":"Package Structure"},{"location":"documentation/package-structure/#folders","text":"In addition to the files in the root each package has three folders dist, docs, and src.","title":"Folders"},{"location":"documentation/package-structure/#root-files","text":"These files are found at the root of each package. File Description index.d.ts Referenced in package.json typings property and provides the TypeScript type information for consumers LICENSE Package license package.json npm package definition readme.md Basic readme referencing the docs site","title":"Root Files"},{"location":"documentation/package-structure/#dist","text":"The dist folder contains the transpiled files bundled in various ways. You can choose the best file for your usage as needed. Below the {package} will be replaced with the name of the package - in our examples case this would be \"common\" making the file name \"{package}.es5.js\" = \"common.es5.js\". All of the *.map files are the debug mapping files related to the .js file of the same name. File Description {package}.es5.js Library packaged in es5 format not wrapped as a module {package}.es5.umd.bundle.js The library bundled with all dependencies into a single UMD module. Global variable will be \"pnp.{package}\". Referenced in the main property of package.json {package}.es5.umd.bundle.min.js Minified version of the bundled umd module {package}.es5.umd.js The library in es5 bundled as a UMD modules with no included dependencies. They are designed to work with the other *.es5.umd.js files. Referenced in the module property of package.json {package}.es5.umd.min.js Minified version of the es5 umd module {package}.js es6 format file of the library. Referenced by es2015 property of package.json","title":"Dist"},{"location":"documentation/package-structure/#docs","text":"This folder contains markdown documentation for the library. All packages will include an index.md which serves as the root of the docs. These files are also used to build the public site . To edit these files they can be found in the packages/{package}/docs folder.","title":"Docs"},{"location":"documentation/package-structure/#src","text":"Contains the TypeScript definition files refrenced by the index.d.ts in the package root. These files serve to provide typing information about the library to consumers who can process typing information.","title":"Src"},{"location":"documentation/packages/","text":"The following packages comprise the Patterns and Practices client side libraries. All of the packages are published as a set and depend on their peers within the @pnp scope. The latest published version is ****. @pnp/ common Provides shared functionality across all pnp libraries config-store Provides a way to manage configuration within your application graph Provides a fluent api for working with Microsoft Graph logging Light-weight, subscribable logging framework nodejs Provides functionality enabling the @pnp libraries within nodejs odata Provides shared odata functionality and base classes pnpjs Rollup library of core functionality (mimics sp-pnp-js) sp Provides a fluent api for working with SharePoint REST sp-addinhelpers Provides functionality for working within SharePoint add-ins sp-clientsvc Provides base classes for working with the legacy SharePoint sp-taxonomy Provides a fluent api for working with SharePoint Managed Metadata","title":"Packages"},{"location":"documentation/polyfill/","text":"Polyfills \u00b6 These libraries may make use of some features not found in older browsers, mainly fetch, Map, and Proxy. This primarily affects Internet Explorer 11, which requires that we provide this missing functionality. There are several ways to include this missing functionality. IE 11 Polyfill package \u00b6 We created a package you can use to include the needed functionality without having to determine what polyfills are required. Also, this package is independent of the other @pnp/* packages and does not need to be updated monthly unless we introduce additional polyfills and publish a new version. This package is only needed if you need to support IE 11. Install \u00b6 npm install --save @pnp/polyfill-ie11 Use \u00b6 import \"@pnp/polyfill-ie11\" ; import { sp } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( \"BigList\" ). items . filter ( `ID gt 6000` ). get (). then ( r => { this . domElement . innerHTML += r . map ( l => ` ${ l . Title }
    ` ); }); SearchQueryBuilder \u00b6 Because the latest version of SearchQueryBuilder uses Proxy internally you can fall back on the older version for IE 11 as shown below. import \"@pnp/polyfill-ie11\" ; import { SearchQueryBuilder } from \"@pnp/polyfill-ie11/dist/searchquerybuilder\" ; import { sp , ISearchQueryBuilder } from \"@pnp/sp\" ; // works in IE11 and other browsers const builder : ISearchQueryBuilder = SearchQueryBuilder (). text ( \"test\" ); sp . search ( builder ). then ( r => { this . domElement . innerHTML = JSON . stringify ( r ); }); Polyfill Service \u00b6 If acceptable to your design and security requirements you can use a service to provide missing functionality. This loads scripts from a service outside of your and our control, so please ensure you understand any associated risks. To use this option you need to wrap the code in a function, here called \"stuffisloaded\". Then you need to add another script tag as shown below that will load what you need from the polyfill service. Note the parameter \"callback\" takes our function name. < script src = \"https://cdnjs.cloudflare.com/ajax/libs/pnp-pnpjs/1.2.1/pnpjs.es5.umd.bundle.min.js\" type = \"text/javascript\" > < script > // this function will be executed once the polyfill is loaded. function stuffisloaded () { pnp . sp . web . select ( \"Title\" ). get () . then ( function ( data ){ document . getElementById ( \"main\" ). innerText = data . Title ; }) . catch ( function ( err ){ document . getElementById ( \"main\" ). innerText = err ; }); } < script src = \"https://cdn.polyfill.io/v2/polyfill.min.js?callback=stuffisloaded&features=es6,fetch,Map&flags=always,gated\" > Module Loader \u00b6 If you are using a module loader you need to load the following two files as well. You can do this form a CDN or your style library. Download the es6-promises polyfill from https://github.com/stefanpenner/es6-promise and upload it to your style library. Download the fetch polyfill from https://github.com/github/fetch and upload it to your style library. Download the corejs polyfill from https://github.com/zloirock/core-js and upload it to your style library. Update your module loader to set these files as dependencies before the pnp library is opened. One issue you still may see is that you get errors that certain libraries are undefined when you try to run your code. This is because your code is running before these libraries are loaded. You need to ensure that all dependencies are loaded before making use of the pnp libraries.","title":"Polyfills"},{"location":"documentation/polyfill/#polyfills","text":"These libraries may make use of some features not found in older browsers, mainly fetch, Map, and Proxy. This primarily affects Internet Explorer 11, which requires that we provide this missing functionality. There are several ways to include this missing functionality.","title":"Polyfills"},{"location":"documentation/polyfill/#ie-11-polyfill-package","text":"We created a package you can use to include the needed functionality without having to determine what polyfills are required. Also, this package is independent of the other @pnp/* packages and does not need to be updated monthly unless we introduce additional polyfills and publish a new version. This package is only needed if you need to support IE 11.","title":"IE 11 Polyfill package"},{"location":"documentation/polyfill/#install","text":"npm install --save @pnp/polyfill-ie11","title":"Install"},{"location":"documentation/polyfill/#use","text":"import \"@pnp/polyfill-ie11\" ; import { sp } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( \"BigList\" ). items . filter ( `ID gt 6000` ). get (). then ( r => { this . domElement . innerHTML += r . map ( l => ` ${ l . Title }
    ` ); });","title":"Use"},{"location":"documentation/polyfill/#searchquerybuilder","text":"Because the latest version of SearchQueryBuilder uses Proxy internally you can fall back on the older version for IE 11 as shown below. import \"@pnp/polyfill-ie11\" ; import { SearchQueryBuilder } from \"@pnp/polyfill-ie11/dist/searchquerybuilder\" ; import { sp , ISearchQueryBuilder } from \"@pnp/sp\" ; // works in IE11 and other browsers const builder : ISearchQueryBuilder = SearchQueryBuilder (). text ( \"test\" ); sp . search ( builder ). then ( r => { this . domElement . innerHTML = JSON . stringify ( r ); });","title":"SearchQueryBuilder"},{"location":"documentation/polyfill/#polyfill-service","text":"If acceptable to your design and security requirements you can use a service to provide missing functionality. This loads scripts from a service outside of your and our control, so please ensure you understand any associated risks. To use this option you need to wrap the code in a function, here called \"stuffisloaded\". Then you need to add another script tag as shown below that will load what you need from the polyfill service. Note the parameter \"callback\" takes our function name. < script src = \"https://cdnjs.cloudflare.com/ajax/libs/pnp-pnpjs/1.2.1/pnpjs.es5.umd.bundle.min.js\" type = \"text/javascript\" > < script > // this function will be executed once the polyfill is loaded. function stuffisloaded () { pnp . sp . web . select ( \"Title\" ). get () . then ( function ( data ){ document . getElementById ( \"main\" ). innerText = data . Title ; }) . catch ( function ( err ){ document . getElementById ( \"main\" ). innerText = err ; }); } < script src = \"https://cdn.polyfill.io/v2/polyfill.min.js?callback=stuffisloaded&features=es6,fetch,Map&flags=always,gated\" >","title":"Polyfill Service"},{"location":"documentation/polyfill/#module-loader","text":"If you are using a module loader you need to load the following two files as well. You can do this form a CDN or your style library. Download the es6-promises polyfill from https://github.com/stefanpenner/es6-promise and upload it to your style library. Download the fetch polyfill from https://github.com/github/fetch and upload it to your style library. Download the corejs polyfill from https://github.com/zloirock/core-js and upload it to your style library. Update your module loader to set these files as dependencies before the pnp library is opened. One issue you still may see is that you get errors that certain libraries are undefined when you try to run your code. This is because your code is running before these libraries are loaded. You need to ensure that all dependencies are loaded before making use of the pnp libraries.","title":"Module Loader"},{"location":"documentation/transition-guide/","text":"Transition Guide \u00b6 These libraries are based on the sp-pnp-js library and our goal was to make transition as easy as possible. The most obvious difference is the splitting of the library into multiple packages. We have however created a rollup library to help folks make the move - though our recommendation is to switch to the separate packages. This article outlines transitioning your existing projects from sp-pnp-js to the new libraries, please provide feedback on how we can improve out guidance. Installing @pnp libraries \u00b6 With the separation of the packages we needed a way to indicate how they are related, while making things easy for folks to track and update and we have used peer dependencies between the packages to do this. With each release we will release all packages so that the version numbers move in lock-step, making it easy to ensure you are working with compatible versions. One thing to keep in mind with peer dependencies is that they are not automatically installed. The advantage is you will only have one copy of each library in your project. Installing peer dependencies is easy, you can specify each of the packages in a single line, here we are installing everything required to use the @pnp/sp package. npm i @pnp/logging @pnp/core @pnp/queryable @pnp/sp If you do not install all of the peer dependencies you will get a message specifying which ones are missing along with the version expected. Import Simplification \u00b6 With the separation of packages we have also simplified the imports, and allowed you more control over what you are importing. Compare these two examples showing the same set of imports, but one is done via sp-pnp-js and the other using the @pnp libraries. From sp-pnp-js \u00b6 import pnp , { Web , Util , Logger , FunctionListener , LogLevel , } from \"sp-pnp-js\" ; From @pnp libraries \u00b6 import { Logger , LogLevel , FunctionListener } from \"@pnp/logging\" ; import * as Util from \"@pnp/core\" ; import { sp , Web } from \"@pnp/sp\" ; In the above example the \"sp\" import replaces \"pnp\" and is the root of your method chains. Once we have updated our imports we have a few small code changes to make, depending on how you have used the library in your applications. Watch this short video discussing the most common updates: Updated settings file format \u00b6 If you are doing local debugging or testing you have likely created a settings.js from the supplied settings.example.js. Please note the format of that file has changed, the new format is shown below. var settings = { spsave : { username : \"develina.devsson@mydevtenant.onmicrosoft.com\" , password : \"pass@word1\" , siteUrl : \"https://mydevtenant.sharepoint.com/\" }, testing : { enableWebTests : true , sp : { id : \"{ client id }\" , secret : \"{ client secret }\" , url : \"{ site collection url }\" , notificationUrl : \"{ notification url }\" , }, graph : { tenant : \"{tenant.onmicrosoft.com}\" , id : \"{your app id}\" , secret : \"{your secret}\" }, } } HttpClient Renamed \u00b6 If you used HttpClient from sp-pnp-js it was renamed to SPHttpClient. A transition to @pnp/sp assumes replacement of: import { HttpClient } from 'sp-pnp-js' ; to the following import statement: import { SPHttpClient } from '@pnp/sp' ;","title":"Transition Guide"},{"location":"documentation/transition-guide/#transition-guide","text":"These libraries are based on the sp-pnp-js library and our goal was to make transition as easy as possible. The most obvious difference is the splitting of the library into multiple packages. We have however created a rollup library to help folks make the move - though our recommendation is to switch to the separate packages. This article outlines transitioning your existing projects from sp-pnp-js to the new libraries, please provide feedback on how we can improve out guidance.","title":"Transition Guide"},{"location":"documentation/transition-guide/#installing-pnp-libraries","text":"With the separation of the packages we needed a way to indicate how they are related, while making things easy for folks to track and update and we have used peer dependencies between the packages to do this. With each release we will release all packages so that the version numbers move in lock-step, making it easy to ensure you are working with compatible versions. One thing to keep in mind with peer dependencies is that they are not automatically installed. The advantage is you will only have one copy of each library in your project. Installing peer dependencies is easy, you can specify each of the packages in a single line, here we are installing everything required to use the @pnp/sp package. npm i @pnp/logging @pnp/core @pnp/queryable @pnp/sp If you do not install all of the peer dependencies you will get a message specifying which ones are missing along with the version expected.","title":"Installing @pnp libraries"},{"location":"documentation/transition-guide/#import-simplification","text":"With the separation of packages we have also simplified the imports, and allowed you more control over what you are importing. Compare these two examples showing the same set of imports, but one is done via sp-pnp-js and the other using the @pnp libraries.","title":"Import Simplification"},{"location":"documentation/transition-guide/#from-sp-pnp-js","text":"import pnp , { Web , Util , Logger , FunctionListener , LogLevel , } from \"sp-pnp-js\" ;","title":"From sp-pnp-js"},{"location":"documentation/transition-guide/#from-pnp-libraries","text":"import { Logger , LogLevel , FunctionListener } from \"@pnp/logging\" ; import * as Util from \"@pnp/core\" ; import { sp , Web } from \"@pnp/sp\" ; In the above example the \"sp\" import replaces \"pnp\" and is the root of your method chains. Once we have updated our imports we have a few small code changes to make, depending on how you have used the library in your applications. Watch this short video discussing the most common updates:","title":"From @pnp libraries"},{"location":"documentation/transition-guide/#updated-settings-file-format","text":"If you are doing local debugging or testing you have likely created a settings.js from the supplied settings.example.js. Please note the format of that file has changed, the new format is shown below. var settings = { spsave : { username : \"develina.devsson@mydevtenant.onmicrosoft.com\" , password : \"pass@word1\" , siteUrl : \"https://mydevtenant.sharepoint.com/\" }, testing : { enableWebTests : true , sp : { id : \"{ client id }\" , secret : \"{ client secret }\" , url : \"{ site collection url }\" , notificationUrl : \"{ notification url }\" , }, graph : { tenant : \"{tenant.onmicrosoft.com}\" , id : \"{your app id}\" , secret : \"{your secret}\" }, } }","title":"Updated settings file format"},{"location":"documentation/transition-guide/#httpclient-renamed","text":"If you used HttpClient from sp-pnp-js it was renamed to SPHttpClient. A transition to @pnp/sp assumes replacement of: import { HttpClient } from 'sp-pnp-js' ; to the following import statement: import { SPHttpClient } from '@pnp/sp' ;","title":"HttpClient Renamed"},{"location":"graph/docs/","text":"@pnp/graph \u00b6 This package contains the fluent api used to call the graph rest services. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save Import the library into your application and access the root sp object import { graph } from \"@pnp/graph\" ; ( function main() { // here we will load the current web's properties graph . groups . get (). then ( g => { console . log ( `Groups: ${ JSON . stringify ( g , null , 4 ) } ` ); }); })() Getting Started with SharePoint Framework \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save Import the library into your application, update OnInit, and access the root sp object in render import { graph } from \"@pnp/graph\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present graph . setup ({ spfxContext : this.context }); }); } // ... public render () : void { // A simple loading message this . domElement . innerHTML = `Loading...` ; // here we will load the current web's properties graph . groups . get (). then ( groups => { this . domElement . innerHTML = `Groups:
      ${ groups . map ( g => `
    • ${ g . displayName }
    • ` ). join ( \"\" ) }
    ` ; }); } Getting Started on Nodejs \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph @pnp/nodejs --save Import the library into your application, setup the node client, make a request import { graph } from \"@pnp/graph\" ; import { AdalFetchClient } from \"@pnp/nodejs\" ; // do this once per page load graph . setup ({ graph : { fetchClientFactory : () => { return new AdalFetchClient ( \"{tenant}.onmicrosoft.com\" , \"AAD Application Id\" , \"AAD Application Secret\" ); }, }, }); // here we will load the groups information graph . groups . get (). then ( g => { console . log ( `Groups: ${ JSON . stringify ( g , null , 4 ) } ` ); }); UML \u00b6 Graphical UML diagram of @pnp/graph. Right-click the diagram and open in new tab if it is too small.","title":"graph"},{"location":"graph/docs/#pnpgraph","text":"This package contains the fluent api used to call the graph rest services.","title":"@pnp/graph"},{"location":"graph/docs/#getting-started","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save Import the library into your application and access the root sp object import { graph } from \"@pnp/graph\" ; ( function main() { // here we will load the current web's properties graph . groups . get (). then ( g => { console . log ( `Groups: ${ JSON . stringify ( g , null , 4 ) } ` ); }); })()","title":"Getting Started"},{"location":"graph/docs/#getting-started-with-sharepoint-framework","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save Import the library into your application, update OnInit, and access the root sp object in render import { graph } from \"@pnp/graph\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present graph . setup ({ spfxContext : this.context }); }); } // ... public render () : void { // A simple loading message this . domElement . innerHTML = `Loading...` ; // here we will load the current web's properties graph . groups . get (). then ( groups => { this . domElement . innerHTML = `Groups:
      ${ groups . map ( g => `
    • ${ g . displayName }
    • ` ). join ( \"\" ) }
    ` ; }); }","title":"Getting Started with SharePoint Framework"},{"location":"graph/docs/#getting-started-on-nodejs","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph @pnp/nodejs --save Import the library into your application, setup the node client, make a request import { graph } from \"@pnp/graph\" ; import { AdalFetchClient } from \"@pnp/nodejs\" ; // do this once per page load graph . setup ({ graph : { fetchClientFactory : () => { return new AdalFetchClient ( \"{tenant}.onmicrosoft.com\" , \"AAD Application Id\" , \"AAD Application Secret\" ); }, }, }); // here we will load the groups information graph . groups . get (). then ( g => { console . log ( `Groups: ${ JSON . stringify ( g , null , 4 ) } ` ); });","title":"Getting Started on Nodejs"},{"location":"graph/docs/#uml","text":"Graphical UML diagram of @pnp/graph. Right-click the diagram and open in new tab if it is too small.","title":"UML"},{"location":"graph/docs/contacts/","text":"@pnp/graph/contacts \u00b6 The ability to manage contacts and folders in Outlook is a capability introduced in version 1.2.2 of @pnp/graph. Through the methods described you can add and edit both contacts and folders in a users Outlook. Get all of the Contacts \u00b6 Using the contacts() you can get the users contacts from Outlook import { graph } from \"@pnp/graph\" ; const contacts = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contacts . get (); const contacts = await graph . me . contacts . get (); Add a new Contact \u00b6 Using the contacts.add() you can a add Contact to the users Outlook import { graph } from \"@pnp/graph\" ; const addedContact = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contacts . add ( 'Pavel' , 'Bansky' , [ < EmailAddress > { address : 'pavelb@fabrikam.onmicrosoft.com' , name : 'Pavel Bansky' }], [ '+1 732 555 0102' ]); const addedContact = await graph . me . contacts . add ( 'Pavel' , 'Bansky' , [ < EmailAddress > { address : 'pavelb@fabrikam.onmicrosoft.com' , name : 'Pavel Bansky' }], [ '+1 732 555 0102' ]); Get Contact by Id \u00b6 Using the contacts.getById() you can get one of the users Contacts in Outlook import { graph } from \"@pnp/graph\" ; const contact = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contacts . getById ( 'userId' ); const contact = await graph . me . contacts . getById ( 'userId' ); Delete a Contact \u00b6 Using the delete you can remove one of the users Contacts in Outlook import { graph } from \"@pnp/graph\" ; const delContact = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contacts . getById ( 'userId' ). delete (); const delContact = await graph . me . contacts . getById ( 'userId' ). delete (); Update a Contact \u00b6 Using the update you can update one of the users Contacts in Outlook import { graph } from \"@pnp/graph\" ; const updContact = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contacts . getById ( 'userId' ). update ({ birthday : \"1986-05-30\" }); const updContact = await graph . me . contacts . getById ( 'userId' ). update ({ birthday : \"1986-05-30\" }); Get all of the Contact Folders \u00b6 Using the contactFolders() you can get the users Contact Folders from Outlook import { graph } from \"@pnp/graph\" ; const contactFolders = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . get (); const contactFolders = await graph . me . contactFolders . get (); Add a new Contact Folder \u00b6 Using the contactFolders.add() you can a add Contact Folder to the users Outlook import { graph } from \"@pnp/graph\" ; const addedContactFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . add ( 'displayName' , '' ); const addedContactFolder = await graph . me . contactFolders . contactFolders . add ( 'displayName' , '' ); Get Contact Folder by Id \u00b6 Using the contactFolders.getById() you can get one of the users Contact Folders in Outlook import { graph } from \"@pnp/graph\" ; const contactFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( 'folderId' ); const contactFolder = await graph . me . contactFolders . getById ( 'folderId' ); Delete a Contact Folder \u00b6 Using the delete you can remove one of the users Contact Folders in Outlook import { graph } from \"@pnp/graph\" ; const delContactFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( 'folderId' ). delete (); const delContactFolder = await graph . me . contactFolders . getById ( 'folderId' ). delete (); Update a Contact Folder \u00b6 Using the update you can update one of the users Contact Folders in Outlook import { graph } from \"@pnp/graph\" ; const updContactFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( 'userId' ). update ({ displayName : \"value\" }); const updContactFolder = await graph . me . contactFolders . getById ( 'userId' ). update ({ displayName : \"value\" }); Get all of the Contacts from the Contact Folder \u00b6 Using the contacts() in the Contact Folder gets the users Contact from the folder. import { graph } from \"@pnp/graph\" ; const contactsInContactFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( 'folderId' ). contacts . get (); const contactsInContactFolder = await graph . me . contactFolders . getById ( 'folderId' ). contacts . get (); Get Child Folders of the Contact Folder \u00b6 Using the childFolders() you can get the Child Folders of the current Contact Folder from Outlook import { graph } from \"@pnp/graph\" ; const childFolders = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( '' ). childFolders . get (); const childFolders = await graph . me . contactFolders . getById ( '' ). childFolders . get (); Add a new Child Folder \u00b6 Using the childFolders.add() you can a add Child Folder in a Contact Folder import { graph } from \"@pnp/graph\" ; const addedChildFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( '' ). childFolders . add ( 'displayName' , '' ); const addedChildFolder = await graph . me . contactFolders . getById ( '' ). childFolders . add ( 'displayName' , '' ); Get Child Folder by Id \u00b6 Using the childFolders.getById() you can get one of the users Child Folders in Outlook import { graph } from \"@pnp/graph\" ; const childFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( '' ). childFolders . getById ( 'folderId' ); const childFolder = await graph . me . contactFolders . getById ( '' ). childFolders . getById ( 'folderId' ); Add Contact in Child Folder of Contact Folder \u00b6 Using contacts.add in the Child Folder of a Contact Folder, adds a new Contact to that folder import { graph } from \"@pnp/graph\" ; const addedContact = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( '' ). childFolders . getById ( 'folderId' ). contacts . add ( 'Pavel' , 'Bansky' , [ < EmailAddress > { address : 'pavelb@fabrikam.onmicrosoft.com' , name : 'Pavel Bansky' }], [ '+1 732 555 0102' ]); const addedContact = await graph . me . contactFolders . getById ( '' ). childFolders . getById ( 'folderId' ). contacts . add ( 'Pavel' , 'Bansky' , [ < EmailAddress > { address : 'pavelb@fabrikam.onmicrosoft.com' , name : 'Pavel Bansky' }], [ '+1 732 555 0102' ]);","title":"contacts"},{"location":"graph/docs/contacts/#pnpgraphcontacts","text":"The ability to manage contacts and folders in Outlook is a capability introduced in version 1.2.2 of @pnp/graph. Through the methods described you can add and edit both contacts and folders in a users Outlook.","title":"@pnp/graph/contacts"},{"location":"graph/docs/contacts/#get-all-of-the-contacts","text":"Using the contacts() you can get the users contacts from Outlook import { graph } from \"@pnp/graph\" ; const contacts = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contacts . get (); const contacts = await graph . me . contacts . get ();","title":"Get all of the Contacts"},{"location":"graph/docs/contacts/#add-a-new-contact","text":"Using the contacts.add() you can a add Contact to the users Outlook import { graph } from \"@pnp/graph\" ; const addedContact = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contacts . add ( 'Pavel' , 'Bansky' , [ < EmailAddress > { address : 'pavelb@fabrikam.onmicrosoft.com' , name : 'Pavel Bansky' }], [ '+1 732 555 0102' ]); const addedContact = await graph . me . contacts . add ( 'Pavel' , 'Bansky' , [ < EmailAddress > { address : 'pavelb@fabrikam.onmicrosoft.com' , name : 'Pavel Bansky' }], [ '+1 732 555 0102' ]);","title":"Add a new Contact"},{"location":"graph/docs/contacts/#get-contact-by-id","text":"Using the contacts.getById() you can get one of the users Contacts in Outlook import { graph } from \"@pnp/graph\" ; const contact = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contacts . getById ( 'userId' ); const contact = await graph . me . contacts . getById ( 'userId' );","title":"Get Contact by Id"},{"location":"graph/docs/contacts/#delete-a-contact","text":"Using the delete you can remove one of the users Contacts in Outlook import { graph } from \"@pnp/graph\" ; const delContact = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contacts . getById ( 'userId' ). delete (); const delContact = await graph . me . contacts . getById ( 'userId' ). delete ();","title":"Delete a Contact"},{"location":"graph/docs/contacts/#update-a-contact","text":"Using the update you can update one of the users Contacts in Outlook import { graph } from \"@pnp/graph\" ; const updContact = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contacts . getById ( 'userId' ). update ({ birthday : \"1986-05-30\" }); const updContact = await graph . me . contacts . getById ( 'userId' ). update ({ birthday : \"1986-05-30\" });","title":"Update a Contact"},{"location":"graph/docs/contacts/#get-all-of-the-contact-folders","text":"Using the contactFolders() you can get the users Contact Folders from Outlook import { graph } from \"@pnp/graph\" ; const contactFolders = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . get (); const contactFolders = await graph . me . contactFolders . get ();","title":"Get all of the Contact Folders"},{"location":"graph/docs/contacts/#add-a-new-contact-folder","text":"Using the contactFolders.add() you can a add Contact Folder to the users Outlook import { graph } from \"@pnp/graph\" ; const addedContactFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . add ( 'displayName' , '' ); const addedContactFolder = await graph . me . contactFolders . contactFolders . add ( 'displayName' , '' );","title":"Add a new Contact Folder"},{"location":"graph/docs/contacts/#get-contact-folder-by-id","text":"Using the contactFolders.getById() you can get one of the users Contact Folders in Outlook import { graph } from \"@pnp/graph\" ; const contactFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( 'folderId' ); const contactFolder = await graph . me . contactFolders . getById ( 'folderId' );","title":"Get Contact Folder by Id"},{"location":"graph/docs/contacts/#delete-a-contact-folder","text":"Using the delete you can remove one of the users Contact Folders in Outlook import { graph } from \"@pnp/graph\" ; const delContactFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( 'folderId' ). delete (); const delContactFolder = await graph . me . contactFolders . getById ( 'folderId' ). delete ();","title":"Delete a Contact Folder"},{"location":"graph/docs/contacts/#update-a-contact-folder","text":"Using the update you can update one of the users Contact Folders in Outlook import { graph } from \"@pnp/graph\" ; const updContactFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( 'userId' ). update ({ displayName : \"value\" }); const updContactFolder = await graph . me . contactFolders . getById ( 'userId' ). update ({ displayName : \"value\" });","title":"Update a Contact Folder"},{"location":"graph/docs/contacts/#get-all-of-the-contacts-from-the-contact-folder","text":"Using the contacts() in the Contact Folder gets the users Contact from the folder. import { graph } from \"@pnp/graph\" ; const contactsInContactFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( 'folderId' ). contacts . get (); const contactsInContactFolder = await graph . me . contactFolders . getById ( 'folderId' ). contacts . get ();","title":"Get all of the Contacts from the Contact Folder"},{"location":"graph/docs/contacts/#get-child-folders-of-the-contact-folder","text":"Using the childFolders() you can get the Child Folders of the current Contact Folder from Outlook import { graph } from \"@pnp/graph\" ; const childFolders = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( '' ). childFolders . get (); const childFolders = await graph . me . contactFolders . getById ( '' ). childFolders . get ();","title":"Get Child Folders of the Contact Folder"},{"location":"graph/docs/contacts/#add-a-new-child-folder","text":"Using the childFolders.add() you can a add Child Folder in a Contact Folder import { graph } from \"@pnp/graph\" ; const addedChildFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( '' ). childFolders . add ( 'displayName' , '' ); const addedChildFolder = await graph . me . contactFolders . getById ( '' ). childFolders . add ( 'displayName' , '' );","title":"Add a new Child Folder"},{"location":"graph/docs/contacts/#get-child-folder-by-id","text":"Using the childFolders.getById() you can get one of the users Child Folders in Outlook import { graph } from \"@pnp/graph\" ; const childFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( '' ). childFolders . getById ( 'folderId' ); const childFolder = await graph . me . contactFolders . getById ( '' ). childFolders . getById ( 'folderId' );","title":"Get Child Folder by Id"},{"location":"graph/docs/contacts/#add-contact-in-child-folder-of-contact-folder","text":"Using contacts.add in the Child Folder of a Contact Folder, adds a new Contact to that folder import { graph } from \"@pnp/graph\" ; const addedContact = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). contactFolders . getById ( '' ). childFolders . getById ( 'folderId' ). contacts . add ( 'Pavel' , 'Bansky' , [ < EmailAddress > { address : 'pavelb@fabrikam.onmicrosoft.com' , name : 'Pavel Bansky' }], [ '+1 732 555 0102' ]); const addedContact = await graph . me . contactFolders . getById ( '' ). childFolders . getById ( 'folderId' ). contacts . add ( 'Pavel' , 'Bansky' , [ < EmailAddress > { address : 'pavelb@fabrikam.onmicrosoft.com' , name : 'Pavel Bansky' }], [ '+1 732 555 0102' ]);","title":"Add Contact in Child Folder of Contact Folder"},{"location":"graph/docs/directoryobjects/","text":"@pnp/graph/directoryObjects \u00b6 The groups and directory roles for the user \u00b6 import { graph } from \"@pnp/graph\" ; const memberOf = await graph . users . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). memberOf . get (); const memberOf = await graph . me . memberOf . get (); Return all the groups the user, group or directoryObject is a member of \u00b6 import { graph } from \"@pnp/graph\" ; const memberGroups = await graph . users . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). getMemberGroups (); const memberGroups = await graph . me . getMemberGroups (); const memberGroups = await graph . groups . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). getMemberGroups (); const memberGroups = await graph . directoryObjects . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). getMemberGroups (); Returns all the groups, administrative units and directory roles that a user, group, or directory object is a member of. \u00b6 import { graph } from \"@pnp/graph\" ; const memberObjects = await graph . users . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). getMemberObjects (); const memberObjects = await graph . me . getMemberObjects (); const memberObjects = await graph . groups . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). getMemberObjects (); const memberObjects = await graph . directoryObjects . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). getMemberObjects (); Check for membership in a specified list of groups \u00b6 And returns from that list those groups of which the specified user, group, or directory object is a member import { graph } from \"@pnp/graph\" ; const checkedMembers = await graph . users . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). checkMemberGroups ([ \"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\" , \"2001bb09-1d46-40a6-8176-7bb867fb75aa\" ]); const checkedMembers = await graph . me . checkMemberGroups ([ \"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\" , \"2001bb09-1d46-40a6-8176-7bb867fb75aa\" ]); const checkedMembers = await graph . groups . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). checkMemberGroups ([ \"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\" , \"2001bb09-1d46-40a6-8176-7bb867fb75aa\" ]); const checkedMembers = await graph . directoryObjects . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). checkMemberGroups ([ \"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\" , \"2001bb09-1d46-40a6-8176-7bb867fb75aa\" ]); Get directoryObject by Id \u00b6 import { graph } from \"@pnp/graph\" ; const dirObject = await graph . directoryObjects . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). get (); Delete directoryObject \u00b6 import { graph } from \"@pnp/graph\" ; const deleted = await graph . directoryObjects . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). delete ()","title":"directory objects"},{"location":"graph/docs/directoryobjects/#pnpgraphdirectoryobjects","text":"","title":"@pnp/graph/directoryObjects"},{"location":"graph/docs/directoryobjects/#the-groups-and-directory-roles-for-the-user","text":"import { graph } from \"@pnp/graph\" ; const memberOf = await graph . users . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). memberOf . get (); const memberOf = await graph . me . memberOf . get ();","title":"The groups and directory roles for the user"},{"location":"graph/docs/directoryobjects/#return-all-the-groups-the-user-group-or-directoryobject-is-a-member-of","text":"import { graph } from \"@pnp/graph\" ; const memberGroups = await graph . users . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). getMemberGroups (); const memberGroups = await graph . me . getMemberGroups (); const memberGroups = await graph . groups . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). getMemberGroups (); const memberGroups = await graph . directoryObjects . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). getMemberGroups ();","title":"Return all the groups the user, group or directoryObject is a member of"},{"location":"graph/docs/directoryobjects/#returns-all-the-groups-administrative-units-and-directory-roles-that-a-user-group-or-directory-object-is-a-member-of","text":"import { graph } from \"@pnp/graph\" ; const memberObjects = await graph . users . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). getMemberObjects (); const memberObjects = await graph . me . getMemberObjects (); const memberObjects = await graph . groups . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). getMemberObjects (); const memberObjects = await graph . directoryObjects . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). getMemberObjects ();","title":"Returns all the groups, administrative units and directory roles that a user, group, or directory object is a member of."},{"location":"graph/docs/directoryobjects/#check-for-membership-in-a-specified-list-of-groups","text":"And returns from that list those groups of which the specified user, group, or directory object is a member import { graph } from \"@pnp/graph\" ; const checkedMembers = await graph . users . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). checkMemberGroups ([ \"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\" , \"2001bb09-1d46-40a6-8176-7bb867fb75aa\" ]); const checkedMembers = await graph . me . checkMemberGroups ([ \"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\" , \"2001bb09-1d46-40a6-8176-7bb867fb75aa\" ]); const checkedMembers = await graph . groups . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). checkMemberGroups ([ \"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\" , \"2001bb09-1d46-40a6-8176-7bb867fb75aa\" ]); const checkedMembers = await graph . directoryObjects . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). checkMemberGroups ([ \"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\" , \"2001bb09-1d46-40a6-8176-7bb867fb75aa\" ]);","title":"Check for membership in a specified list of groups"},{"location":"graph/docs/directoryobjects/#get-directoryobject-by-id","text":"import { graph } from \"@pnp/graph\" ; const dirObject = await graph . directoryObjects . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). get ();","title":"Get directoryObject by Id"},{"location":"graph/docs/directoryobjects/#delete-directoryobject","text":"import { graph } from \"@pnp/graph\" ; const deleted = await graph . directoryObjects . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). delete ()","title":"Delete directoryObject"},{"location":"graph/docs/insights/","text":"@pnp/graph/insights \u00b6 Insights are relationships calculated using advanced analytics and machine learning techniques. You can, for example, identify OneDrive documents trending around users. Get the trending documents \u00b6 Using the trending() returns documents from OneDrive and from SharePoint sites trending around a user. import { graph } from \"@pnp/graph\" ; const trending = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). insights . trending . get (); const trending = await graph . me . insights . trending . get (); Get the used documents \u00b6 Using the used() returns documents viewed and modified by a user. Includes documents the user used in OneDrive for Business, SharePoint, opened as email attachments, and as link attachments from sources like Box, DropBox and Google Drive. import { graph } from \"@pnp/graph\" ; const used = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). insights . used . get (); const used = await graph . me . insights . used . get (); Get the shared documents \u00b6 Using the shared() returns documents shared with a user. Documents can be shared as email attachments or as OneDrive for Business links sent in emails. import { graph } from \"@pnp/graph\" ; const shared = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). insights . shared . get (); const shared = await graph . me . insights . shared . get ();","title":"@pnp/graph/insights"},{"location":"graph/docs/insights/#pnpgraphinsights","text":"Insights are relationships calculated using advanced analytics and machine learning techniques. You can, for example, identify OneDrive documents trending around users.","title":"@pnp/graph/insights"},{"location":"graph/docs/insights/#get-the-trending-documents","text":"Using the trending() returns documents from OneDrive and from SharePoint sites trending around a user. import { graph } from \"@pnp/graph\" ; const trending = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). insights . trending . get (); const trending = await graph . me . insights . trending . get ();","title":"Get the trending documents"},{"location":"graph/docs/insights/#get-the-used-documents","text":"Using the used() returns documents viewed and modified by a user. Includes documents the user used in OneDrive for Business, SharePoint, opened as email attachments, and as link attachments from sources like Box, DropBox and Google Drive. import { graph } from \"@pnp/graph\" ; const used = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). insights . used . get (); const used = await graph . me . insights . used . get ();","title":"Get the used documents"},{"location":"graph/docs/insights/#get-the-shared-documents","text":"Using the shared() returns documents shared with a user. Documents can be shared as email attachments or as OneDrive for Business links sent in emails. import { graph } from \"@pnp/graph\" ; const shared = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). insights . shared . get (); const shared = await graph . me . insights . shared . get ();","title":"Get the shared documents"},{"location":"graph/docs/invitations/","text":"@pnp/graph/invitations \u00b6 The ability invite an external user via the invitation manager Create Invitation \u00b6 Using the invitations.create() you can create an Invitation. We need the email address of the user being invited and the URL user should be redirected to once the invitation is redeemed (redirect URL). import { graph } from \"@pnp/graph\" ; const invitationResult = await graph . invitations . create ( 'external.user@emailadress.com' , 'https://tenant.sharepoint.com/sites/redirecturi' );","title":"invitations"},{"location":"graph/docs/invitations/#pnpgraphinvitations","text":"The ability invite an external user via the invitation manager","title":"@pnp/graph/invitations"},{"location":"graph/docs/invitations/#create-invitation","text":"Using the invitations.create() you can create an Invitation. We need the email address of the user being invited and the URL user should be redirected to once the invitation is redeemed (redirect URL). import { graph } from \"@pnp/graph\" ; const invitationResult = await graph . invitations . create ( 'external.user@emailadress.com' , 'https://tenant.sharepoint.com/sites/redirecturi' );","title":"Create Invitation"},{"location":"graph/docs/onedrive/","text":"@pnp/graph/onedrive \u00b6 The ability to manage drives and drive items in Onedrive is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described you can manage drives and drive items in Onedrive. Get the default drive \u00b6 Using the drive() you can get the default drive from Onedrive import { graph } from \"@pnp/graph\" ; const drives = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . get (); const drives = await graph . me . drives . get (); Get all of the drives \u00b6 Using the drives() you can get the users available drives from Onedrive import { graph } from \"@pnp/graph\" ; const drives = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . get (); const drives = await graph . me . drives . get (); Get drive by Id \u00b6 Using the drives.getById() you can get one of the available drives in Outlook import { graph } from \"@pnp/graph\" ; const drive = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ); const drive = await graph . me . drives . getById ( 'driveId' ); Get the associated list of a drive \u00b6 Using the list() you get the associated list import { graph } from \"@pnp/graph\" ; const list = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). list . get (); const list = await graph . me . drives . getById ( 'driveId' ). list . get (); Get the recent files \u00b6 Using the recent() you get the recent files import { graph } from \"@pnp/graph\" ; const files = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). recent . get (); const files = await graph . me . drives . getById ( 'driveId' ). recent . get (); Get the files shared with me \u00b6 Using the sharedWithMe() you get the files shared with the user import { graph } from \"@pnp/graph\" ; const shared = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). sharedWithMe . get (); const shared = await graph . me . drives . getById ( 'driveId' ). sharedWithMe . get (); Get the Root folder \u00b6 Using the root() you get the root folder import { graph } from \"@pnp/graph\" ; const root = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). root . get (); const root = await graph . me . drives . getById ( 'driveId' ). root . get (); Get the Children \u00b6 Using the children() you get the children import { graph } from \"@pnp/graph\" ; const rootChildren = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). root . children . get (); const rootChildren = await graph . me . drives . getById ( 'driveId' ). root . children . get (); const itemChildren = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). items . getById ( 'itemId' ). children . get (); const itemChildren = await graph . me . drives . getById ( 'driveId' ). root . items . getById ( 'itemId' ). children . get (); Add folder or item \u00b6 Using the add you can add a folder or an item import { graph } from \"@pnp/graph\" ; import { DriveItem as IDriveItem } from \"@microsoft/microsoft-graph-types\" ; const addFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). root . children . add ( 'New Folder' , < IDriveItem > { folder : {}}); const addFolder = await graph . me . drives . getById ( 'driveId' ). root . children . add ( 'New Folder' , < IDriveItem > { folder : {}}); Search items \u00b6 Using the search() you can search for items, and optionally select properties import { graph } from \"@pnp/graph\" ; const search = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ) root . search ( 'queryText' ). get (); const search = await graph . me . drives . getById ( 'driveId' ) root . search ( 'queryText' ). get (); Get specific item in drive \u00b6 Using the items.getById() you can get a specific item from the current drive import { graph } from \"@pnp/graph\" ; const item = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). items . getById ( 'itemId' ); const item = await graph . me . drives . getById ( 'driveId' ). items . getById ( 'itemId' ); Get thumbnails \u00b6 Using the thumbnails() you get the thumbnails import { graph } from \"@pnp/graph\" ; const thumbs = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). items . getById ( 'itemId' ). thumbnails . get (); const thumbs = await graph . me . drives . getById ( 'driveId' ). items . getById ( 'itemId' ). thumbnails . get (); Delete drive item \u00b6 Using the delete() you delete the current item import { graph } from \"@pnp/graph\" ; const thumbs = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). items . getById ( 'itemId' ). delete (); const thumbs = await graph . me . drives . getById ( 'driveId' ). items . getById ( 'itemId' ). delete (); Update drive item \u00b6 Using the update() you update the current item import { graph } from \"@pnp/graph\" ; const update = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). items . getById ( 'itemId' ). update ({ name : \"New Name\" }); const update = await graph . me . drives . getById ( 'driveId' ). items . getById ( 'itemId' ). update ({ name : \"New Name\" }); Move drive item \u00b6 Using the move() you move the current item, and optionally update it import { graph } from \"@pnp/graph\" ; // Requires a parentReference to the new folder location const move = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). items . getById ( 'itemId' ). move ({ parentReference : { id : 'itemId' }}, { name : \"New Name\" }); const move = await graph . me . drives . getById ( 'driveId' ). items . getById ( 'itemId' ). move ({ parentReference : { id : 'itemId' }}, { name : \"New Name\" });","title":"onedrive"},{"location":"graph/docs/onedrive/#pnpgraphonedrive","text":"The ability to manage drives and drive items in Onedrive is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described you can manage drives and drive items in Onedrive.","title":"@pnp/graph/onedrive"},{"location":"graph/docs/onedrive/#get-the-default-drive","text":"Using the drive() you can get the default drive from Onedrive import { graph } from \"@pnp/graph\" ; const drives = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . get (); const drives = await graph . me . drives . get ();","title":"Get the default drive"},{"location":"graph/docs/onedrive/#get-all-of-the-drives","text":"Using the drives() you can get the users available drives from Onedrive import { graph } from \"@pnp/graph\" ; const drives = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . get (); const drives = await graph . me . drives . get ();","title":"Get all of the drives"},{"location":"graph/docs/onedrive/#get-drive-by-id","text":"Using the drives.getById() you can get one of the available drives in Outlook import { graph } from \"@pnp/graph\" ; const drive = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ); const drive = await graph . me . drives . getById ( 'driveId' );","title":"Get drive by Id"},{"location":"graph/docs/onedrive/#get-the-associated-list-of-a-drive","text":"Using the list() you get the associated list import { graph } from \"@pnp/graph\" ; const list = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). list . get (); const list = await graph . me . drives . getById ( 'driveId' ). list . get ();","title":"Get the associated list of a drive"},{"location":"graph/docs/onedrive/#get-the-recent-files","text":"Using the recent() you get the recent files import { graph } from \"@pnp/graph\" ; const files = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). recent . get (); const files = await graph . me . drives . getById ( 'driveId' ). recent . get ();","title":"Get the recent files"},{"location":"graph/docs/onedrive/#get-the-files-shared-with-me","text":"Using the sharedWithMe() you get the files shared with the user import { graph } from \"@pnp/graph\" ; const shared = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). sharedWithMe . get (); const shared = await graph . me . drives . getById ( 'driveId' ). sharedWithMe . get ();","title":"Get the files shared with me"},{"location":"graph/docs/onedrive/#get-the-root-folder","text":"Using the root() you get the root folder import { graph } from \"@pnp/graph\" ; const root = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). root . get (); const root = await graph . me . drives . getById ( 'driveId' ). root . get ();","title":"Get the Root folder"},{"location":"graph/docs/onedrive/#get-the-children","text":"Using the children() you get the children import { graph } from \"@pnp/graph\" ; const rootChildren = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). root . children . get (); const rootChildren = await graph . me . drives . getById ( 'driveId' ). root . children . get (); const itemChildren = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). items . getById ( 'itemId' ). children . get (); const itemChildren = await graph . me . drives . getById ( 'driveId' ). root . items . getById ( 'itemId' ). children . get ();","title":"Get the Children"},{"location":"graph/docs/onedrive/#add-folder-or-item","text":"Using the add you can add a folder or an item import { graph } from \"@pnp/graph\" ; import { DriveItem as IDriveItem } from \"@microsoft/microsoft-graph-types\" ; const addFolder = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). root . children . add ( 'New Folder' , < IDriveItem > { folder : {}}); const addFolder = await graph . me . drives . getById ( 'driveId' ). root . children . add ( 'New Folder' , < IDriveItem > { folder : {}});","title":"Add folder or item"},{"location":"graph/docs/onedrive/#search-items","text":"Using the search() you can search for items, and optionally select properties import { graph } from \"@pnp/graph\" ; const search = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ) root . search ( 'queryText' ). get (); const search = await graph . me . drives . getById ( 'driveId' ) root . search ( 'queryText' ). get ();","title":"Search items"},{"location":"graph/docs/onedrive/#get-specific-item-in-drive","text":"Using the items.getById() you can get a specific item from the current drive import { graph } from \"@pnp/graph\" ; const item = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). items . getById ( 'itemId' ); const item = await graph . me . drives . getById ( 'driveId' ). items . getById ( 'itemId' );","title":"Get specific item in drive"},{"location":"graph/docs/onedrive/#get-thumbnails","text":"Using the thumbnails() you get the thumbnails import { graph } from \"@pnp/graph\" ; const thumbs = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). items . getById ( 'itemId' ). thumbnails . get (); const thumbs = await graph . me . drives . getById ( 'driveId' ). items . getById ( 'itemId' ). thumbnails . get ();","title":"Get thumbnails"},{"location":"graph/docs/onedrive/#delete-drive-item","text":"Using the delete() you delete the current item import { graph } from \"@pnp/graph\" ; const thumbs = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). items . getById ( 'itemId' ). delete (); const thumbs = await graph . me . drives . getById ( 'driveId' ). items . getById ( 'itemId' ). delete ();","title":"Delete drive item"},{"location":"graph/docs/onedrive/#update-drive-item","text":"Using the update() you update the current item import { graph } from \"@pnp/graph\" ; const update = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). items . getById ( 'itemId' ). update ({ name : \"New Name\" }); const update = await graph . me . drives . getById ( 'driveId' ). items . getById ( 'itemId' ). update ({ name : \"New Name\" });","title":"Update drive item"},{"location":"graph/docs/onedrive/#move-drive-item","text":"Using the move() you move the current item, and optionally update it import { graph } from \"@pnp/graph\" ; // Requires a parentReference to the new folder location const move = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). drives . getById ( 'driveId' ). items . getById ( 'itemId' ). move ({ parentReference : { id : 'itemId' }}, { name : \"New Name\" }); const move = await graph . me . drives . getById ( 'driveId' ). items . getById ( 'itemId' ). move ({ parentReference : { id : 'itemId' }}, { name : \"New Name\" });","title":"Move drive item"},{"location":"graph/docs/people/","text":"@pnp/graph/people \u00b6 The ability to retrieve a list of person objects ordered by their relevance to the user, which is determined by the user's communication and collaboration patterns, and business relationships. Get all of the people \u00b6 Using the people() you can retrieve a list of person objects ordered by their relevance to the user. import { graph } from \"@pnp/graph\" ; const people = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). people . get (); const people = await graph . me . people . get ();","title":"@pnp/graph/people"},{"location":"graph/docs/people/#pnpgraphpeople","text":"The ability to retrieve a list of person objects ordered by their relevance to the user, which is determined by the user's communication and collaboration patterns, and business relationships.","title":"@pnp/graph/people"},{"location":"graph/docs/people/#get-all-of-the-people","text":"Using the people() you can retrieve a list of person objects ordered by their relevance to the user. import { graph } from \"@pnp/graph\" ; const people = await graph . users . getById ( 'user@tenant.onmicrosoft.com' ). people . get (); const people = await graph . me . people . get ();","title":"Get all of the people"},{"location":"graph/docs/planner/","text":"@pnp/graph/planner \u00b6 The ability to manage plans and tasks in Planner is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described you can add, update and delete items in Planner. Get Plans by Id \u00b6 Using the planner.plans.getById() you can get a specific Plan. Planner.plans is not an available endpoint, you need to get a specific Plan. import { graph } from \"@pnp/graph\" ; const plan = await graph . planner . plans . getById ( 'planId' ); Add new Plan \u00b6 Using the planner.plans.add() you can create a new Plan. import { graph } from \"@pnp/graph\" ; const newPlan = await graph . planner . plans . add ( 'groupObjectId' , 'title' ); Get Tasks in Plan \u00b6 Using the tasks() you can get the Tasks in a Plan. import { graph } from \"@pnp/graph\" ; const planTasks = await graph . planner . plans . getById ( 'planId' ). tasks . get (); Get Buckets in Plan \u00b6 Using the buckets() you can get the Buckets in a Plan. import { graph } from \"@pnp/graph\" ; const planBuckets = await graph . planner . plans . getById ( 'planId' ). buckets . get (); Get Details in Plan \u00b6 Using the details() you can get the details in a Plan. import { graph } from \"@pnp/graph\" ; const planDetails = await graph . planner . plans . getById ( 'planId' ). details . get (); Delete Plan \u00b6 Using the delete() you can get delete a Plan. import { graph } from \"@pnp/graph\" ; const delPlan = await graph . planner . plans . getById ( 'planId' ). delete (); Update Plan \u00b6 Using the update() you can get update a Plan. import { graph } from \"@pnp/graph\" ; const updPlan = await graph . planner . plans . getById ( 'planId' ). update ({ title : 'New Title' }); Get Task by Id \u00b6 Using the planner.tasks.getById() you can get a specific Task. Planner.tasks is not an available endpoint, you need to get a specific Task. import { graph } from \"@pnp/graph\" ; const task = await graph . planner . tasks . getById ( 'taskId' ); Add new Task \u00b6 Using the planner.tasks.add() you can create a new Task. import { graph } from \"@pnp/graph\" ; const newTask = await graph . planner . tasks . add ( 'planId' , 'title' ); Get Details in Task \u00b6 Using the details() you can get the details in a Task. import { graph } from \"@pnp/graph\" ; const taskDetails = await graph . planner . tasks . getById ( 'taskId' ). details . get (); Delete Task \u00b6 Using the delete() you can get delete a Task. import { graph } from \"@pnp/graph\" ; const delTask = await graph . planner . tasks . getById ( 'taskId' ). delete (); Update Task \u00b6 Using the update() you can get update a Task. import { graph } from \"@pnp/graph\" ; const updTask = await graph . planner . tasks . getById ( 'taskId' ). update ({ properties }); Get Buckets by Id \u00b6 Using the planner.buckets.getById() you can get a specific Bucket. planner.buckets is not an available endpoint, you need to get a specific Bucket. import { graph } from \"@pnp/graph\" ; const bucket = await graph . planner . buckets . getById ( 'bucketId' ); Add new Bucket \u00b6 Using the planner.buckets.add() you can create a new Bucket. import { graph } from \"@pnp/graph\" ; const newBucket = await graph . planner . buckets . add ( 'name' , 'planId' ); Update Bucket \u00b6 Using the update() you can get update a Bucket. import { graph } from \"@pnp/graph\" ; const updBucket = await graph . planner . buckets . getById ( 'bucketId' ). update ({ name : \"Name\" }); Delete Bucket \u00b6 Using the delete() you can get delete a Bucket. import { graph } from \"@pnp/graph\" ; const delBucket = await graph . planner . buckets . getById ( 'bucketId' ). delete (); Get Bucket Tasks \u00b6 Using the tasks() you can get Tasks in a Bucket. import { graph } from \"@pnp/graph\" ; const bucketTasks = await graph . planner . buckets . getById ( 'bucketId' ). tasks . get ();","title":"planner"},{"location":"graph/docs/planner/#pnpgraphplanner","text":"The ability to manage plans and tasks in Planner is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described you can add, update and delete items in Planner.","title":"@pnp/graph/planner"},{"location":"graph/docs/planner/#get-plans-by-id","text":"Using the planner.plans.getById() you can get a specific Plan. Planner.plans is not an available endpoint, you need to get a specific Plan. import { graph } from \"@pnp/graph\" ; const plan = await graph . planner . plans . getById ( 'planId' );","title":"Get Plans by Id"},{"location":"graph/docs/planner/#add-new-plan","text":"Using the planner.plans.add() you can create a new Plan. import { graph } from \"@pnp/graph\" ; const newPlan = await graph . planner . plans . add ( 'groupObjectId' , 'title' );","title":"Add new Plan"},{"location":"graph/docs/planner/#get-tasks-in-plan","text":"Using the tasks() you can get the Tasks in a Plan. import { graph } from \"@pnp/graph\" ; const planTasks = await graph . planner . plans . getById ( 'planId' ). tasks . get ();","title":"Get Tasks in Plan"},{"location":"graph/docs/planner/#get-buckets-in-plan","text":"Using the buckets() you can get the Buckets in a Plan. import { graph } from \"@pnp/graph\" ; const planBuckets = await graph . planner . plans . getById ( 'planId' ). buckets . get ();","title":"Get Buckets in Plan"},{"location":"graph/docs/planner/#get-details-in-plan","text":"Using the details() you can get the details in a Plan. import { graph } from \"@pnp/graph\" ; const planDetails = await graph . planner . plans . getById ( 'planId' ). details . get ();","title":"Get Details in Plan"},{"location":"graph/docs/planner/#delete-plan","text":"Using the delete() you can get delete a Plan. import { graph } from \"@pnp/graph\" ; const delPlan = await graph . planner . plans . getById ( 'planId' ). delete ();","title":"Delete Plan"},{"location":"graph/docs/planner/#update-plan","text":"Using the update() you can get update a Plan. import { graph } from \"@pnp/graph\" ; const updPlan = await graph . planner . plans . getById ( 'planId' ). update ({ title : 'New Title' });","title":"Update Plan"},{"location":"graph/docs/planner/#get-task-by-id","text":"Using the planner.tasks.getById() you can get a specific Task. Planner.tasks is not an available endpoint, you need to get a specific Task. import { graph } from \"@pnp/graph\" ; const task = await graph . planner . tasks . getById ( 'taskId' );","title":"Get Task by Id"},{"location":"graph/docs/planner/#add-new-task","text":"Using the planner.tasks.add() you can create a new Task. import { graph } from \"@pnp/graph\" ; const newTask = await graph . planner . tasks . add ( 'planId' , 'title' );","title":"Add new Task"},{"location":"graph/docs/planner/#get-details-in-task","text":"Using the details() you can get the details in a Task. import { graph } from \"@pnp/graph\" ; const taskDetails = await graph . planner . tasks . getById ( 'taskId' ). details . get ();","title":"Get Details in Task"},{"location":"graph/docs/planner/#delete-task","text":"Using the delete() you can get delete a Task. import { graph } from \"@pnp/graph\" ; const delTask = await graph . planner . tasks . getById ( 'taskId' ). delete ();","title":"Delete Task"},{"location":"graph/docs/planner/#update-task","text":"Using the update() you can get update a Task. import { graph } from \"@pnp/graph\" ; const updTask = await graph . planner . tasks . getById ( 'taskId' ). update ({ properties });","title":"Update Task"},{"location":"graph/docs/planner/#get-buckets-by-id","text":"Using the planner.buckets.getById() you can get a specific Bucket. planner.buckets is not an available endpoint, you need to get a specific Bucket. import { graph } from \"@pnp/graph\" ; const bucket = await graph . planner . buckets . getById ( 'bucketId' );","title":"Get Buckets by Id"},{"location":"graph/docs/planner/#add-new-bucket","text":"Using the planner.buckets.add() you can create a new Bucket. import { graph } from \"@pnp/graph\" ; const newBucket = await graph . planner . buckets . add ( 'name' , 'planId' );","title":"Add new Bucket"},{"location":"graph/docs/planner/#update-bucket","text":"Using the update() you can get update a Bucket. import { graph } from \"@pnp/graph\" ; const updBucket = await graph . planner . buckets . getById ( 'bucketId' ). update ({ name : \"Name\" });","title":"Update Bucket"},{"location":"graph/docs/planner/#delete-bucket","text":"Using the delete() you can get delete a Bucket. import { graph } from \"@pnp/graph\" ; const delBucket = await graph . planner . buckets . getById ( 'bucketId' ). delete ();","title":"Delete Bucket"},{"location":"graph/docs/planner/#get-bucket-tasks","text":"Using the tasks() you can get Tasks in a Bucket. import { graph } from \"@pnp/graph\" ; const bucketTasks = await graph . planner . buckets . getById ( 'bucketId' ). tasks . get ();","title":"Get Bucket Tasks"},{"location":"graph/docs/security/","text":"@pnp/graph/security \u00b6 The Microsoft Graph Security API can be used as a federated security aggregation service to submit queries to all onboarded security providers to get aggregated responses. Get all Alerts \u00b6 Using the alerts() to retrieve a list of Alert objects import { graph } from \"@pnp/graph\" ; const alerts = await graph . security . alerts . get (); Get an Alert by Id \u00b6 Using the alerts.getById() to retrieve a specific Alert object import { graph } from \"@pnp/graph\" ; const alert = await graph . security . alerts . getById ( 'alertId' ). get (); Update an Alert \u00b6 Using the alerts.getById().update() to retrieve a specific Alert object import { graph } from \"@pnp/graph\" ; const updAlert = await graph . security . alerts . getById ( 'alertId' ). update ({ status : 'Status' });","title":"@pnp/graph/security"},{"location":"graph/docs/security/#pnpgraphsecurity","text":"The Microsoft Graph Security API can be used as a federated security aggregation service to submit queries to all onboarded security providers to get aggregated responses.","title":"@pnp/graph/security"},{"location":"graph/docs/security/#get-all-alerts","text":"Using the alerts() to retrieve a list of Alert objects import { graph } from \"@pnp/graph\" ; const alerts = await graph . security . alerts . get ();","title":"Get all Alerts"},{"location":"graph/docs/security/#get-an-alert-by-id","text":"Using the alerts.getById() to retrieve a specific Alert object import { graph } from \"@pnp/graph\" ; const alert = await graph . security . alerts . getById ( 'alertId' ). get ();","title":"Get an Alert by Id"},{"location":"graph/docs/security/#update-an-alert","text":"Using the alerts.getById().update() to retrieve a specific Alert object import { graph } from \"@pnp/graph\" ; const updAlert = await graph . security . alerts . getById ( 'alertId' ). update ({ status : 'Status' });","title":"Update an Alert"},{"location":"graph/docs/sites/","text":"@pnp/graph/sites \u00b6 The ability to manage sites, lists and listitems in SharePoint is a capability introduced in version 1.3.0 of @pnp/graph. Get the Root Site \u00b6 Using the sites.root()() you can get the tenant root site import { graph } from \"@pnp/graph\" ; const tenantRootSite = await graph . sites . root . get () Get the Root Site by Id \u00b6 Using the sites.getById()() you can get the root site as well import { graph } from \"@pnp/graph\" ; const tenantRootSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). get () Access a Site by server-relative URL \u00b6 Using the sites.getById()() you can get a specific site. With the combination of the base URL and a relative URL. We are using an internal method for combining the URL in the right combination, with : ex: contoso.sharepoint.com:/sites/site1: Here are a few url combinations that works: import { graph } from \"@pnp/graph\" ; // No / in the URLs const siteByRelativeUrl = await graph . sites . getById ( 'contoso.sharepoint.com' , 'sites/site1' ). get () // Both trailing / in the base URL and starting / in the relative URL const siteByRelativeUrl = await graph . sites . getById ( 'contoso.sharepoint.com/' , '/sites/site1' ). get () // Both trailing / in the base URL and starting and trailing / in the relative URL const siteByRelativeUrl = await graph . sites . getById ( 'contoso.sharepoint.com/' , '/sites/site1/' ). get () Get the Sub Sites in a Site \u00b6 Using the sites()() you can get the sub sites of a site. As this is returned as Sites, you could use getById() for a specific site and use the operations. import { graph } from \"@pnp/graph\" ; const subsites = await graph . sites . getById ( 'contoso.sharepoint.com' ). sites . get (); Get Content Types \u00b6 Using the contentTypes()() you can get the Content Types from a Site or from a List import { graph } from \"@pnp/graph\" ; const contentTypesFromSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). contentTypes . get (); const contentTypesFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). contentTypes . get (); Get Specific Content Type \u00b6 Using the getById() you can get a specific Content Type from a Site or from a List import { graph } from \"@pnp/graph\" ; const contentTypeFromSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). contentTypes . getById ( 'contentTypeId' ). get (); const contentTypeFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). contentTypes . getById ( 'contentTypeId' ). get (); Get the Lists in a Site \u00b6 Using the lists() you can get the lists of a site. import { graph } from \"@pnp/graph\" ; const lists = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . get (); Get a specific List in a Site \u00b6 Using the lists.getById() you can get the lists of a site. import { graph } from \"@pnp/graph\" ; const list = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). get (); Create a Lists in a Site \u00b6 Using the lists.create() you can create a list in a site. import { graph } from \"@pnp/graph\" ; const newLists = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . create ( 'DisplayName' , { contentTypesEnabled : true , hidden : false , template : \"genericList\" }) Get the default drive \u00b6 Using the drive() you can get the default drive from a Site or a List import { graph } from \"@pnp/graph\" ; const drive = await graph . sites . getById ( 'contoso.sharepoint.com' ). drive . get (); const drive = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). drive . get (); Get all of the drives \u00b6 Using the drives() you can get the drives from the Site import { graph } from \"@pnp/graph\" ; const drives = await graph . sites . getById ( 'contoso.sharepoint.com' ). drives . get (); Get drive by Id \u00b6 Using the drives.getById() you can get one specific Drive. For more operations make sure to have a look in the onedrive documentation. import { graph } from \"@pnp/graph\" ; const drive = await raph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). drives . getById ( 'driveId' ). get (); Get Columns \u00b6 Using the columns() you can get the columns from a Site or from a List import { graph } from \"@pnp/graph\" ; const columnsFromSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). columns . get (); const columnsFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). columns . get (); Get Specific Column \u00b6 Using the columns.getById() you can get a specific column from a Site or from a List import { graph } from \"@pnp/graph\" ; const columnFromSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). columns . getById ( 'columnId' ). get (); const columnsFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). columns . getById ( 'columnId' ). get (); Get Column Links \u00b6 Using the column.columnLinks() you can get the column links for a specific column, from a Site or from a List import { graph } from \"@pnp/graph\" ; const columnLinksFromSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). columns . getById ( 'columnId' ). columnLinks . get (); const columnLinksFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). columns . getById ( 'columnId' ). columnLinks . get (); Get Column Link \u00b6 Using the column.columnLinks().getById() you can get a specific column link for a specific column, from a Site or from a List import { graph } from \"@pnp/graph\" ; const columnLinkFromSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). columns . getById ( 'columnId' ). columnLinks . getById ( 'columnLinkId' ). get (); const columnLinkFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). columns . getById ( 'columnId' ). columnLinks . getById ( 'columnLinkId' ). get (); Get Items \u00b6 Using the items() you can get the Items from a List import { graph } from \"@pnp/graph\" ; const itemsFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . get (); Get Specific Item \u00b6 Using the getById()() you can get a specific Item from a List import { graph } from \"@pnp/graph\" ; const itemFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . getById ( 'itemId' ). get (); Create Item \u00b6 Using the items.create() you can create an Item in a List. import { graph } from \"@pnp/graph\" ; const newItem = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . create ({ \"Title\" : \"Widget\" , \"Color\" : \"Purple\" , \"Weight\" : 32 }); Update Item \u00b6 Using the update() you can update an Item in a List. import { graph } from \"@pnp/graph\" ; const Item = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . getById ( 'itemId' ). update ({ { \"Color\" : \"Fuchsia\" } }) Delete Item \u00b6 Using the delete() you can delete an Item in a List. import { graph } from \"@pnp/graph\" ; const Item = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . getById ( 'itemId' ). delete () Get Fields from Item \u00b6 Using the fields() you can the Fields in an Item import { graph } from \"@pnp/graph\" ; const fieldsFromItem = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . getById ( 'itemId' ). fields . get (); Get Versions from Item \u00b6 Using the versions() you can the Versions of an Item import { graph } from \"@pnp/graph\" ; const versionsFromItem = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . getById ( 'itemId' ). versions . get (); Get Version from Item \u00b6 Using the versions.getById()() you can the Versions of an Item import { graph } from \"@pnp/graph\" ; const versionFromItem = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . getById ( 'itemId' ). versions . getById ( 'versionId' ). get ();","title":"@pnp/graph/sites"},{"location":"graph/docs/sites/#pnpgraphsites","text":"The ability to manage sites, lists and listitems in SharePoint is a capability introduced in version 1.3.0 of @pnp/graph.","title":"@pnp/graph/sites"},{"location":"graph/docs/sites/#get-the-root-site","text":"Using the sites.root()() you can get the tenant root site import { graph } from \"@pnp/graph\" ; const tenantRootSite = await graph . sites . root . get ()","title":"Get the Root Site"},{"location":"graph/docs/sites/#get-the-root-site-by-id","text":"Using the sites.getById()() you can get the root site as well import { graph } from \"@pnp/graph\" ; const tenantRootSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). get ()","title":"Get the Root Site by Id"},{"location":"graph/docs/sites/#access-a-site-by-server-relative-url","text":"Using the sites.getById()() you can get a specific site. With the combination of the base URL and a relative URL. We are using an internal method for combining the URL in the right combination, with : ex: contoso.sharepoint.com:/sites/site1: Here are a few url combinations that works: import { graph } from \"@pnp/graph\" ; // No / in the URLs const siteByRelativeUrl = await graph . sites . getById ( 'contoso.sharepoint.com' , 'sites/site1' ). get () // Both trailing / in the base URL and starting / in the relative URL const siteByRelativeUrl = await graph . sites . getById ( 'contoso.sharepoint.com/' , '/sites/site1' ). get () // Both trailing / in the base URL and starting and trailing / in the relative URL const siteByRelativeUrl = await graph . sites . getById ( 'contoso.sharepoint.com/' , '/sites/site1/' ). get ()","title":"Access a Site by server-relative URL"},{"location":"graph/docs/sites/#get-the-sub-sites-in-a-site","text":"Using the sites()() you can get the sub sites of a site. As this is returned as Sites, you could use getById() for a specific site and use the operations. import { graph } from \"@pnp/graph\" ; const subsites = await graph . sites . getById ( 'contoso.sharepoint.com' ). sites . get ();","title":"Get the Sub Sites in a Site"},{"location":"graph/docs/sites/#get-content-types","text":"Using the contentTypes()() you can get the Content Types from a Site or from a List import { graph } from \"@pnp/graph\" ; const contentTypesFromSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). contentTypes . get (); const contentTypesFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). contentTypes . get ();","title":"Get Content Types"},{"location":"graph/docs/sites/#get-specific-content-type","text":"Using the getById() you can get a specific Content Type from a Site or from a List import { graph } from \"@pnp/graph\" ; const contentTypeFromSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). contentTypes . getById ( 'contentTypeId' ). get (); const contentTypeFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). contentTypes . getById ( 'contentTypeId' ). get ();","title":"Get Specific Content Type"},{"location":"graph/docs/sites/#get-the-lists-in-a-site","text":"Using the lists() you can get the lists of a site. import { graph } from \"@pnp/graph\" ; const lists = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . get ();","title":"Get the Lists in a Site"},{"location":"graph/docs/sites/#get-a-specific-list-in-a-site","text":"Using the lists.getById() you can get the lists of a site. import { graph } from \"@pnp/graph\" ; const list = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). get ();","title":"Get a specific List in a Site"},{"location":"graph/docs/sites/#create-a-lists-in-a-site","text":"Using the lists.create() you can create a list in a site. import { graph } from \"@pnp/graph\" ; const newLists = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . create ( 'DisplayName' , { contentTypesEnabled : true , hidden : false , template : \"genericList\" })","title":"Create a Lists in a Site"},{"location":"graph/docs/sites/#get-the-default-drive","text":"Using the drive() you can get the default drive from a Site or a List import { graph } from \"@pnp/graph\" ; const drive = await graph . sites . getById ( 'contoso.sharepoint.com' ). drive . get (); const drive = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). drive . get ();","title":"Get the default drive"},{"location":"graph/docs/sites/#get-all-of-the-drives","text":"Using the drives() you can get the drives from the Site import { graph } from \"@pnp/graph\" ; const drives = await graph . sites . getById ( 'contoso.sharepoint.com' ). drives . get ();","title":"Get all of the drives"},{"location":"graph/docs/sites/#get-drive-by-id","text":"Using the drives.getById() you can get one specific Drive. For more operations make sure to have a look in the onedrive documentation. import { graph } from \"@pnp/graph\" ; const drive = await raph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). drives . getById ( 'driveId' ). get ();","title":"Get drive by Id"},{"location":"graph/docs/sites/#get-columns","text":"Using the columns() you can get the columns from a Site or from a List import { graph } from \"@pnp/graph\" ; const columnsFromSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). columns . get (); const columnsFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). columns . get ();","title":"Get Columns"},{"location":"graph/docs/sites/#get-specific-column","text":"Using the columns.getById() you can get a specific column from a Site or from a List import { graph } from \"@pnp/graph\" ; const columnFromSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). columns . getById ( 'columnId' ). get (); const columnsFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). columns . getById ( 'columnId' ). get ();","title":"Get Specific Column"},{"location":"graph/docs/sites/#get-column-links","text":"Using the column.columnLinks() you can get the column links for a specific column, from a Site or from a List import { graph } from \"@pnp/graph\" ; const columnLinksFromSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). columns . getById ( 'columnId' ). columnLinks . get (); const columnLinksFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). columns . getById ( 'columnId' ). columnLinks . get ();","title":"Get Column Links"},{"location":"graph/docs/sites/#get-column-link","text":"Using the column.columnLinks().getById() you can get a specific column link for a specific column, from a Site or from a List import { graph } from \"@pnp/graph\" ; const columnLinkFromSite = await graph . sites . getById ( 'contoso.sharepoint.com' ). columns . getById ( 'columnId' ). columnLinks . getById ( 'columnLinkId' ). get (); const columnLinkFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). columns . getById ( 'columnId' ). columnLinks . getById ( 'columnLinkId' ). get ();","title":"Get Column Link"},{"location":"graph/docs/sites/#get-items","text":"Using the items() you can get the Items from a List import { graph } from \"@pnp/graph\" ; const itemsFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . get ();","title":"Get Items"},{"location":"graph/docs/sites/#get-specific-item","text":"Using the getById()() you can get a specific Item from a List import { graph } from \"@pnp/graph\" ; const itemFromList = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . getById ( 'itemId' ). get ();","title":"Get Specific Item"},{"location":"graph/docs/sites/#create-item","text":"Using the items.create() you can create an Item in a List. import { graph } from \"@pnp/graph\" ; const newItem = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . create ({ \"Title\" : \"Widget\" , \"Color\" : \"Purple\" , \"Weight\" : 32 });","title":"Create Item"},{"location":"graph/docs/sites/#update-item","text":"Using the update() you can update an Item in a List. import { graph } from \"@pnp/graph\" ; const Item = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . getById ( 'itemId' ). update ({ { \"Color\" : \"Fuchsia\" } })","title":"Update Item"},{"location":"graph/docs/sites/#delete-item","text":"Using the delete() you can delete an Item in a List. import { graph } from \"@pnp/graph\" ; const Item = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . getById ( 'itemId' ). delete ()","title":"Delete Item"},{"location":"graph/docs/sites/#get-fields-from-item","text":"Using the fields() you can the Fields in an Item import { graph } from \"@pnp/graph\" ; const fieldsFromItem = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . getById ( 'itemId' ). fields . get ();","title":"Get Fields from Item"},{"location":"graph/docs/sites/#get-versions-from-item","text":"Using the versions() you can the Versions of an Item import { graph } from \"@pnp/graph\" ; const versionsFromItem = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . getById ( 'itemId' ). versions . get ();","title":"Get Versions from Item"},{"location":"graph/docs/sites/#get-version-from-item","text":"Using the versions.getById()() you can the Versions of an Item import { graph } from \"@pnp/graph\" ; const versionFromItem = await graph . sites . getById ( 'contoso.sharepoint.com' ). lists . getById ( 'listId' ). items . getById ( 'itemId' ). versions . getById ( 'versionId' ). get ();","title":"Get Version from Item"},{"location":"graph/docs/subscriptions/","text":"@pnp/graph/subscriptions \u00b6 The ability to manage subscriptions is a capability introduced in version 1.2.9 of @pnp/graph. A subscription allows a client app to receive notifications about changes to data in Microsoft Graph. Currently, subscriptions are enabled for the following resources: Mail, events, and contacts from Outlook. Conversations from Office Groups. Drive root items from OneDrive. Users and Groups from Azure Active Directory. * Alerts from the Microsoft Graph Security API. Get all of the Subscriptions \u00b6 Using the subscriptions(). If successful this method returns a 200 OK response code and a list of subscription objects in the response body. import { graph } from \"@pnp/graph\" ; const subscriptions = await graph . subscriptions . get (); Create a new Subscription \u00b6 Using the subscriptions.add(). Creating a subscription requires read scope to the resource. For example, to get notifications messages, your app needs the Mail.Read permission. To learn more about the scopes visit this url. import { graph } from \"@pnp/graph\" ; const addedSubscription = await graph . subscriptions . add ( \"created,updated\" , \"https://webhook.azurewebsites.net/api/send/myNotifyClient\" , \"me/mailFolders('Inbox')/messages\" , \"2019-11-20T18:23:45.9356913Z\" ); Get Subscription by Id \u00b6 Using the subscriptions.getById() you can get one of the subscriptions import { graph } from \"@pnp/graph\" ; const subscription = await graph . subscriptions . getById ( 'subscriptionId' ); Delete a Subscription \u00b6 Using the subscriptions.getById().delete() you can remove one of the Subscriptions import { graph } from \"@pnp/graph\" ; const delSubscription = await graph . subscription . getById ( 'subscriptionId' ). delete (); Update a Subscription \u00b6 Using the subscriptions.getById().update() you can update one of the Subscriptions import { graph } from \"@pnp/graph\" ; const updSubscription = await graph . subscriptions . getById ( 'subscriptionId' ). update ({ changeType : \"created,updated,deleted\" });","title":"subscriptions"},{"location":"graph/docs/subscriptions/#pnpgraphsubscriptions","text":"The ability to manage subscriptions is a capability introduced in version 1.2.9 of @pnp/graph. A subscription allows a client app to receive notifications about changes to data in Microsoft Graph. Currently, subscriptions are enabled for the following resources: Mail, events, and contacts from Outlook. Conversations from Office Groups. Drive root items from OneDrive. Users and Groups from Azure Active Directory. * Alerts from the Microsoft Graph Security API.","title":"@pnp/graph/subscriptions"},{"location":"graph/docs/subscriptions/#get-all-of-the-subscriptions","text":"Using the subscriptions(). If successful this method returns a 200 OK response code and a list of subscription objects in the response body. import { graph } from \"@pnp/graph\" ; const subscriptions = await graph . subscriptions . get ();","title":"Get all of the Subscriptions"},{"location":"graph/docs/subscriptions/#create-a-new-subscription","text":"Using the subscriptions.add(). Creating a subscription requires read scope to the resource. For example, to get notifications messages, your app needs the Mail.Read permission. To learn more about the scopes visit this url. import { graph } from \"@pnp/graph\" ; const addedSubscription = await graph . subscriptions . add ( \"created,updated\" , \"https://webhook.azurewebsites.net/api/send/myNotifyClient\" , \"me/mailFolders('Inbox')/messages\" , \"2019-11-20T18:23:45.9356913Z\" );","title":"Create a new Subscription"},{"location":"graph/docs/subscriptions/#get-subscription-by-id","text":"Using the subscriptions.getById() you can get one of the subscriptions import { graph } from \"@pnp/graph\" ; const subscription = await graph . subscriptions . getById ( 'subscriptionId' );","title":"Get Subscription by Id"},{"location":"graph/docs/subscriptions/#delete-a-subscription","text":"Using the subscriptions.getById().delete() you can remove one of the Subscriptions import { graph } from \"@pnp/graph\" ; const delSubscription = await graph . subscription . getById ( 'subscriptionId' ). delete ();","title":"Delete a Subscription"},{"location":"graph/docs/subscriptions/#update-a-subscription","text":"Using the subscriptions.getById().update() you can update one of the Subscriptions import { graph } from \"@pnp/graph\" ; const updSubscription = await graph . subscriptions . getById ( 'subscriptionId' ). update ({ changeType : \"created,updated,deleted\" });","title":"Update a Subscription"},{"location":"graph/docs/teams/","text":"@pnp/graph/teams \u00b6 The ability to manage Team is a capability introduced in the 1.2.7 of @pnp/graph. Through the methods described you can add, update and delete items in Teams. Teams the user is a member of \u00b6 import { graph } from \"@pnp/graph\" ; const joinedTeams = await graph . users . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). joinedTeams . get (); const myJoinedTeams = await graph . me . joinedTeams . get (); Get Teams by Id \u00b6 Using the teams.getById() you can get a specific Team. import { graph } from \"@pnp/graph\" ; const team = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). get (); Create new Group and Team \u00b6 When you create a new group and add a Team, the group needs to have an Owner. Or else we get an error. So the owner Id is important, and you could just get the users Ids from import { graph } from \"@pnp/graph\" ; const users = await graph . users . get (); Then create import { graph } from \"@pnp/graph\" ; const createdGroupTeam = await graph . teams . create ( 'Groupname' , 'mailNickname' , 'description' , 'OwnerId' ,{ \"memberSettings\" : { \"allowCreateUpdateChannels\" : true }, \"messagingSettings\" : { \"allowUserEditMessages\" : true , \"allowUserDeleteMessages\" : true }, \"funSettings\" : { \"allowGiphy\" : true , \"giphyContentRating\" : \"strict\" }}); Create a Team via a specific group \u00b6 Here we get the group via id and use createTeam import { graph } from \"@pnp/graph\" ; const createdTeam = await graph . groups . getById ( '679c8ff4-f07d-40de-b02b-60ec332472dd' ). createTeam ({ \"memberSettings\" : { \"allowCreateUpdateChannels\" : true }, \"messagingSettings\" : { \"allowUserEditMessages\" : true , \"allowUserDeleteMessages\" : true }, \"funSettings\" : { \"allowGiphy\" : true , \"giphyContentRating\" : \"strict\" }}); Archive a Team \u00b6 import { graph } from \"@pnp/graph\" ; const archived = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). archive (); Unarchive a Team \u00b6 import { graph } from \"@pnp/graph\" ; const archived = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). unarchive (); Clone a Team \u00b6 import { graph } from \"@pnp/graph\" ; const clonedTeam = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). cloneTeam ( 'Cloned' , 'mailNickname' , 'description' , 'apps,tabs,settings,channels,members' , 'public' ); Get all channels of a Team \u00b6 import { graph } from \"@pnp/graph\" ; const channels = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). channels . get (); Get channel by Id \u00b6 import { graph } from \"@pnp/graph\" ; const channel = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). channels . getById ( '19:65723d632b384ca89c81115c281428a3@thread.skype' ). get (); Create a new Channel \u00b6 import { graph } from \"@pnp/graph\" ; const newChannel = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). channels . create ( 'New Channel' , 'Description' ); Get installed Apps \u00b6 import { graph } from \"@pnp/graph\" ; const installedApps = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). installedApps . get (); Add an App \u00b6 import { graph } from \"@pnp/graph\" ; const addedApp = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). installedApps . add ( 'https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a' ); Remove an App \u00b6 import { graph } from \"@pnp/graph\" ; const removedApp = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). installedApps . remove (); Get Tabs from a Channel \u00b6 import { graph } from \"@pnp/graph\" ; const tabs = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). channels . getById ( '19:65723d632b384ca89c81115c281428a3@thread.skype' ). tabs . get (); Get Tab by Id \u00b6 import { graph } from \"@pnp/graph\" ; const tab = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). channels . getById ( '19:65723d632b384ca89c81115c281428a3@thread.skype' ). tabs . getById ( 'Id' ); Add a new Tab \u00b6 import { graph } from \"@pnp/graph\" ; const newTab = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). channels . getById ( '19:65723d632b384ca89c81115c281428a3@thread.skype' ). tabs . add ( 'Tab' , 'https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a' , < TabsConfiguration > {});","title":"teams"},{"location":"graph/docs/teams/#pnpgraphteams","text":"The ability to manage Team is a capability introduced in the 1.2.7 of @pnp/graph. Through the methods described you can add, update and delete items in Teams.","title":"@pnp/graph/teams"},{"location":"graph/docs/teams/#teams-the-user-is-a-member-of","text":"import { graph } from \"@pnp/graph\" ; const joinedTeams = await graph . users . getById ( '99dc1039-eb80-43b1-a09e-250d50a80b26' ). joinedTeams . get (); const myJoinedTeams = await graph . me . joinedTeams . get ();","title":"Teams the user is a member of"},{"location":"graph/docs/teams/#get-teams-by-id","text":"Using the teams.getById() you can get a specific Team. import { graph } from \"@pnp/graph\" ; const team = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). get ();","title":"Get Teams by Id"},{"location":"graph/docs/teams/#create-new-group-and-team","text":"When you create a new group and add a Team, the group needs to have an Owner. Or else we get an error. So the owner Id is important, and you could just get the users Ids from import { graph } from \"@pnp/graph\" ; const users = await graph . users . get (); Then create import { graph } from \"@pnp/graph\" ; const createdGroupTeam = await graph . teams . create ( 'Groupname' , 'mailNickname' , 'description' , 'OwnerId' ,{ \"memberSettings\" : { \"allowCreateUpdateChannels\" : true }, \"messagingSettings\" : { \"allowUserEditMessages\" : true , \"allowUserDeleteMessages\" : true }, \"funSettings\" : { \"allowGiphy\" : true , \"giphyContentRating\" : \"strict\" }});","title":"Create new Group and Team"},{"location":"graph/docs/teams/#create-a-team-via-a-specific-group","text":"Here we get the group via id and use createTeam import { graph } from \"@pnp/graph\" ; const createdTeam = await graph . groups . getById ( '679c8ff4-f07d-40de-b02b-60ec332472dd' ). createTeam ({ \"memberSettings\" : { \"allowCreateUpdateChannels\" : true }, \"messagingSettings\" : { \"allowUserEditMessages\" : true , \"allowUserDeleteMessages\" : true }, \"funSettings\" : { \"allowGiphy\" : true , \"giphyContentRating\" : \"strict\" }});","title":"Create a Team via a specific group"},{"location":"graph/docs/teams/#archive-a-team","text":"import { graph } from \"@pnp/graph\" ; const archived = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). archive ();","title":"Archive a Team"},{"location":"graph/docs/teams/#unarchive-a-team","text":"import { graph } from \"@pnp/graph\" ; const archived = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). unarchive ();","title":"Unarchive a Team"},{"location":"graph/docs/teams/#clone-a-team","text":"import { graph } from \"@pnp/graph\" ; const clonedTeam = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). cloneTeam ( 'Cloned' , 'mailNickname' , 'description' , 'apps,tabs,settings,channels,members' , 'public' );","title":"Clone a Team"},{"location":"graph/docs/teams/#get-all-channels-of-a-team","text":"import { graph } from \"@pnp/graph\" ; const channels = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). channels . get ();","title":"Get all channels of a Team"},{"location":"graph/docs/teams/#get-channel-by-id","text":"import { graph } from \"@pnp/graph\" ; const channel = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). channels . getById ( '19:65723d632b384ca89c81115c281428a3@thread.skype' ). get ();","title":"Get channel by Id"},{"location":"graph/docs/teams/#create-a-new-channel","text":"import { graph } from \"@pnp/graph\" ; const newChannel = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). channels . create ( 'New Channel' , 'Description' );","title":"Create a new Channel"},{"location":"graph/docs/teams/#get-installed-apps","text":"import { graph } from \"@pnp/graph\" ; const installedApps = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). installedApps . get ();","title":"Get installed Apps"},{"location":"graph/docs/teams/#add-an-app","text":"import { graph } from \"@pnp/graph\" ; const addedApp = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). installedApps . add ( 'https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a' );","title":"Add an App"},{"location":"graph/docs/teams/#remove-an-app","text":"import { graph } from \"@pnp/graph\" ; const removedApp = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). installedApps . remove ();","title":"Remove an App"},{"location":"graph/docs/teams/#get-tabs-from-a-channel","text":"import { graph } from \"@pnp/graph\" ; const tabs = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). channels . getById ( '19:65723d632b384ca89c81115c281428a3@thread.skype' ). tabs . get ();","title":"Get Tabs from a Channel"},{"location":"graph/docs/teams/#get-tab-by-id","text":"import { graph } from \"@pnp/graph\" ; const tab = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). channels . getById ( '19:65723d632b384ca89c81115c281428a3@thread.skype' ). tabs . getById ( 'Id' );","title":"Get Tab by Id"},{"location":"graph/docs/teams/#add-a-new-tab","text":"import { graph } from \"@pnp/graph\" ; const newTab = await graph . teams . getById ( '3531f3fb-f9ee-4f43-982a-6c90d8226528' ). channels . getById ( '19:65723d632b384ca89c81115c281428a3@thread.skype' ). tabs . add ( 'Tab' , 'https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a' , < TabsConfiguration > {});","title":"Add a new Tab"},{"location":"logging/docs/","text":"@pnp/logging \u00b6 The logging module provides light weight subscribable and extensiable logging framework which is used internally and available for use in your projects. This article outlines how to setup logging and use the various loggers. Getting Started \u00b6 Install the logging module, it has no other dependencies npm install @pnp/logging --save Understanding the Logging Framework \u00b6 The logging framework is based on the Logger class to which any number of listeners can be subscribed. Each of these listeners will receive each of the messages logged. Each listener must implement the LogListener interface, shown below. There is only one method to implement and it takes an instance of the LogEntry interface. /** * Interface that defines a log listener * */ export interface LogListener { /** * Any associated data that a given logging listener may choose to log or ignore * * @param entry The information to be logged */ log ( entry : LogEntry ) : void ; } /** * Interface that defines a log entry * */ export interface LogEntry { /** * The main message to be logged */ message : string ; /** * The level of information this message represents */ level : LogLevel ; /** * Any associated data that a given logging listener may choose to log or ignore */ data? : any ; } Log Levels \u00b6 export const enum LogLevel { Verbose = 0 , Info = 1 , Warning = 2 , Error = 3 , Off = 99 , } Writing to the Logger \u00b6 To write information to a logger you can use either write, writeJSON, or log. import { Logger , LogLevel } from \"@pnp/logging\" ; // write logs a simple string as the message value of the LogEntry Logger . write ( \"This is logging a simple string\" ); // optionally passing a level, default level is Verbose Logger . write ( \"This is logging a simple string\" , LogLevel . Error ); // this will convert the object to a string using JSON.stringify and set the message with the result Logger . writeJSON ({ name : \"value\" , name2 : \"value2\" }); // optionally passing a level, default level is Verbose Logger . writeJSON ({ name : \"value\" , name2 : \"value2\" }, LogLevel . Warn ); // specify the entire LogEntry interface using log Logger . log ({ data : { name : \"value\" , name2 : \"value2\" }, level : LogLevel.Warning , message : \"This is my message\" }); Log an error \u00b6 There exists a shortcut method to log an error to the Logger. This will log an entry to the subscribed loggers where the data property will be the Error instance pased in, the level will be Error, and the message will be the Error instance message. const e = new Error ( \"An Error\" ); Logger . error ( e ); Subscribing a Listener \u00b6 By default no listeners are subscribed, so if you would like to get logging information you need to subscribe at least one listener. This is done as shown below by importing the Logger and your listener(s) of choice. Here we are using the provided ConsoleListener. We are also setting the active log level, which controls the level of logging that will be output. Be aware that Verbose produces a substantial amount of data about each request. import { Logger , ConsoleListener , LogLevel } from \"@pnp/logging\" ; // subscribe a listener Logger . subscribe ( new ConsoleListener ()); // set the active log level Logger . activeLogLevel = LogLevel . Info ; Available Listeners \u00b6 There are two listeners included in the library, ConsoleListener and FunctionListener. ConsoleListener \u00b6 This listener outputs information to the console and works in Node as well as within browsers. It takes no settings and writes to the appropriate console method based on message level. For example a LogEntry with level Warning will be written to console.warn. Usage is shown in the example above. FunctionListener \u00b6 The FunctionListener allows you to wrap any functionality by creating a function that takes a LogEntry as its single argument. This produces the same result as implementing the LogListener interface, but is useful if you already have a logging method or framework to which you want to pass the messages. import { Logger , FunctionListener , LogEntry } from \"@pnp/logging\" ; let listener = new FunctionListener (( entry : LogEntry ) => { // pass all logging data to an existing framework MyExistingCompanyLoggingFramework . log ( entry . message ); }); Logger . subscribe ( listener ); Create a Custom Listener \u00b6 If desirable for your project you can create a custom listener to perform any logging action you would like. This is done by implementing the LogListener interface. import { Logger , LogListener , LogEntry } from \"@pnp/logging\" ; class MyListener implements LogListener { log ( entry : LogEntry ) : void { // here you would do something with the entry } } Logger . subscribe ( new MyListener ()); UML \u00b6 Graphical UML diagram of @pnp/logging. Right-click the diagram and open in new tab if it is too small.","title":"logging"},{"location":"logging/docs/#pnplogging","text":"The logging module provides light weight subscribable and extensiable logging framework which is used internally and available for use in your projects. This article outlines how to setup logging and use the various loggers.","title":"@pnp/logging"},{"location":"logging/docs/#getting-started","text":"Install the logging module, it has no other dependencies npm install @pnp/logging --save","title":"Getting Started"},{"location":"logging/docs/#understanding-the-logging-framework","text":"The logging framework is based on the Logger class to which any number of listeners can be subscribed. Each of these listeners will receive each of the messages logged. Each listener must implement the LogListener interface, shown below. There is only one method to implement and it takes an instance of the LogEntry interface. /** * Interface that defines a log listener * */ export interface LogListener { /** * Any associated data that a given logging listener may choose to log or ignore * * @param entry The information to be logged */ log ( entry : LogEntry ) : void ; } /** * Interface that defines a log entry * */ export interface LogEntry { /** * The main message to be logged */ message : string ; /** * The level of information this message represents */ level : LogLevel ; /** * Any associated data that a given logging listener may choose to log or ignore */ data? : any ; }","title":"Understanding the Logging Framework"},{"location":"logging/docs/#log-levels","text":"export const enum LogLevel { Verbose = 0 , Info = 1 , Warning = 2 , Error = 3 , Off = 99 , }","title":"Log Levels"},{"location":"logging/docs/#writing-to-the-logger","text":"To write information to a logger you can use either write, writeJSON, or log. import { Logger , LogLevel } from \"@pnp/logging\" ; // write logs a simple string as the message value of the LogEntry Logger . write ( \"This is logging a simple string\" ); // optionally passing a level, default level is Verbose Logger . write ( \"This is logging a simple string\" , LogLevel . Error ); // this will convert the object to a string using JSON.stringify and set the message with the result Logger . writeJSON ({ name : \"value\" , name2 : \"value2\" }); // optionally passing a level, default level is Verbose Logger . writeJSON ({ name : \"value\" , name2 : \"value2\" }, LogLevel . Warn ); // specify the entire LogEntry interface using log Logger . log ({ data : { name : \"value\" , name2 : \"value2\" }, level : LogLevel.Warning , message : \"This is my message\" });","title":"Writing to the Logger"},{"location":"logging/docs/#log-an-error","text":"There exists a shortcut method to log an error to the Logger. This will log an entry to the subscribed loggers where the data property will be the Error instance pased in, the level will be Error, and the message will be the Error instance message. const e = new Error ( \"An Error\" ); Logger . error ( e );","title":"Log an error"},{"location":"logging/docs/#subscribing-a-listener","text":"By default no listeners are subscribed, so if you would like to get logging information you need to subscribe at least one listener. This is done as shown below by importing the Logger and your listener(s) of choice. Here we are using the provided ConsoleListener. We are also setting the active log level, which controls the level of logging that will be output. Be aware that Verbose produces a substantial amount of data about each request. import { Logger , ConsoleListener , LogLevel } from \"@pnp/logging\" ; // subscribe a listener Logger . subscribe ( new ConsoleListener ()); // set the active log level Logger . activeLogLevel = LogLevel . Info ;","title":"Subscribing a Listener"},{"location":"logging/docs/#available-listeners","text":"There are two listeners included in the library, ConsoleListener and FunctionListener.","title":"Available Listeners"},{"location":"logging/docs/#consolelistener","text":"This listener outputs information to the console and works in Node as well as within browsers. It takes no settings and writes to the appropriate console method based on message level. For example a LogEntry with level Warning will be written to console.warn. Usage is shown in the example above.","title":"ConsoleListener"},{"location":"logging/docs/#functionlistener","text":"The FunctionListener allows you to wrap any functionality by creating a function that takes a LogEntry as its single argument. This produces the same result as implementing the LogListener interface, but is useful if you already have a logging method or framework to which you want to pass the messages. import { Logger , FunctionListener , LogEntry } from \"@pnp/logging\" ; let listener = new FunctionListener (( entry : LogEntry ) => { // pass all logging data to an existing framework MyExistingCompanyLoggingFramework . log ( entry . message ); }); Logger . subscribe ( listener );","title":"FunctionListener"},{"location":"logging/docs/#create-a-custom-listener","text":"If desirable for your project you can create a custom listener to perform any logging action you would like. This is done by implementing the LogListener interface. import { Logger , LogListener , LogEntry } from \"@pnp/logging\" ; class MyListener implements LogListener { log ( entry : LogEntry ) : void { // here you would do something with the entry } } Logger . subscribe ( new MyListener ());","title":"Create a Custom Listener"},{"location":"logging/docs/#uml","text":"Graphical UML diagram of @pnp/logging. Right-click the diagram and open in new tab if it is too small.","title":"UML"},{"location":"nodejs/docs/","text":"@pnp/nodejs \u00b6 This package supplies helper code when using the @pnp libraries within the context of nodejs. This removes the node specific functionality from any of the packages. Primarily these consist of clients to enable use of the libraries in nodejs. Getting Started \u00b6 Install the library and required dependencies. You will also need to install other libraries such as @pnp/sp or @pnp/graph to use the exported functionality. npm install @pnp/logging @pnp/core @pnp/nodejs --save AdalFetchClient SPFetchClient BearerTokenFetchClient Using A Proxy UML \u00b6 Graphical UML diagram of @pnp/nodejs. Right-click the diagram and open in new tab if it is too small.","title":"nodejs"},{"location":"nodejs/docs/#pnpnodejs","text":"This package supplies helper code when using the @pnp libraries within the context of nodejs. This removes the node specific functionality from any of the packages. Primarily these consist of clients to enable use of the libraries in nodejs.","title":"@pnp/nodejs"},{"location":"nodejs/docs/#getting-started","text":"Install the library and required dependencies. You will also need to install other libraries such as @pnp/sp or @pnp/graph to use the exported functionality. npm install @pnp/logging @pnp/core @pnp/nodejs --save AdalFetchClient SPFetchClient BearerTokenFetchClient Using A Proxy","title":"Getting Started"},{"location":"nodejs/docs/#uml","text":"Graphical UML diagram of @pnp/nodejs. Right-click the diagram and open in new tab if it is too small.","title":"UML"},{"location":"nodejs/docs/adal-certificate-fetch-client/","text":"@pnp/nodejs/adalcertificatefetchclient \u00b6 The AdalCertificateFetchClient class depends on the adal-node package to authenticate against Azure AD using the client credentials with a client certificate flow. The example below outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected. import { AdalCertificateFetchClient } from \"@pnp/nodejs\" ; import { graph } from \"@pnp/graph\" ; import * as fs from \"fs\" ; import * as path from \"path\" ; // Get the private key from a file (Assuming it's a .pem file) const keyPemFile = \"/path/to/privatekey.pem\" ; const privateKey = fs . readFileSync ( path . resolve ( __dirname , keyPemFile ), { encoding : 'utf8' } ); // setup the client using graph setup function graph . setup ({ graph : { fetchClientFactory : () => { return new AdalCertificateFetchClient ( \"{tenant id}\" , \"{app id}\" , \"{certificate thumbprint}\" , privateKey ); }, }, }); // execute a library request as normal graph . groups . get (). then ( g => { console . log ( JSON . stringify ( g , null , 4 )); }). catch ( e => { console . error ( e ); });","title":"@pnp/nodejs/adalcertificatefetchclient"},{"location":"nodejs/docs/adal-certificate-fetch-client/#pnpnodejsadalcertificatefetchclient","text":"The AdalCertificateFetchClient class depends on the adal-node package to authenticate against Azure AD using the client credentials with a client certificate flow. The example below outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected. import { AdalCertificateFetchClient } from \"@pnp/nodejs\" ; import { graph } from \"@pnp/graph\" ; import * as fs from \"fs\" ; import * as path from \"path\" ; // Get the private key from a file (Assuming it's a .pem file) const keyPemFile = \"/path/to/privatekey.pem\" ; const privateKey = fs . readFileSync ( path . resolve ( __dirname , keyPemFile ), { encoding : 'utf8' } ); // setup the client using graph setup function graph . setup ({ graph : { fetchClientFactory : () => { return new AdalCertificateFetchClient ( \"{tenant id}\" , \"{app id}\" , \"{certificate thumbprint}\" , privateKey ); }, }, }); // execute a library request as normal graph . groups . get (). then ( g => { console . log ( JSON . stringify ( g , null , 4 )); }). catch ( e => { console . error ( e ); });","title":"@pnp/nodejs/adalcertificatefetchclient"},{"location":"nodejs/docs/adal-fetch-client/","text":"@pnp/nodejs/adalfetchclient \u00b6 The AdalFetchClient class depends on the adal-node package to authenticate against Azure AD. The example below outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected. import { AdalFetchClient } from \"@pnp/nodejs\" ; import { graph } from \"@pnp/graph\" ; // setup the client using graph setup function graph . setup ({ graph : { fetchClientFactory : () => { return new AdalFetchClient ( \"{tenant}\" , \"{app id}\" , \"{app secret}\" ); }, }, }); // execute a library request as normal graph . groups . get (). then ( g => { console . log ( JSON . stringify ( g , null , 4 )); }). catch ( e => { console . error ( e ); });","title":"AdalFetchClient"},{"location":"nodejs/docs/adal-fetch-client/#pnpnodejsadalfetchclient","text":"The AdalFetchClient class depends on the adal-node package to authenticate against Azure AD. The example below outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected. import { AdalFetchClient } from \"@pnp/nodejs\" ; import { graph } from \"@pnp/graph\" ; // setup the client using graph setup function graph . setup ({ graph : { fetchClientFactory : () => { return new AdalFetchClient ( \"{tenant}\" , \"{app id}\" , \"{app secret}\" ); }, }, }); // execute a library request as normal graph . groups . get (). then ( g => { console . log ( JSON . stringify ( g , null , 4 )); }). catch ( e => { console . error ( e ); });","title":"@pnp/nodejs/adalfetchclient"},{"location":"nodejs/docs/bearer-token-fetch-client/","text":"@pnp/nodejs/BearerTokenFetchClient \u00b6 The BearerTokenFetchClient class allows you to easily specify your own Bearer tokens to be used in the requests. How you derive the token is up to you. import { BearerTokenFetchClient } from \"@pnp/nodejs\" ; import { graph } from \"@pnp/graph\" ; // setup the client using graph setup function graph . setup ({ graph : { fetchClientFactory : () => { return new BearerTokenFetchClient ( \"{Bearer Token}\" ); }, }, }); // execute a library request as normal graph . groups . get (). then ( g => { console . log ( JSON . stringify ( g , null , 4 )); }). catch ( e => { console . error ( e ); });","title":"BearerTokenFetchClient"},{"location":"nodejs/docs/bearer-token-fetch-client/#pnpnodejsbearertokenfetchclient","text":"The BearerTokenFetchClient class allows you to easily specify your own Bearer tokens to be used in the requests. How you derive the token is up to you. import { BearerTokenFetchClient } from \"@pnp/nodejs\" ; import { graph } from \"@pnp/graph\" ; // setup the client using graph setup function graph . setup ({ graph : { fetchClientFactory : () => { return new BearerTokenFetchClient ( \"{Bearer Token}\" ); }, }, }); // execute a library request as normal graph . groups . get (). then ( g => { console . log ( JSON . stringify ( g , null , 4 )); }). catch ( e => { console . error ( e ); });","title":"@pnp/nodejs/BearerTokenFetchClient"},{"location":"nodejs/docs/provider-hosted-app/","text":"@pnp/nodejs/providerhostedrequestcontext \u00b6 Added in 1.2.7 The ProviderHostedRequestcontext enables the creation of provider-hosted add-ins built in node.js to use pnpjs to interact with SharePoint. The context is associated to a SharePoint user, allowing requests to be made by the add-in on the behalf of the user. The usage of this class assumes the provider-hosted add-in is called from SharePoint with a valid SPAppToken. This is typically done by means of accessing /_layouts/15/AppRedirect.aspx with the app's client ID and app's redirect URI. Note : To support concurrent requests by different users and/or add-ins on different tenants, do not use the SPFetchClient class. Instead, use the more generic NodeFetchClient class. The downside is that you have to manually configure each request to use the desired user/app context. import { sp , SPRest } from \"@pnp/sp\" ; import { NodeFetchClient , ProviderHostedRequestContext } from \"@pnp/nodejs\" ; // configure your node options sp . setup ({ sp : { fetchClientFactory : () => { return new NodeFetchClient (); }, }, }); // get request data generated by /_layouts/15/AppRedirect.aspx const spAppToken = request . body . SPAppToken ; const spSiteUrl = request . body . SPSiteUrl ; // create a context based on the add-in details and SPAppToken const ctx = await ProviderHostedRequestContext . create ( spSiteUrl , \"{client id}\" , \"{client secret}\" , spAppToken ); // create an SPRest object configured to use our context // this is used in place of the global sp object const userSP = new SPRest (). configure ( await ctx . getUserConfig (), spSiteUrl ); const addinSP = new SPRest (). configure ( await ctx . getAddInOnlyConfig (), spSiteUrl ); // make a request on behalf of the user const user = await userSP . web . currentUser . get (); console . log ( `Hello ${ user . Title } ` ); // make an add-in only request const app = await addinSP . web . currentUser . get (); console . log ( `Add-in principal: ${ app . Title } ` );","title":"ProviderHostedRequestContext"},{"location":"nodejs/docs/provider-hosted-app/#pnpnodejsproviderhostedrequestcontext","text":"Added in 1.2.7 The ProviderHostedRequestcontext enables the creation of provider-hosted add-ins built in node.js to use pnpjs to interact with SharePoint. The context is associated to a SharePoint user, allowing requests to be made by the add-in on the behalf of the user. The usage of this class assumes the provider-hosted add-in is called from SharePoint with a valid SPAppToken. This is typically done by means of accessing /_layouts/15/AppRedirect.aspx with the app's client ID and app's redirect URI. Note : To support concurrent requests by different users and/or add-ins on different tenants, do not use the SPFetchClient class. Instead, use the more generic NodeFetchClient class. The downside is that you have to manually configure each request to use the desired user/app context. import { sp , SPRest } from \"@pnp/sp\" ; import { NodeFetchClient , ProviderHostedRequestContext } from \"@pnp/nodejs\" ; // configure your node options sp . setup ({ sp : { fetchClientFactory : () => { return new NodeFetchClient (); }, }, }); // get request data generated by /_layouts/15/AppRedirect.aspx const spAppToken = request . body . SPAppToken ; const spSiteUrl = request . body . SPSiteUrl ; // create a context based on the add-in details and SPAppToken const ctx = await ProviderHostedRequestContext . create ( spSiteUrl , \"{client id}\" , \"{client secret}\" , spAppToken ); // create an SPRest object configured to use our context // this is used in place of the global sp object const userSP = new SPRest (). configure ( await ctx . getUserConfig (), spSiteUrl ); const addinSP = new SPRest (). configure ( await ctx . getAddInOnlyConfig (), spSiteUrl ); // make a request on behalf of the user const user = await userSP . web . currentUser . get (); console . log ( `Hello ${ user . Title } ` ); // make an add-in only request const app = await addinSP . web . currentUser . get (); console . log ( `Add-in principal: ${ app . Title } ` );","title":"@pnp/nodejs/providerhostedrequestcontext"},{"location":"nodejs/docs/proxy/","text":"@pnp/nodejs/proxy \u00b6 Added in 1.3.2 In some cases when deploying on node you may need to use a proxy as governed by corporate policy, or perhaps you want to examine the traffic using a tool such as Fiddler. In the 1.3.2 relesae we introduced the ability to use a proxy with the @pnp/nodejs library. Basic Usage \u00b6 You need to import the new setProxyUrl function from the library and call it with your proxy url. Once done an https-proxy-agent will be used with each request. This works across all clients within the @pnp/nodejs library. import { SPFetchClient , SPOAuthEnv , setProxyUrl } from \"@pnp/nodejs\" ; sp . setup ({ sp : { fetchClientFactory : () => { // call the set proxy url function and it will be used for all requests regardless of client setProxyUrl ( \"{your proxy url}\" ); return new SPFetchClient ( settings . testing . sp . url , settings . testing . sp . id , settings . testing . sp . secret , SPOAuthEnv . SPO ); }, }, }); Use with Fiddler \u00b6 To get Fiddler to work you may need to set an environment variable. This should only be done for testing! import { SPFetchClient , SPOAuthEnv , setProxyUrl } from \"@pnp/nodejs\" ; sp . setup ({ sp : { fetchClientFactory : () => { // ignore certificate errors: ONLY FOR TESTING!! process . env . NODE_TLS_REJECT_UNAUTHORIZED = \"0\" ; // this is my fiddler url locally setProxyUrl ( \"http://127.0.0.1:8888\" ); return new SPFetchClient ( settings . testing . sp . url , settings . testing . sp . id , settings . testing . sp . secret , SPOAuthEnv . SPO ); }, }, });","title":"@pnp/nodejs/proxy"},{"location":"nodejs/docs/proxy/#pnpnodejsproxy","text":"Added in 1.3.2 In some cases when deploying on node you may need to use a proxy as governed by corporate policy, or perhaps you want to examine the traffic using a tool such as Fiddler. In the 1.3.2 relesae we introduced the ability to use a proxy with the @pnp/nodejs library.","title":"@pnp/nodejs/proxy"},{"location":"nodejs/docs/proxy/#basic-usage","text":"You need to import the new setProxyUrl function from the library and call it with your proxy url. Once done an https-proxy-agent will be used with each request. This works across all clients within the @pnp/nodejs library. import { SPFetchClient , SPOAuthEnv , setProxyUrl } from \"@pnp/nodejs\" ; sp . setup ({ sp : { fetchClientFactory : () => { // call the set proxy url function and it will be used for all requests regardless of client setProxyUrl ( \"{your proxy url}\" ); return new SPFetchClient ( settings . testing . sp . url , settings . testing . sp . id , settings . testing . sp . secret , SPOAuthEnv . SPO ); }, }, });","title":"Basic Usage"},{"location":"nodejs/docs/proxy/#use-with-fiddler","text":"To get Fiddler to work you may need to set an environment variable. This should only be done for testing! import { SPFetchClient , SPOAuthEnv , setProxyUrl } from \"@pnp/nodejs\" ; sp . setup ({ sp : { fetchClientFactory : () => { // ignore certificate errors: ONLY FOR TESTING!! process . env . NODE_TLS_REJECT_UNAUTHORIZED = \"0\" ; // this is my fiddler url locally setProxyUrl ( \"http://127.0.0.1:8888\" ); return new SPFetchClient ( settings . testing . sp . url , settings . testing . sp . id , settings . testing . sp . secret , SPOAuthEnv . SPO ); }, }, });","title":"Use with Fiddler"},{"location":"nodejs/docs/sp-fetch-client/","text":"@pnp/nodejs/spfetchclient \u00b6 The SPFetchClient is used to authentication to SharePoint as a provider hosted add-in using a client and secret in nodejs. Remember it is not a good practice to expose client ids and secrets on the client and use of this class is intended for nodejs exclusively. import { SPFetchClient } from \"@pnp/nodejs\" ; import { sp } from \"@pnp/sp\" ; sp . setup ({ sp : { fetchClientFactory : () => { return new SPFetchClient ( \"{site url}\" , \"{client id}\" , \"{client secret}\" ); }, }, }); // execute a library request as normal sp . web . get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); }). catch ( e => { console . error ( e ); }); Set Authentication Environment \u00b6 Added in 1.1.2 For some areas such as Germany, China, and US Gov clouds you need to specify a different authentication url to the service. This is done by specifying the correct SPOAuthEnv enumeration to the SPFetchClient constructor. The options are listed below. If you are not sure which option to specify the default is likely OK. SPO : (default) for all *.sharepoint.com urls China: for China hosted cloud Germany: for Germany local cloud USDef: USA Defense cloud USGov: USA Government cloud import { sp } from \"@pnp/sp\" ; import { SPFetchClient , SPOAuthEnv } from \"@pnp/nodejs\" ; sp . setup ({ sp : { fetchClientFactory : () => { return new SPFetchClient ( \"{site url}\" , \"{client id}\" , \"{client secret}\" , SPOAuthEnv . China ); }, }, }); Set Realm \u00b6 In some cases automatically resolving the realm may not work. In this case you can set the realm parameter in the SPFetchClient constructor. You can determine the correct value for the realm by navigating to \"https://{site name}-admin.sharepoint.com/_layouts/15/TA_AllAppPrincipals.aspx\" and copying the GUID value that appears after the \"@\" - this is the realm id. As of version 1.1.2 the realm parameter is now the 5th parameter in the constructor. import { sp } from \"@pnp/sp\" ; import { SPFetchClient , SPOAuthEnv } from \"@pnp/nodejs\" ; sp . setup ({ sp : { fetchClientFactory : () => { return new SPFetchClient ( \"{site url}\" , \"{client id}\" , \"{client secret}\" , SPOAuthEnv . SPO , \"{realm}\" ); }, }, }); Creating a client id and secret \u00b6 This section outlines how to register for a client id and secret for use in the above code. Register An Add-In \u00b6 Before you can begin running tests you need to register a low-trust add-in with SharePoint. This is primarily designed for Office 365, but can work on-premises if you configure your farm accordingly . Navigation to {site url}/_layouts/appregnew.aspx Click \"Generate\" for both the Client Id and Secret values Give you add-in a title, this can be anything but will let you locate it in the list of add-in permissions Provide a fake value for app domain and redirect uri, you can use the values shown in the examples Click \"Create\" Copy the returned block of text containing the client id and secret as well as app name for your records and later in this article. Grant Your Add-In Permissions \u00b6 Now that we have created an add-in registration we need to tell SharePoint what permissions it can use. Due to an update in SharePoint Online you now have to register add-ins with certain permissions in the admin site . Navigate to {admin site url}/_layouts/appinv.aspx Paste your client id from the above section into the Add Id box and click \"Lookup\" You should see the information populated into the form from the last section, if not ensure you have the correct id value Paste the below XML into the permissions request xml box and hit \"Create\" You should get a confirmation message. Note that the above XML will grant full tenant control, you should grant only those permissions necessary for your application","title":"SPFetchClient"},{"location":"nodejs/docs/sp-fetch-client/#pnpnodejsspfetchclient","text":"The SPFetchClient is used to authentication to SharePoint as a provider hosted add-in using a client and secret in nodejs. Remember it is not a good practice to expose client ids and secrets on the client and use of this class is intended for nodejs exclusively. import { SPFetchClient } from \"@pnp/nodejs\" ; import { sp } from \"@pnp/sp\" ; sp . setup ({ sp : { fetchClientFactory : () => { return new SPFetchClient ( \"{site url}\" , \"{client id}\" , \"{client secret}\" ); }, }, }); // execute a library request as normal sp . web . get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); }). catch ( e => { console . error ( e ); });","title":"@pnp/nodejs/spfetchclient"},{"location":"nodejs/docs/sp-fetch-client/#set-authentication-environment","text":"Added in 1.1.2 For some areas such as Germany, China, and US Gov clouds you need to specify a different authentication url to the service. This is done by specifying the correct SPOAuthEnv enumeration to the SPFetchClient constructor. The options are listed below. If you are not sure which option to specify the default is likely OK. SPO : (default) for all *.sharepoint.com urls China: for China hosted cloud Germany: for Germany local cloud USDef: USA Defense cloud USGov: USA Government cloud import { sp } from \"@pnp/sp\" ; import { SPFetchClient , SPOAuthEnv } from \"@pnp/nodejs\" ; sp . setup ({ sp : { fetchClientFactory : () => { return new SPFetchClient ( \"{site url}\" , \"{client id}\" , \"{client secret}\" , SPOAuthEnv . China ); }, }, });","title":"Set Authentication Environment"},{"location":"nodejs/docs/sp-fetch-client/#set-realm","text":"In some cases automatically resolving the realm may not work. In this case you can set the realm parameter in the SPFetchClient constructor. You can determine the correct value for the realm by navigating to \"https://{site name}-admin.sharepoint.com/_layouts/15/TA_AllAppPrincipals.aspx\" and copying the GUID value that appears after the \"@\" - this is the realm id. As of version 1.1.2 the realm parameter is now the 5th parameter in the constructor. import { sp } from \"@pnp/sp\" ; import { SPFetchClient , SPOAuthEnv } from \"@pnp/nodejs\" ; sp . setup ({ sp : { fetchClientFactory : () => { return new SPFetchClient ( \"{site url}\" , \"{client id}\" , \"{client secret}\" , SPOAuthEnv . SPO , \"{realm}\" ); }, }, });","title":"Set Realm"},{"location":"nodejs/docs/sp-fetch-client/#creating-a-client-id-and-secret","text":"This section outlines how to register for a client id and secret for use in the above code.","title":"Creating a client id and secret"},{"location":"nodejs/docs/sp-fetch-client/#register-an-add-in","text":"Before you can begin running tests you need to register a low-trust add-in with SharePoint. This is primarily designed for Office 365, but can work on-premises if you configure your farm accordingly . Navigation to {site url}/_layouts/appregnew.aspx Click \"Generate\" for both the Client Id and Secret values Give you add-in a title, this can be anything but will let you locate it in the list of add-in permissions Provide a fake value for app domain and redirect uri, you can use the values shown in the examples Click \"Create\" Copy the returned block of text containing the client id and secret as well as app name for your records and later in this article.","title":"Register An Add-In"},{"location":"nodejs/docs/sp-fetch-client/#grant-your-add-in-permissions","text":"Now that we have created an add-in registration we need to tell SharePoint what permissions it can use. Due to an update in SharePoint Online you now have to register add-ins with certain permissions in the admin site . Navigate to {admin site url}/_layouts/appinv.aspx Paste your client id from the above section into the Add Id box and click \"Lookup\" You should see the information populated into the form from the last section, if not ensure you have the correct id value Paste the below XML into the permissions request xml box and hit \"Create\" You should get a confirmation message. Note that the above XML will grant full tenant control, you should grant only those permissions necessary for your application","title":"Grant Your Add-In Permissions"},{"location":"odata/docs/","text":"@pnp/queryable \u00b6 This modules contains the abstract core classes used to process odata requests. They can also be used to build your own odata library should you wish to. By sharing the core functionality across libraries we can provide a consistent API as well as ensure the core code is solid and well tested, with any updates benefitting all inheriting libraries. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable --save Library Topics \u00b6 caching core OData Batching Parsers Pipeline Queryable UML \u00b6 Graphical UML diagram of @pnp/queryable. Right-click the diagram and open in new tab if it is too small.","title":"odata"},{"location":"odata/docs/#pnpodata","text":"This modules contains the abstract core classes used to process odata requests. They can also be used to build your own odata library should you wish to. By sharing the core functionality across libraries we can provide a consistent API as well as ensure the core code is solid and well tested, with any updates benefitting all inheriting libraries.","title":"@pnp/queryable"},{"location":"odata/docs/#getting-started","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable --save","title":"Getting Started"},{"location":"odata/docs/#library-topics","text":"caching core OData Batching Parsers Pipeline Queryable","title":"Library Topics"},{"location":"odata/docs/#uml","text":"Graphical UML diagram of @pnp/queryable. Right-click the diagram and open in new tab if it is too small.","title":"UML"},{"location":"odata/docs/caching/","text":"@pnp/queryable/caching \u00b6 Often times data doesn't change that quickly, especially in the case of rolling up corporate news or upcoming events. These types of things can be cached for minutes if not hours. To help make caching easy you just need to insert the usingCaching method in your chain. This only applies to get requests. The usingCaching method can be used with the inBatch method as well to cache the results of batched requests. The below examples uses the @pnp/sp library as the example - but this works equally well for any library making use of the @pnp/queryable base classes, such as @pnp/graph. Basic example \u00b6 You can use the method without any additional configuration. We have made some default choices for you and will discuss ways to override them later. The below code will get the items from the list, first checking the cache for the value. You can also use it with OData operators such as top and orderBy. The usingCaching() should always be the last method in the chain before the get() (OR if you are using [[batching]] these methods can be transposed, more details below). import { sp } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( \"Tasks\" ). items . usingCaching (). get (). then ( r => { console . log ( r ) }); sp . web . lists . getByTitle ( \"Tasks\" ). items . top ( 5 ). orderBy ( \"Modified\" ). usingCaching (). get (). then ( r => { console . log ( r ) }); Globally Configure Cache Settings \u00b6 If you would like to not use the default values, but don't want to clutter your code by setting the caching values on each request you can configure custom options globally. These will be applied to all calls to usingCaching() throughout your application. import { sp } from \"@pnp/sp\" ; sp . setup ({ defaultCachingStore : \"session\" , // or \"local\" defaultCachingTimeoutSeconds : 30 , globalCacheDisable : false // or true to disable caching in case of debugging/testing }); sp . web . lists . getByTitle ( \"Tasks\" ). items . top ( 5 ). orderBy ( \"Modified\" ). usingCaching (). get (). then ( r => { console . log ( r ) }); Per Call Configuration \u00b6 If you prefer more verbose code or have a need to manage the cache settings on a per request basis you can include individual caching settings for each request. These settings are passed to the usingCaching method call and are defined in the following interface. If you want to use the per-request options you must include the key. export interface ICachingOptions { expiration? : Date ; storeName ?: \"session\" | \"local\" ; key : string ; } import { sp } from \"@pnp/sp\" ; import { dateAdd } from \"@pnp/core\" ; sp . web . lists . getByTitle ( \"Tasks\" ). items . top ( 5 ). orderBy ( \"Modified\" ). usingCaching ({ expiration : dateAdd ( new Date (), \"minute\" , 20 ), key : \"My Key\" , storeName : \"local\" }). get (). then ( r => { console . log ( r ) }); Using Batching with Caching \u00b6 You can use batching and caching together, but remember caching is only applied to get requests. When you use them together the methods can be transposed, the below example is valid. import { sp } from \"@pnp/sp\" ; let batch = sp . createBatch (); sp . web . lists . inBatch ( batch ). usingCaching (). get (). then ( r => { console . log ( r ) }); sp . web . lists . getByTitle ( \"Tasks\" ). items . usingCaching (). inBatch ( batch ). get (). then ( r => { console . log ( r ) }); batch . execute (). then (() => console . log ( \"All done!\" )); Implement Custom Caching \u00b6 You may desire to use a different caching strategy than the one we implemented within the library. The easiest way to achieve this is to wrap the request in your custom caching functionality using the unresolved promise as needed. Here we show how to implement the Stale While Revalidate pattern as discussed here . Implement caching helper method: \u00b6 We create a map to act as our cache storage and a function to wrap the request caching logic const map = new Map < string , any > (); async function staleWhileRevalidate < T > ( key : string , p : Promise < T > ) : Promise < T > { if ( map . has ( key )) { // In Cache p . then ( u => { // Update Cache once we have a result map . set ( key , u ); }); // Return from Cache return map . get ( key ); } // Not In Cache so we need to wait for the value const r = await p ; // Set Cache map . set ( key , r ); // Return from Promise return r ; } Usage \u00b6 Don't call usingCaching just apply the helper method // this one will wait for the request to finish const r1 = await staleWhileRevalidate ( \"test1\" , sp . web . select ( \"Title\" , \"Description\" ). get ()); console . log ( JSON . stringify ( r1 , null , 2 )); // this one will return the result from cache and then update the cache in the background const r2 = await staleWhileRevalidate ( \"test1\" , sp . web . select ( \"Title\" , \"Description\" ). get ()); console . log ( JSON . stringify ( r2 , null , 2 )); Wrapper Function \u00b6 You can wrap this call into a single function you can reuse within your application each time you need the web data for example. You can update the select and interface to match your needs as well. interface WebData { Title : string ; Description : string ; } function getWebData () : Promise < WebData > { return staleWhileRevalidate ( \"test1\" , sp . web . select ( \"Title\" , \"Description\" ). get ()); } // this one will wait for the request to finish const r1 = await getWebData (); console . log ( JSON . stringify ( r1 , null , 2 )); // this one will return the result from cache and then update the cache in the background const r2 = await getWebData (); console . log ( JSON . stringify ( r2 , null , 2 ));","title":"caching"},{"location":"odata/docs/caching/#pnpodatacaching","text":"Often times data doesn't change that quickly, especially in the case of rolling up corporate news or upcoming events. These types of things can be cached for minutes if not hours. To help make caching easy you just need to insert the usingCaching method in your chain. This only applies to get requests. The usingCaching method can be used with the inBatch method as well to cache the results of batched requests. The below examples uses the @pnp/sp library as the example - but this works equally well for any library making use of the @pnp/queryable base classes, such as @pnp/graph.","title":"@pnp/queryable/caching"},{"location":"odata/docs/caching/#basic-example","text":"You can use the method without any additional configuration. We have made some default choices for you and will discuss ways to override them later. The below code will get the items from the list, first checking the cache for the value. You can also use it with OData operators such as top and orderBy. The usingCaching() should always be the last method in the chain before the get() (OR if you are using [[batching]] these methods can be transposed, more details below). import { sp } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( \"Tasks\" ). items . usingCaching (). get (). then ( r => { console . log ( r ) }); sp . web . lists . getByTitle ( \"Tasks\" ). items . top ( 5 ). orderBy ( \"Modified\" ). usingCaching (). get (). then ( r => { console . log ( r ) });","title":"Basic example"},{"location":"odata/docs/caching/#globally-configure-cache-settings","text":"If you would like to not use the default values, but don't want to clutter your code by setting the caching values on each request you can configure custom options globally. These will be applied to all calls to usingCaching() throughout your application. import { sp } from \"@pnp/sp\" ; sp . setup ({ defaultCachingStore : \"session\" , // or \"local\" defaultCachingTimeoutSeconds : 30 , globalCacheDisable : false // or true to disable caching in case of debugging/testing }); sp . web . lists . getByTitle ( \"Tasks\" ). items . top ( 5 ). orderBy ( \"Modified\" ). usingCaching (). get (). then ( r => { console . log ( r ) });","title":"Globally Configure Cache Settings"},{"location":"odata/docs/caching/#per-call-configuration","text":"If you prefer more verbose code or have a need to manage the cache settings on a per request basis you can include individual caching settings for each request. These settings are passed to the usingCaching method call and are defined in the following interface. If you want to use the per-request options you must include the key. export interface ICachingOptions { expiration? : Date ; storeName ?: \"session\" | \"local\" ; key : string ; } import { sp } from \"@pnp/sp\" ; import { dateAdd } from \"@pnp/core\" ; sp . web . lists . getByTitle ( \"Tasks\" ). items . top ( 5 ). orderBy ( \"Modified\" ). usingCaching ({ expiration : dateAdd ( new Date (), \"minute\" , 20 ), key : \"My Key\" , storeName : \"local\" }). get (). then ( r => { console . log ( r ) });","title":"Per Call Configuration"},{"location":"odata/docs/caching/#using-batching-with-caching","text":"You can use batching and caching together, but remember caching is only applied to get requests. When you use them together the methods can be transposed, the below example is valid. import { sp } from \"@pnp/sp\" ; let batch = sp . createBatch (); sp . web . lists . inBatch ( batch ). usingCaching (). get (). then ( r => { console . log ( r ) }); sp . web . lists . getByTitle ( \"Tasks\" ). items . usingCaching (). inBatch ( batch ). get (). then ( r => { console . log ( r ) }); batch . execute (). then (() => console . log ( \"All done!\" ));","title":"Using Batching with Caching"},{"location":"odata/docs/caching/#implement-custom-caching","text":"You may desire to use a different caching strategy than the one we implemented within the library. The easiest way to achieve this is to wrap the request in your custom caching functionality using the unresolved promise as needed. Here we show how to implement the Stale While Revalidate pattern as discussed here .","title":"Implement Custom Caching"},{"location":"odata/docs/caching/#implement-caching-helper-method","text":"We create a map to act as our cache storage and a function to wrap the request caching logic const map = new Map < string , any > (); async function staleWhileRevalidate < T > ( key : string , p : Promise < T > ) : Promise < T > { if ( map . has ( key )) { // In Cache p . then ( u => { // Update Cache once we have a result map . set ( key , u ); }); // Return from Cache return map . get ( key ); } // Not In Cache so we need to wait for the value const r = await p ; // Set Cache map . set ( key , r ); // Return from Promise return r ; }","title":"Implement caching helper method:"},{"location":"odata/docs/caching/#usage","text":"Don't call usingCaching just apply the helper method // this one will wait for the request to finish const r1 = await staleWhileRevalidate ( \"test1\" , sp . web . select ( \"Title\" , \"Description\" ). get ()); console . log ( JSON . stringify ( r1 , null , 2 )); // this one will return the result from cache and then update the cache in the background const r2 = await staleWhileRevalidate ( \"test1\" , sp . web . select ( \"Title\" , \"Description\" ). get ()); console . log ( JSON . stringify ( r2 , null , 2 ));","title":"Usage"},{"location":"odata/docs/caching/#wrapper-function","text":"You can wrap this call into a single function you can reuse within your application each time you need the web data for example. You can update the select and interface to match your needs as well. interface WebData { Title : string ; Description : string ; } function getWebData () : Promise < WebData > { return staleWhileRevalidate ( \"test1\" , sp . web . select ( \"Title\" , \"Description\" ). get ()); } // this one will wait for the request to finish const r1 = await getWebData (); console . log ( JSON . stringify ( r1 , null , 2 )); // this one will return the result from cache and then update the cache in the background const r2 = await getWebData (); console . log ( JSON . stringify ( r2 , null , 2 ));","title":"Wrapper Function"},{"location":"odata/docs/core/","text":"@pnp/queryable/core \u00b6 This modules contains shared interfaces and abstract classes used within, and by inheritors of, the @pnp/queryable package. ProcessHttpClientResponseException \u00b6 The exception thrown when a response is returned and cannot be processed. interface ODataParser \u00b6 Base interface used to describe a class that that will parse incoming responses. It takes a single type parameter representing the type of the value to be returned. It has two methods, one is optional: parse(r: Response): Promise - main method use to parse a response and return a Promise resolving to an object of type T hydrate?: (d: any) => T - optional method used when getting an object from the cache if it requires calling a constructor ODataParserBase \u00b6 The base class used by all parsers in the @pnp libraries. It is optional to use when creating your own custom parsers, but does contain several helper methods. Create a custom parser from ODataParserBase \u00b6 You can always create custom parsers for your projects, however it is likely you will not require this step as the default parsers should work for most cases. class MyParser extends ODataParserBase < any > { // we need to override the parse method to do our custom stuff public parse ( r : Response ) : Promise < T > { // we wrap everything in a promise return new Promise (( resolve , reject ) => { // lets use the default error handling which returns true for no error // and will call reject with an error if one exists if ( this . handleError ( r , reject )) { // now we add our custom parsing here r . text (). then ( txt => { // here we call a madeup function to parse the result // this is where we would do our parsing as required myCustomerUnencode ( txt ). then ( v => { resolve ( v ); }); }); } }); } }","title":"core"},{"location":"odata/docs/core/#pnpodatacore","text":"This modules contains shared interfaces and abstract classes used within, and by inheritors of, the @pnp/queryable package.","title":"@pnp/queryable/core"},{"location":"odata/docs/core/#processhttpclientresponseexception","text":"The exception thrown when a response is returned and cannot be processed.","title":"ProcessHttpClientResponseException"},{"location":"odata/docs/core/#interface-odataparser","text":"Base interface used to describe a class that that will parse incoming responses. It takes a single type parameter representing the type of the value to be returned. It has two methods, one is optional: parse(r: Response): Promise - main method use to parse a response and return a Promise resolving to an object of type T hydrate?: (d: any) => T - optional method used when getting an object from the cache if it requires calling a constructor","title":"interface ODataParser"},{"location":"odata/docs/core/#odataparserbase","text":"The base class used by all parsers in the @pnp libraries. It is optional to use when creating your own custom parsers, but does contain several helper methods.","title":"ODataParserBase"},{"location":"odata/docs/core/#create-a-custom-parser-from-odataparserbase","text":"You can always create custom parsers for your projects, however it is likely you will not require this step as the default parsers should work for most cases. class MyParser extends ODataParserBase < any > { // we need to override the parse method to do our custom stuff public parse ( r : Response ) : Promise < T > { // we wrap everything in a promise return new Promise (( resolve , reject ) => { // lets use the default error handling which returns true for no error // and will call reject with an error if one exists if ( this . handleError ( r , reject )) { // now we add our custom parsing here r . text (). then ( txt => { // here we call a madeup function to parse the result // this is where we would do our parsing as required myCustomerUnencode ( txt ). then ( v => { resolve ( v ); }); }); } }); } }","title":"Create a custom parser from ODataParserBase"},{"location":"odata/docs/odata-batch/","text":"@pnp/queryable/odatabatch \u00b6 This module contains an abstract class used as a base when inheriting libraries support batching. ODataBatchRequestInfo \u00b6 This interface defines what each batch needs to know about each request. It is generic in that any library can provide the information but will be responsible for processing that info by implementing the abstract executeImpl method. ODataBatch \u00b6 Base class for building batching support for a library inheriting from @pnp/queryable. You can see implementations of this abstract class in the @pnp/sp and @pnp/graph modules.","title":"OData Batching"},{"location":"odata/docs/odata-batch/#pnpodataodatabatch","text":"This module contains an abstract class used as a base when inheriting libraries support batching.","title":"@pnp/queryable/odatabatch"},{"location":"odata/docs/odata-batch/#odatabatchrequestinfo","text":"This interface defines what each batch needs to know about each request. It is generic in that any library can provide the information but will be responsible for processing that info by implementing the abstract executeImpl method.","title":"ODataBatchRequestInfo"},{"location":"odata/docs/odata-batch/#odatabatch","text":"Base class for building batching support for a library inheriting from @pnp/queryable. You can see implementations of this abstract class in the @pnp/sp and @pnp/graph modules.","title":"ODataBatch"},{"location":"odata/docs/parsers/","text":"@pnp/queryable/parsers \u00b6 This modules contains a set of generic parsers. These can be used or extended as needed, though it is likely in most cases the default parser will be all you need. ODataDefaultParser \u00b6 The simplest parser used to transform a Response into its JSON representation. The default parser will handle errors in a consistent manner throwing an HttpRequestError instance. This class extends Error and adds the response, status, and statusText properties. The response object is unread. You can use this custom error as shown below to gather more information about what went wrong in the request. import { sp } from \"@pnp/sp\" ; import { JSONParser } from \"@pnp/queryable\" ; try { const parser = new JSONParser (); // this always throws a 404 error await sp . web . getList ( \"doesn't exist\" ). get ( parser ); } catch ( e ) { // we can check for the property \"isHttpRequestError\" to see if this is an instance of our class // this gets by all the many limitations of subclassing Error and type detection in JavaScript if ( e . hasOwnProperty ( \"isHttpRequestError\" )) { console . log ( \"e is HttpRequestError\" ); // now we can access the various properties and make use of the response object. // at this point the body is unread console . log ( `status: ${ e . status } ` ); console . log ( `statusText: ${ e . statusText } ` ); const json = await e . response . clone (). json (); console . log ( JSON . stringify ( json )); const text = await e . response . clone (). text (); console . log ( text ); const headers = e . response . headers ; } console . error ( e ); } TextParser \u00b6 Specialized parser used to parse the response using the .text() method with no other processing. Used primarily for files. BlobParser \u00b6 Specialized parser used to parse the response using the .blob() method with no other processing. Used primarily for files. JSONParser \u00b6 Specialized parser used to parse the response using the .json() method with no other processing. Used primarily for files. BufferParser \u00b6 Specialized parser used to parse the response using the .arrayBuffer() [node] for .buffer() [browser] method with no other processing. Used primarily for files. LambdaParser \u00b6 Allows you to pass in any handler function you want, called if the request does not result in an error that transforms the raw, unread request into the result type. import { LambdaParser } from \"@pnp/queryable\" ; import { sp } from \"@pnp/sp\" ; // here a simple parser duplicating the functionality of the JSONParser const parser = new LambdaParser (( r : Response ) => r . json ()); const webDataJson = await sp . web . get ( parser ); console . log ( webDataJson );","title":"Parsers"},{"location":"odata/docs/parsers/#pnpodataparsers","text":"This modules contains a set of generic parsers. These can be used or extended as needed, though it is likely in most cases the default parser will be all you need.","title":"@pnp/queryable/parsers"},{"location":"odata/docs/parsers/#odatadefaultparser","text":"The simplest parser used to transform a Response into its JSON representation. The default parser will handle errors in a consistent manner throwing an HttpRequestError instance. This class extends Error and adds the response, status, and statusText properties. The response object is unread. You can use this custom error as shown below to gather more information about what went wrong in the request. import { sp } from \"@pnp/sp\" ; import { JSONParser } from \"@pnp/queryable\" ; try { const parser = new JSONParser (); // this always throws a 404 error await sp . web . getList ( \"doesn't exist\" ). get ( parser ); } catch ( e ) { // we can check for the property \"isHttpRequestError\" to see if this is an instance of our class // this gets by all the many limitations of subclassing Error and type detection in JavaScript if ( e . hasOwnProperty ( \"isHttpRequestError\" )) { console . log ( \"e is HttpRequestError\" ); // now we can access the various properties and make use of the response object. // at this point the body is unread console . log ( `status: ${ e . status } ` ); console . log ( `statusText: ${ e . statusText } ` ); const json = await e . response . clone (). json (); console . log ( JSON . stringify ( json )); const text = await e . response . clone (). text (); console . log ( text ); const headers = e . response . headers ; } console . error ( e ); }","title":"ODataDefaultParser"},{"location":"odata/docs/parsers/#textparser","text":"Specialized parser used to parse the response using the .text() method with no other processing. Used primarily for files.","title":"TextParser"},{"location":"odata/docs/parsers/#blobparser","text":"Specialized parser used to parse the response using the .blob() method with no other processing. Used primarily for files.","title":"BlobParser"},{"location":"odata/docs/parsers/#jsonparser","text":"Specialized parser used to parse the response using the .json() method with no other processing. Used primarily for files.","title":"JSONParser"},{"location":"odata/docs/parsers/#bufferparser","text":"Specialized parser used to parse the response using the .arrayBuffer() [node] for .buffer() [browser] method with no other processing. Used primarily for files.","title":"BufferParser"},{"location":"odata/docs/parsers/#lambdaparser","text":"Allows you to pass in any handler function you want, called if the request does not result in an error that transforms the raw, unread request into the result type. import { LambdaParser } from \"@pnp/queryable\" ; import { sp } from \"@pnp/sp\" ; // here a simple parser duplicating the functionality of the JSONParser const parser = new LambdaParser (( r : Response ) => r . json ()); const webDataJson = await sp . web . get ( parser ); console . log ( webDataJson );","title":"LambdaParser"},{"location":"odata/docs/pipeline/","text":"@pnp/queryable/pipeline \u00b6 All of the odata requests processed by @pnp/queryable pass through an extensible request pipeline. Each request is executed in a specific request context defined by the RequestContext interface with the type parameter representing the type ultimately returned at the end a successful processing through the pipeline. Unless you are writing a pipeline method it is unlikely you will ever interact directly with the request pipeline. interface RequestContext \u00b6 The interface that defines the context within which all requests are executed. Note that the pipeline methods to be executed are part of the context. This allows full control over the methods called during a request, and allows for the insertion of any custom methods required. interface RequestContext < T > { batch : ODataBatch ; batchDependency : () => void ; cachingOptions : ICachingOptions ; hasResult? : boolean ; isBatched : boolean ; isCached : boolean ; options : FetchOptions ; parser : ODataParser < T > ; pipeline : Array < ( c : RequestContext < T > ) => Promise < RequestContext < T >>> ; requestAbsoluteUrl : string ; requestId : string ; result? : T ; verb : string ; clientFactory : () => RequestClient ; } requestPipelineMethod decorator \u00b6 The requestPipelineMethod decorator is used to tag a pipeline method and add functionality to bypass processing if a result is already present in the pipeline. If you would like your method to always run regardless of the existance of a result you can pass true to ensure it will always run. Each pipeline method takes a single argument of the current RequestContext and returns a promise resolving to the RequestContext updated as needed. @requestPipelineMethod ( true ) public static myPipelineMethod < T > ( context : RequestContext < T > ) : Promise < RequestContext < T >> { return new Promise < RequestContext < T >> ( resolve => { // do something resolve ( context ); }); } Default Pipeline \u00b6 logs the start of the request checks the cache for a value based on the context's cache settings sends the request if no value from found in the cache logs the end of the request","title":"Pipeline"},{"location":"odata/docs/pipeline/#pnpodatapipeline","text":"All of the odata requests processed by @pnp/queryable pass through an extensible request pipeline. Each request is executed in a specific request context defined by the RequestContext interface with the type parameter representing the type ultimately returned at the end a successful processing through the pipeline. Unless you are writing a pipeline method it is unlikely you will ever interact directly with the request pipeline.","title":"@pnp/queryable/pipeline"},{"location":"odata/docs/pipeline/#interface-requestcontext","text":"The interface that defines the context within which all requests are executed. Note that the pipeline methods to be executed are part of the context. This allows full control over the methods called during a request, and allows for the insertion of any custom methods required. interface RequestContext < T > { batch : ODataBatch ; batchDependency : () => void ; cachingOptions : ICachingOptions ; hasResult? : boolean ; isBatched : boolean ; isCached : boolean ; options : FetchOptions ; parser : ODataParser < T > ; pipeline : Array < ( c : RequestContext < T > ) => Promise < RequestContext < T >>> ; requestAbsoluteUrl : string ; requestId : string ; result? : T ; verb : string ; clientFactory : () => RequestClient ; }","title":"interface RequestContext"},{"location":"odata/docs/pipeline/#requestpipelinemethod-decorator","text":"The requestPipelineMethod decorator is used to tag a pipeline method and add functionality to bypass processing if a result is already present in the pipeline. If you would like your method to always run regardless of the existance of a result you can pass true to ensure it will always run. Each pipeline method takes a single argument of the current RequestContext and returns a promise resolving to the RequestContext updated as needed. @requestPipelineMethod ( true ) public static myPipelineMethod < T > ( context : RequestContext < T > ) : Promise < RequestContext < T >> { return new Promise < RequestContext < T >> ( resolve => { // do something resolve ( context ); }); }","title":"requestPipelineMethod decorator"},{"location":"odata/docs/pipeline/#default-pipeline","text":"logs the start of the request checks the cache for a value based on the context's cache settings sends the request if no value from found in the cache logs the end of the request","title":"Default Pipeline"},{"location":"odata/docs/queryable/","text":"@pnp/queryable/queryable \u00b6 The Queryable class is the base class for all of the libraries building fluent request apis. abstract class ODataQueryable \u00b6 This class takes a single type parameter representing the type of the batch implementation object. If your api will not support batching you can create a dummy class here and simply not use the batching calls. properties \u00b6 query \u00b6 Provides access to the query string builder for this url public methods \u00b6 concat \u00b6 Directly concatenates the supplied string to the current url, not normalizing \"/\" chars configure \u00b6 Sets custom options for current object and all derived objects accessible via chaining import { ConfigOptions } from \"@pnp/queryable\" ; import { sp } from \"@pnp/sp\" ; const headers : ConfigOptions = { Accept : 'application/json;odata=nometadata' }; // here we use configure to set the headers value for all child requests of the list instance const list = sp . web . lists . getByTitle ( \"List1\" ). configure ({ headers }); // this will use the values set in configure list . items . get (). then ( items => console . log ( JSON . stringify ( items , null , 2 )); For reference the ConfigOptions interface is shown below: export interface ConfigOptions { headers? : string [][] | { [ key : string ] : string } | Headers ; mode ?: \"navigate\" | \"same-origin\" | \"no-cors\" | \"cors\" ; credentials ?: \"omit\" | \"same-origin\" | \"include\" ; cache ?: \"default\" | \"no-store\" | \"reload\" | \"no-cache\" | \"force-cache\" | \"only-if-cached\" ; } configureFrom \u00b6 Sets custom options from another queryable instance's options. Identical to configure except the options are derived from the supplied instance. usingCaching \u00b6 Enables caching for this request. See caching for more details. import { sp } from \"@pnp/sp\" sp . web . usingCaching (). get (). then (...); inBatch \u00b6 Adds this query to the supplied batch toUrl \u00b6 Gets the current url abstract toUrlAndQuery() \u00b6 When implemented by an inheriting class will build the full url with appropriate query string used to make the actual request get \u00b6 Execute the current request. Takes an optional type parameter allowing for the typing of the value or the user of parsers that will create specific object instances.","title":"Queryable"},{"location":"odata/docs/queryable/#pnpodataqueryable","text":"The Queryable class is the base class for all of the libraries building fluent request apis.","title":"@pnp/queryable/queryable"},{"location":"odata/docs/queryable/#abstract-class-odataqueryable","text":"This class takes a single type parameter representing the type of the batch implementation object. If your api will not support batching you can create a dummy class here and simply not use the batching calls.","title":"abstract class ODataQueryable"},{"location":"odata/docs/queryable/#properties","text":"","title":"properties"},{"location":"odata/docs/queryable/#query","text":"Provides access to the query string builder for this url","title":"query"},{"location":"odata/docs/queryable/#public-methods","text":"","title":"public methods"},{"location":"odata/docs/queryable/#concat","text":"Directly concatenates the supplied string to the current url, not normalizing \"/\" chars","title":"concat"},{"location":"odata/docs/queryable/#configure","text":"Sets custom options for current object and all derived objects accessible via chaining import { ConfigOptions } from \"@pnp/queryable\" ; import { sp } from \"@pnp/sp\" ; const headers : ConfigOptions = { Accept : 'application/json;odata=nometadata' }; // here we use configure to set the headers value for all child requests of the list instance const list = sp . web . lists . getByTitle ( \"List1\" ). configure ({ headers }); // this will use the values set in configure list . items . get (). then ( items => console . log ( JSON . stringify ( items , null , 2 )); For reference the ConfigOptions interface is shown below: export interface ConfigOptions { headers? : string [][] | { [ key : string ] : string } | Headers ; mode ?: \"navigate\" | \"same-origin\" | \"no-cors\" | \"cors\" ; credentials ?: \"omit\" | \"same-origin\" | \"include\" ; cache ?: \"default\" | \"no-store\" | \"reload\" | \"no-cache\" | \"force-cache\" | \"only-if-cached\" ; }","title":"configure"},{"location":"odata/docs/queryable/#configurefrom","text":"Sets custom options from another queryable instance's options. Identical to configure except the options are derived from the supplied instance.","title":"configureFrom"},{"location":"odata/docs/queryable/#usingcaching","text":"Enables caching for this request. See caching for more details. import { sp } from \"@pnp/sp\" sp . web . usingCaching (). get (). then (...);","title":"usingCaching"},{"location":"odata/docs/queryable/#inbatch","text":"Adds this query to the supplied batch","title":"inBatch"},{"location":"odata/docs/queryable/#tourl","text":"Gets the current url","title":"toUrl"},{"location":"odata/docs/queryable/#abstract-tourlandquery","text":"When implemented by an inheriting class will build the full url with appropriate query string used to make the actual request","title":"abstract toUrlAndQuery()"},{"location":"odata/docs/queryable/#get","text":"Execute the current request. Takes an optional type parameter allowing for the typing of the value or the user of parsers that will create specific object instances.","title":"get"},{"location":"pnpjs/docs/","text":"@pnp/pnpjs \u00b6 The pnpjs library is a rollup of the core libraries across the @pnp scope and is designed only as a bridge to help folks transition from sp-pnp-js, primarily in scenarios where a single file is being imported via a script tag. It is recommended to not use this rollup library where possible and migrate to the individual libraries . Getting Started \u00b6 There are two approaches to using this library: the first is to import, the second is to manually extract the bundled file for use in your project. Install \u00b6 npm install @pnp/pnpjs --save You can then make use of the pnpjs rollup library within your application. It's structure matches sp-pnp-js, though some things may have changed based on the rolled-up dependencies. import pnp from \"@pnp/pnpjs\" ; pnp . sp . web . get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); }); Grab Bundle File \u00b6 This method is useful if you are primarily working within a script editor web part or similar case where you are not using a build pipeline to bundle your application. Install only this library. npm install @pnp/pnpjs Browse to ./node_modules/@pnp/pnpjs/dist and grab either pnpjs.es5.umd.bundle.js or pnpjs.es5.umd.bundle.min.js depending on your needs. You can then add a script tag referencing this file and you will have a global variable \"pnp\". For example you could paste the following into a script editor web part: < p > Script Editor is on page. < script src = \"https://mysite/site_assets/pnpjs.es5.umd.bundle.min.js\" type = \"text/javascript\" > < script type = \"text/javascript\" > pnp . Logger . subscribe ( new pnp . ConsoleListener ()); pnp . Logger . activeLogLevel = pnp . LogLevel . Info ; pnp . sp . web . get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); }); Alternatively to serve the script from the project at \"https://localhost:8080/assets/pnp.js\" you can use: gulp serve --p pnpjs This will allow you to test your changes to the entire bundle live while making updates.","title":"pnpjs"},{"location":"pnpjs/docs/#pnppnpjs","text":"The pnpjs library is a rollup of the core libraries across the @pnp scope and is designed only as a bridge to help folks transition from sp-pnp-js, primarily in scenarios where a single file is being imported via a script tag. It is recommended to not use this rollup library where possible and migrate to the individual libraries .","title":"@pnp/pnpjs"},{"location":"pnpjs/docs/#getting-started","text":"There are two approaches to using this library: the first is to import, the second is to manually extract the bundled file for use in your project.","title":"Getting Started"},{"location":"pnpjs/docs/#install","text":"npm install @pnp/pnpjs --save You can then make use of the pnpjs rollup library within your application. It's structure matches sp-pnp-js, though some things may have changed based on the rolled-up dependencies. import pnp from \"@pnp/pnpjs\" ; pnp . sp . web . get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); });","title":"Install"},{"location":"pnpjs/docs/#grab-bundle-file","text":"This method is useful if you are primarily working within a script editor web part or similar case where you are not using a build pipeline to bundle your application. Install only this library. npm install @pnp/pnpjs Browse to ./node_modules/@pnp/pnpjs/dist and grab either pnpjs.es5.umd.bundle.js or pnpjs.es5.umd.bundle.min.js depending on your needs. You can then add a script tag referencing this file and you will have a global variable \"pnp\". For example you could paste the following into a script editor web part: < p > Script Editor is on page. < script src = \"https://mysite/site_assets/pnpjs.es5.umd.bundle.min.js\" type = \"text/javascript\" > < script type = \"text/javascript\" > pnp . Logger . subscribe ( new pnp . ConsoleListener ()); pnp . Logger . activeLogLevel = pnp . LogLevel . Info ; pnp . sp . web . get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); }); Alternatively to serve the script from the project at \"https://localhost:8080/assets/pnp.js\" you can use: gulp serve --p pnpjs This will allow you to test your changes to the entire bundle live while making updates.","title":"Grab Bundle File"},{"location":"sp/docs/","text":"@pnp/sp \u00b6 This package contains the fluent api used to call the SharePoint rest services. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp --save Import the library into your application and access the root sp object import { sp } from \"@pnp/sp\" ; ( function main() { // here we will load the current web's title sp . web . select ( \"Title\" ). get (). then ( w => { console . log ( `Web Title: ${ w . Title } ` ); }); })() Getting Started: SharePoint Framework \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp --save Import the library into your application, update OnInit, and access the root sp object in render import { sp } from \"@pnp/sp\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present sp . setup ({ spfxContext : this.context }); }); } // ... public render () : void { // A simple loading message this . domElement . innerHTML = `Loading...` ; sp . web . select ( \"Title\" ). get (). then ( w => { this . domElement . innerHTML = `Web Title: ${ w . Title } ` ; }); } Getting Started: Nodejs \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/nodejs --save Import the library into your application, setup the node client, make a request import { sp } from \"@pnp/sp\" ; import { SPFetchClient } from \"@pnp/nodejs\" ; // do this once per page load sp . setup ({ sp : { fetchClientFactory : () => { return new SPFetchClient ( \"{your site url}\" , \"{your client id}\" , \"{your client secret}\" ); }, }, }); // now make any calls you need using the configured client sp . web . select ( \"Title\" ). get (). then ( w => { console . log ( `Web Title: ${ w . Title } ` ); }); Library Topics \u00b6 Alias Parameters ALM api Attachments Client-side Pages Features Fields Files List Items Navigation Service Permissions Related Items Search Sharing Site Designs Social SP.Utilities.Utility Tenant Properties Views Webs Comments and Likes UML \u00b6 Graphical UML diagram of @pnp/sp. Right-click the diagram and open in new tab if it is too small.","title":"sp"},{"location":"sp/docs/#pnpsp","text":"This package contains the fluent api used to call the SharePoint rest services.","title":"@pnp/sp"},{"location":"sp/docs/#getting-started","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp --save Import the library into your application and access the root sp object import { sp } from \"@pnp/sp\" ; ( function main() { // here we will load the current web's title sp . web . select ( \"Title\" ). get (). then ( w => { console . log ( `Web Title: ${ w . Title } ` ); }); })()","title":"Getting Started"},{"location":"sp/docs/#getting-started-sharepoint-framework","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp --save Import the library into your application, update OnInit, and access the root sp object in render import { sp } from \"@pnp/sp\" ; // ... public onInit () : Promise < void > { return super . onInit (). then ( _ => { // other init code may be present sp . setup ({ spfxContext : this.context }); }); } // ... public render () : void { // A simple loading message this . domElement . innerHTML = `Loading...` ; sp . web . select ( \"Title\" ). get (). then ( w => { this . domElement . innerHTML = `Web Title: ${ w . Title } ` ; }); }","title":"Getting Started: SharePoint Framework"},{"location":"sp/docs/#getting-started-nodejs","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/nodejs --save Import the library into your application, setup the node client, make a request import { sp } from \"@pnp/sp\" ; import { SPFetchClient } from \"@pnp/nodejs\" ; // do this once per page load sp . setup ({ sp : { fetchClientFactory : () => { return new SPFetchClient ( \"{your site url}\" , \"{your client id}\" , \"{your client secret}\" ); }, }, }); // now make any calls you need using the configured client sp . web . select ( \"Title\" ). get (). then ( w => { console . log ( `Web Title: ${ w . Title } ` ); });","title":"Getting Started: Nodejs"},{"location":"sp/docs/#library-topics","text":"Alias Parameters ALM api Attachments Client-side Pages Features Fields Files List Items Navigation Service Permissions Related Items Search Sharing Site Designs Social SP.Utilities.Utility Tenant Properties Views Webs Comments and Likes","title":"Library Topics"},{"location":"sp/docs/#uml","text":"Graphical UML diagram of @pnp/sp. Right-click the diagram and open in new tab if it is too small.","title":"UML"},{"location":"sp/docs/alias-parameters/","text":"@pnp/sp - Aliased Parameters \u00b6 Within the @pnp/sp api you can alias any of the parameters so they will be written into the querystring. This is most helpful if you are hitting up against the url length limits when working with files and folders. To alias a parameter you include the label name, a separator (\"::\") and the value in the string. You also need to prepend a \"!\" to the string to trigger the replacement. You can see this below, as well as the string that will be generated. Labels must start with a \"@\" followed by a letter. It is also your responsibility to ensure that the aliases you supply do not conflict, for example if you use \"@p1\" you should use \"@p2\" for a second parameter alias in the same query. Construct a parameter alias \u00b6 Pattern: !@{label name}::{value} Example: \"!@p1::\\sites\\dev\" or \"!@p2::\\text.txt\" Example without aliasing \u00b6 import { sp } from \"@pnp/sp\" ; // still works as expected, no aliasing const query = sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/\" ). files . select ( \"Title\" ). top ( 3 ); console . log ( query . toUrl ()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files console . log ( query . toUrlAndQuery ()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files?$select=Title&$top=3 query . get (). then ( r => { console . log ( r ); }); Example with aliasing \u00b6 import { sp } from \"@pnp/sp\" ; // same query with aliasing const query = sp . web . getFolderByServerRelativeUrl ( \"!@p1::/sites/dev/Shared Documents/\" ). files . select ( \"Title\" ). top ( 3 ); console . log ( query . toUrl ()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files console . log ( query . toUrlAndQuery ()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3 query . get (). then ( r => { console . log ( r ); }); Example with aliasing and batching \u00b6 Aliasing is supported with batching as well: import { sp } from \"@pnp/sp\" ; // same query with aliasing and batching const batch = sp . web . createBatch (); const query = sp . web . getFolderByServerRelativeUrl ( \"!@p1::/sites/dev/Shared Documents/\" ). files . select ( \"Title\" ). top ( 3 ); console . log ( query . toUrl ()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files console . log ( query . toUrlAndQuery ()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3 query . inBatch ( batch ). get (). then ( r => { console . log ( r ); }); batch . execute ();","title":"Alias Parameters"},{"location":"sp/docs/alias-parameters/#pnpsp-aliased-parameters","text":"Within the @pnp/sp api you can alias any of the parameters so they will be written into the querystring. This is most helpful if you are hitting up against the url length limits when working with files and folders. To alias a parameter you include the label name, a separator (\"::\") and the value in the string. You also need to prepend a \"!\" to the string to trigger the replacement. You can see this below, as well as the string that will be generated. Labels must start with a \"@\" followed by a letter. It is also your responsibility to ensure that the aliases you supply do not conflict, for example if you use \"@p1\" you should use \"@p2\" for a second parameter alias in the same query.","title":"@pnp/sp - Aliased Parameters"},{"location":"sp/docs/alias-parameters/#construct-a-parameter-alias","text":"Pattern: !@{label name}::{value} Example: \"!@p1::\\sites\\dev\" or \"!@p2::\\text.txt\"","title":"Construct a parameter alias"},{"location":"sp/docs/alias-parameters/#example-without-aliasing","text":"import { sp } from \"@pnp/sp\" ; // still works as expected, no aliasing const query = sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/\" ). files . select ( \"Title\" ). top ( 3 ); console . log ( query . toUrl ()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files console . log ( query . toUrlAndQuery ()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files?$select=Title&$top=3 query . get (). then ( r => { console . log ( r ); });","title":"Example without aliasing"},{"location":"sp/docs/alias-parameters/#example-with-aliasing","text":"import { sp } from \"@pnp/sp\" ; // same query with aliasing const query = sp . web . getFolderByServerRelativeUrl ( \"!@p1::/sites/dev/Shared Documents/\" ). files . select ( \"Title\" ). top ( 3 ); console . log ( query . toUrl ()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files console . log ( query . toUrlAndQuery ()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3 query . get (). then ( r => { console . log ( r ); });","title":"Example with aliasing"},{"location":"sp/docs/alias-parameters/#example-with-aliasing-and-batching","text":"Aliasing is supported with batching as well: import { sp } from \"@pnp/sp\" ; // same query with aliasing and batching const batch = sp . web . createBatch (); const query = sp . web . getFolderByServerRelativeUrl ( \"!@p1::/sites/dev/Shared Documents/\" ). files . select ( \"Title\" ). top ( 3 ); console . log ( query . toUrl ()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files console . log ( query . toUrlAndQuery ()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3 query . inBatch ( batch ). get (). then ( r => { console . log ( r ); }); batch . execute ();","title":"Example with aliasing and batching"},{"location":"sp/docs/alm/","text":"@pnp/sp/appcatalog \u00b6 The ALM api allows you to manage app installations both in the tenant app catalog and individual site app catalogs. Some of the methods are still in beta and as such may change in the future. This article outlines how to call this api using @pnp/sp. Remember all these actions are bound by permissions so it is likely most users will not have the rights to perform these ALM actions. Understanding the App Catalog Heirarchy \u00b6 Before you begin provisioning applications it is important to understand the relationship between a local web catalog and the tenant app catalog. Some of the methods described below only work within the context of the tenant app catalog web, such as adding an app to the catalog and the app actions retract, remove, and deploy. You can install, uninstall, and upgrade an app in any web. Read more in the official documentation . Reference an App Catalog \u00b6 There are several ways using @pnp/sp to get a reference to an app catalog. These methods are to provide you the greatest amount of flexibility in gaining access to the app catalog. Ultimately each method produces an AppCatalog instance differentiated only by the web to which it points. import { sp } from \"@pnp/sp\" ; // get the curren't context web's app catalog const catalog = sp . web . getAppCatalog (); // you can also chain off the app catalog pnp . sp . web . getAppCatalog (). get (). then ( console . log ); import { sp } from \"@pnp/sp\" ; // you can get the tenant app catalog (or any app catalog) by passing in a url // get the tenant app catalog const tenantCatalog = sp . web . getAppCatalog ( \"https://mytenant.sharepoint.com/sites/appcatalog\" ); // get a different app catalog const catalog = sp . web . getAppCatalog ( \"https://mytenant.sharepoint.com/sites/anothersite\" ); // alternatively you can create a new app catalog instance directly by importing the AppCatalog class import { AppCatalog } from \"@pnp/sp\" ; const catalog = new AppCatalog ( \"https://mytenant.sharepoint.com/sites/dev\" ); // and finally you can combine use of the Web and AppCatalog classes to create an AppCatalog instance from an existing Web import { Web , AppCatalog } from \"@pnp/sp\" ; const web = new Web ( \"https://mytenant.sharepoint.com/sites/dev\" ); const catalog = new AppCatalog ( web ); The following examples make use of a variable \"catalog\" which is assumed to represent an AppCatalog instance obtained using one of the above methods, supporting code is omitted for brevity. List Available Apps \u00b6 The AppCatalog is itself a queryable collection so you can query this object directly to get a list of available apps. Also, the odata operators work on the catalog to sort, filter, and select. // get available apps catalog . get (). then ( console . log ); // get available apps selecting two fields catalog . select ( \"Title\" , \"Deployed\" ). get (). then ( console . log ); Add an App \u00b6 This action must be performed in the context of the tenant app catalog // this represents the file bytes of the app package file const blob = new Blob (); // there is an optional third argument to control overwriting existing files catalog . add ( \"myapp.app\" , blob ). then ( r => { // this is at its core a file add operation so you have access to the response data as well // as a File isntance representing the created file console . log ( JSON . stringify ( r . data , null , 4 )); // all file operations are available r . file . select ( \"Name\" ). get (). then ( console . log ); }); Get an App \u00b6 You can get the details of a single app by GUID id. This is also the branch point to perform specific app actions catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). get (). then ( console . log ); Perform app actions \u00b6 Remember: retract, deploy, and remove only work in the context of the tenant app catalog web. All of these methods return void and you can monitor success using then and catch. // deploy catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). deploy (). then ( console . log ). catch ( console . error ); // retract catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). retract (). then ( console . log ). catch ( console . error ); // install catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). install (). then ( console . log ). catch ( console . error ); // uninstall catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). uninstall (). then ( console . log ). catch ( console . error ); // upgrade catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). upgrade (). then ( console . log ). catch ( console . error ); // remove catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). remove (). then ( console . log ). catch ( console . error ); Notes \u00b6 The app catalog is just a document library under the hood, so you can also perform non-ALM actions on the library if needed. But you should be aware of possible side-effects to the ALM life-cycle when doing so.","title":"ALM api"},{"location":"sp/docs/alm/#pnpspappcatalog","text":"The ALM api allows you to manage app installations both in the tenant app catalog and individual site app catalogs. Some of the methods are still in beta and as such may change in the future. This article outlines how to call this api using @pnp/sp. Remember all these actions are bound by permissions so it is likely most users will not have the rights to perform these ALM actions.","title":"@pnp/sp/appcatalog"},{"location":"sp/docs/alm/#understanding-the-app-catalog-heirarchy","text":"Before you begin provisioning applications it is important to understand the relationship between a local web catalog and the tenant app catalog. Some of the methods described below only work within the context of the tenant app catalog web, such as adding an app to the catalog and the app actions retract, remove, and deploy. You can install, uninstall, and upgrade an app in any web. Read more in the official documentation .","title":"Understanding the App Catalog Heirarchy"},{"location":"sp/docs/alm/#reference-an-app-catalog","text":"There are several ways using @pnp/sp to get a reference to an app catalog. These methods are to provide you the greatest amount of flexibility in gaining access to the app catalog. Ultimately each method produces an AppCatalog instance differentiated only by the web to which it points. import { sp } from \"@pnp/sp\" ; // get the curren't context web's app catalog const catalog = sp . web . getAppCatalog (); // you can also chain off the app catalog pnp . sp . web . getAppCatalog (). get (). then ( console . log ); import { sp } from \"@pnp/sp\" ; // you can get the tenant app catalog (or any app catalog) by passing in a url // get the tenant app catalog const tenantCatalog = sp . web . getAppCatalog ( \"https://mytenant.sharepoint.com/sites/appcatalog\" ); // get a different app catalog const catalog = sp . web . getAppCatalog ( \"https://mytenant.sharepoint.com/sites/anothersite\" ); // alternatively you can create a new app catalog instance directly by importing the AppCatalog class import { AppCatalog } from \"@pnp/sp\" ; const catalog = new AppCatalog ( \"https://mytenant.sharepoint.com/sites/dev\" ); // and finally you can combine use of the Web and AppCatalog classes to create an AppCatalog instance from an existing Web import { Web , AppCatalog } from \"@pnp/sp\" ; const web = new Web ( \"https://mytenant.sharepoint.com/sites/dev\" ); const catalog = new AppCatalog ( web ); The following examples make use of a variable \"catalog\" which is assumed to represent an AppCatalog instance obtained using one of the above methods, supporting code is omitted for brevity.","title":"Reference an App Catalog"},{"location":"sp/docs/alm/#list-available-apps","text":"The AppCatalog is itself a queryable collection so you can query this object directly to get a list of available apps. Also, the odata operators work on the catalog to sort, filter, and select. // get available apps catalog . get (). then ( console . log ); // get available apps selecting two fields catalog . select ( \"Title\" , \"Deployed\" ). get (). then ( console . log );","title":"List Available Apps"},{"location":"sp/docs/alm/#add-an-app","text":"This action must be performed in the context of the tenant app catalog // this represents the file bytes of the app package file const blob = new Blob (); // there is an optional third argument to control overwriting existing files catalog . add ( \"myapp.app\" , blob ). then ( r => { // this is at its core a file add operation so you have access to the response data as well // as a File isntance representing the created file console . log ( JSON . stringify ( r . data , null , 4 )); // all file operations are available r . file . select ( \"Name\" ). get (). then ( console . log ); });","title":"Add an App"},{"location":"sp/docs/alm/#get-an-app","text":"You can get the details of a single app by GUID id. This is also the branch point to perform specific app actions catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). get (). then ( console . log );","title":"Get an App"},{"location":"sp/docs/alm/#perform-app-actions","text":"Remember: retract, deploy, and remove only work in the context of the tenant app catalog web. All of these methods return void and you can monitor success using then and catch. // deploy catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). deploy (). then ( console . log ). catch ( console . error ); // retract catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). retract (). then ( console . log ). catch ( console . error ); // install catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). install (). then ( console . log ). catch ( console . error ); // uninstall catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). uninstall (). then ( console . log ). catch ( console . error ); // upgrade catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). upgrade (). then ( console . log ). catch ( console . error ); // remove catalog . getAppById ( \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\" ). remove (). then ( console . log ). catch ( console . error );","title":"Perform app actions"},{"location":"sp/docs/alm/#notes","text":"The app catalog is just a document library under the hood, so you can also perform non-ALM actions on the library if needed. But you should be aware of possible side-effects to the ALM life-cycle when doing so.","title":"Notes"},{"location":"sp/docs/attachments/","text":"@pnp/sp/attachments \u00b6 The ability to attach file to list items allows users to track documents outside of a document library. You can use the PnP JS Core library to work with attachments as outlined below. Get attachments \u00b6 import { sp } from \"@pnp/sp\" ; let item = sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( 1 ); // get all the attachments item . attachmentFiles . get (). then ( v => { console . log ( v ); }); // get a single file by file name item . attachmentFiles . getByName ( \"file.txt\" ). get (). then ( v => { console . log ( v ); }); // select specific properties using odata operators item . attachmentFiles . select ( \"ServerRelativeUrl\" ). get (). then ( v => { console . log ( v ); }); Add an Attachment \u00b6 You can add an attachment to a list item using the add method. This method takes either a string, Blob, or ArrayBuffer. import { sp } from \"@pnp/sp\" ; let item = sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( 1 ); item . attachmentFiles . add ( \"file2.txt\" , \"Here is my content\" ). then ( v => { console . log ( v ); }); Add Multiple \u00b6 This method allows you to pass an array of AttachmentFileInfo plain objects that will be added one at a time as attachments. Essentially automating the promise chaining. const list = sp . web . lists . getByTitle ( \"MyList\" ); var fileInfos : AttachmentFileInfo [] = []; fileInfos . push ({ name : \"My file name 1\" , content : \"string, blob, or array\" }); fileInfos . push ({ name : \"My file name 2\" , content : \"string, blob, or array\" }); list . items . getById ( 2 ). attachmentFiles . addMultiple ( fileInfos ). then ( r => { console . log ( r ); }); Delete Multiple \u00b6 const list = sp . web . lists . getByTitle ( \"MyList\" ); list . items . getById ( 2 ). attachmentFiles . deleteMultiple ( \"1.txt\" , \"2.txt\" ). then ( r => { console . log ( r ); }); Read Attachment Content \u00b6 You can read the content of an attachment as a string, Blob, ArrayBuffer, or json using the methods supplied. import { sp } from \"@pnp/sp\" ; let item = sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( 1 ); item . attachmentFiles . getByName ( \"file.txt\" ). getText (). then ( v => { console . log ( v ); }); // use this in the browser, does not work in nodejs item . attachmentFiles . getByName ( \"file.mp4\" ). getBlob (). then ( v => { console . log ( v ); }); // use this in nodejs item . attachmentFiles . getByName ( \"file.mp4\" ). getBuffer (). then ( v => { console . log ( v ); }); // file must be valid json item . attachmentFiles . getByName ( \"file.json\" ). getJSON (). then ( v => { console . log ( v ); }); Update Attachment Content \u00b6 You can also update the content of an attachment. This API is limited compared to the full file API - so if you need to upload large files consider using a document library. import { sp } from \"@pnp/sp\" ; let item = sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( 1 ); item . attachmentFiles . getByName ( \"file2.txt\" ). setContent ( \"My new content!!!\" ). then ( v => { console . log ( v ); }); Delete Attachment \u00b6 import { sp } from \"@pnp/sp\" ; let item = sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( 1 ); item . attachmentFiles . getByName ( \"file2.txt\" ). delete (). then ( v => { console . log ( v ); }); Recycle Attachment \u00b6 Added in 1.2.4 Delete the attachment and send it to recycle bin import { sp } from \"@pnp/sp\" ; let item = sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( 1 ); item . attachmentFiles . getByName ( \"file2.txt\" ). recycle (). then ( v => { console . log ( v ); }); Recycle Multiple Attachments \u00b6 Added in 1.2.4 Delete multiple attachments and send them to recycle bin import { sp } from \"@pnp/sp\" ; const list = sp . web . lists . getByTitle ( \"MyList\" ); list . items . getById ( 2 ). attachmentFiles . recycleMultiple ( \"1.txt\" , \"2.txt\" ). then ( r => { console . log ( r ); });","title":"Attachments"},{"location":"sp/docs/attachments/#pnpspattachments","text":"The ability to attach file to list items allows users to track documents outside of a document library. You can use the PnP JS Core library to work with attachments as outlined below.","title":"@pnp/sp/attachments"},{"location":"sp/docs/attachments/#get-attachments","text":"import { sp } from \"@pnp/sp\" ; let item = sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( 1 ); // get all the attachments item . attachmentFiles . get (). then ( v => { console . log ( v ); }); // get a single file by file name item . attachmentFiles . getByName ( \"file.txt\" ). get (). then ( v => { console . log ( v ); }); // select specific properties using odata operators item . attachmentFiles . select ( \"ServerRelativeUrl\" ). get (). then ( v => { console . log ( v ); });","title":"Get attachments"},{"location":"sp/docs/attachments/#add-an-attachment","text":"You can add an attachment to a list item using the add method. This method takes either a string, Blob, or ArrayBuffer. import { sp } from \"@pnp/sp\" ; let item = sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( 1 ); item . attachmentFiles . add ( \"file2.txt\" , \"Here is my content\" ). then ( v => { console . log ( v ); });","title":"Add an Attachment"},{"location":"sp/docs/attachments/#add-multiple","text":"This method allows you to pass an array of AttachmentFileInfo plain objects that will be added one at a time as attachments. Essentially automating the promise chaining. const list = sp . web . lists . getByTitle ( \"MyList\" ); var fileInfos : AttachmentFileInfo [] = []; fileInfos . push ({ name : \"My file name 1\" , content : \"string, blob, or array\" }); fileInfos . push ({ name : \"My file name 2\" , content : \"string, blob, or array\" }); list . items . getById ( 2 ). attachmentFiles . addMultiple ( fileInfos ). then ( r => { console . log ( r ); });","title":"Add Multiple"},{"location":"sp/docs/attachments/#delete-multiple","text":"const list = sp . web . lists . getByTitle ( \"MyList\" ); list . items . getById ( 2 ). attachmentFiles . deleteMultiple ( \"1.txt\" , \"2.txt\" ). then ( r => { console . log ( r ); });","title":"Delete Multiple"},{"location":"sp/docs/attachments/#read-attachment-content","text":"You can read the content of an attachment as a string, Blob, ArrayBuffer, or json using the methods supplied. import { sp } from \"@pnp/sp\" ; let item = sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( 1 ); item . attachmentFiles . getByName ( \"file.txt\" ). getText (). then ( v => { console . log ( v ); }); // use this in the browser, does not work in nodejs item . attachmentFiles . getByName ( \"file.mp4\" ). getBlob (). then ( v => { console . log ( v ); }); // use this in nodejs item . attachmentFiles . getByName ( \"file.mp4\" ). getBuffer (). then ( v => { console . log ( v ); }); // file must be valid json item . attachmentFiles . getByName ( \"file.json\" ). getJSON (). then ( v => { console . log ( v ); });","title":"Read Attachment Content"},{"location":"sp/docs/attachments/#update-attachment-content","text":"You can also update the content of an attachment. This API is limited compared to the full file API - so if you need to upload large files consider using a document library. import { sp } from \"@pnp/sp\" ; let item = sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( 1 ); item . attachmentFiles . getByName ( \"file2.txt\" ). setContent ( \"My new content!!!\" ). then ( v => { console . log ( v ); });","title":"Update Attachment Content"},{"location":"sp/docs/attachments/#delete-attachment","text":"import { sp } from \"@pnp/sp\" ; let item = sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( 1 ); item . attachmentFiles . getByName ( \"file2.txt\" ). delete (). then ( v => { console . log ( v ); });","title":"Delete Attachment"},{"location":"sp/docs/attachments/#recycle-attachment","text":"Added in 1.2.4 Delete the attachment and send it to recycle bin import { sp } from \"@pnp/sp\" ; let item = sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( 1 ); item . attachmentFiles . getByName ( \"file2.txt\" ). recycle (). then ( v => { console . log ( v ); });","title":"Recycle Attachment"},{"location":"sp/docs/attachments/#recycle-multiple-attachments","text":"Added in 1.2.4 Delete multiple attachments and send them to recycle bin import { sp } from \"@pnp/sp\" ; const list = sp . web . lists . getByTitle ( \"MyList\" ); list . items . getById ( 2 ). attachmentFiles . recycleMultiple ( \"1.txt\" , \"2.txt\" ). then ( r => { console . log ( r ); });","title":"Recycle Multiple Attachments"},{"location":"sp/docs/client-side-pages/","text":"@pnp/sp/clientsidepages \u00b6 The ability to manage client-side pages is a capability introduced in version 1.0.2 of @pnp/sp. Through the methods described you can add and edit \"modern\" pages in SharePoint sites. Add Client-side page \u00b6 Using the addClientSidePage you can add a new client side page to a site, specifying the filename. import { sp } from \"@pnp/sp\" ; const page = await sp . web . addClientSidePage ( `file-name` ); // OR const page = await sp . web . addClientSidePage ( `file-name` , `Page Display Title` ); Added in 1.0.5 you can also add a client side page using the list path. This gets around potential language issues with list title. You must specify the list path when calling this method in addition to the new page's filename. import { sp } from \"@pnp/sp\" ; const page = await sp . web . addClientSidePageByPath ( `file-name` , \"/sites/dev/SitePages\" ); Load Client-side page \u00b6 You can also load an existing page based on the file representing that page. Note that the static fromFile returns a promise which resolves so the loaded page. Here we are showing use of the getFileByServerRelativeUrl method to get the File instance, but any of the ways of getting a File instance will work. Also note we are passing the File instance, not the file content. import { sp , ClientSidePage , } from \"@pnp/sp\" ; const page = await ClientSidePage . fromFile ( sp . web . getFileByServerRelativeUrl ( \"/sites/dev/SitePages/ExistingFile.aspx\" )); The remaining examples below reference a variable \"page\" which is assumed to be a ClientSidePage instance loaded through one of the above means. Add Controls \u00b6 A client-side page is made up of sections, which have columns, which contain controls. A new page will have none of these and an existing page may have any combination of these. There are a few rules to understand how sections and columns layout on a page for display. A section is a horizontal piece of a page that extends 100% of the page width. A page with multiple sections will stack these sections based on the section's order property - a 1 based index. Within a section you can have one or more columns. Each column is ordered left to right based on the column's order property. The width of each column is controlled by the factor property whose value is one of 0, 2, 4, 6, 8, 10, or 12. The columns in a section should have factors that add up to 12. Meaning if you wanted to have two equal columns you can set a factor of 6 for each. A page can have empty columns. import { sp , ClientSideText , } from \"@pnp/sp\" ; // this code adds a section, and then adds a control to that section. The control is added to the section's defaultColumn, and if there are no columns a single // column of factor 12 is created as a default. Here we add the ClientSideText part page . addSection (). addControl ( new ClientSideText ( \"@pnp/sp is a great library!\" )); // here we add a section, add two columns, and add a text control to the second section so it will appear on the right of the page // add and get a reference to a new section const section = page . addSection (); // add a column of factor 6 section . addColumn ( 6 ); // add and get a reference to a new column of factor 6 const column = section . addColumn ( 6 ); // add a text control to the second new column column . addControl ( new ClientSideText ( \"Be sure to check out the @pnp docs at https://pnp.github.io/pnpjs/\" )); // we need to save our content changes await page . save (); Add Client-side Web Parts \u00b6 Beyond the text control above you can also add any of the available client-side web parts in a given site. To find out what web parts are available you first call the web's getClientSideWebParts method. Once you have a list of parts you need to find the defintion you want to use, here we get the Embed web part whose's id is \"490d7c76-1824-45b2-9de3-676421c997fa\" (at least in one farm, your mmv). import { sp , ClientSideWebpart , ClientSideWebpartPropertyTypes , } from \"@pnp/sp\" ; // this will be a ClientSidePageComponent array // this can be cached on the client in production scenarios const partDefs = await sp . web . getClientSideWebParts (); // find the definition we want, here by id const partDef = partDefs . filter ( c => c . Id === \"490d7c76-1824-45b2-9de3-676421c997fa\" ); // optionally ensure you found the def if ( partDef . length < 1 ) { // we didn't find it so we throw an error throw new Error ( \"Could not find the web part\" ); } // create a ClientWebPart instance from the definition const part = ClientSideWebpart . fromComponentDef ( partDef [ 0 ]); // set the properties on the web part. Here we have imported the ClientSideWebpartPropertyTypes module and can use that to type // the available settings object. You can use your own types or help us out and add some typings to the module :). // here for the embed web part we only have to supply an embedCode - in this case a youtube video. part . setProperties < ClientSideWebpartPropertyTypes . Embed > ({ embedCode : \"https://www.youtube.com/watch?v=IWQFZ7Lx-rg\" , }); // we add that part to a new section page . addSection (). addControl ( part ); // save our content changes back to the server await page . save (); Find Controls \u00b6 Added in 1.0.3 You can use the either of the two available method to locate controls within a page. These method search through all sections, columns, and controls returning the first instance that meets the supplied criteria. import { ClientSideWebPart } from \"@pnp/sp\" ; // find a control by instance id const control1 = page . findControlById ( \"b99bfccc-164e-4d3d-9b96-da48db62eb78\" ); // type the returned control const control2 = page . findControlById < ClientSideWebPart > ( \"c99bfccc-164e-4d3d-9b96-da48db62eb78\" ); const control3 = page . findControlById < ClientSideText > ( \"a99bfccc-164e-4d3d-9b96-da48db62eb78\" ); // use any predicate to find a control const control4 = page2 . findControl < ClientSideWebpart > (( c : CanvasControl ) => { // any logic you wish can be used on the control here // return true to return that control return c . order > 3 ; }); Control Comments \u00b6 You can choose to enable or disable comments on a page using these methods // indicates if comments are disabled, not valid until the page is loaded (Added in _1.0.3_) page . commentsDisabled // enable comments await page . enableComments (); // disable comments await page . disableComments (); Like/Unlike Client-side page, get like information about page \u00b6 Added in 1.2.4 You can like or unlike a modern page. You can also get information about the likes (i.e like Count and which users liked the page) // Like a Client-side page (Added in _1.2.4_) await page . like (); // Unlike a Client-side page await page . unlike (); // Get liked by information such as like count and user's who liked the page await page . getLikedByInformation (); Sample \u00b6 The below sample shows the process to add a Yammer feed webpart to the page. The properties required as well as the data version are found by adding the part using the UI and reviewing the values. Some or all of these may be discoverable using Yammer APIs . An identical process can be used to add web parts of any type by adjusting the definition, data version, and properties appropriately. // get webpart defs const defs = await sp . web . getClientSideWebParts (); // this is the id of the definition in my farm const yammerPartDef = defs . filter ( d => d . Id === \"31e9537e-f9dc-40a4-8834-0e3b7df418bc\" )[ 0 ]; // page file const file = sp . web . getFileByServerRelativePath ( \"/sites/dev/SitePages/Testing_kVKF.aspx\" ); // create page instance const page = await ClientSidePage . fromFile ( file ); // create part instance from definition const part = ClientSideWebpart . fromComponentDef ( yammerPartDef ); // update data version part . dataVersion = \"1.5\" ; // set the properties required part . setProperties ({ feedType : 0 , isSuiteConnected : false , mode : 2 , networkId : 9999999 , yammerEmbedContainerHeight : 400 , yammerFeedURL : \"\" , yammerGroupId : - 1 , yammerGroupMugshotUrl : \"https://mug0.assets-yammer.com/mugshot/images/{width}x{height}/all_company.png\" , yammerGroupName : \"All Company\" , yammerGroupUrl : \"https://www.yammer.com/{tenant}/#/threads/company?type=general\" , }); // add to the section/column you want page . sections [ 0 ]. addControl ( part ); // persist changes page . save ();","title":"Client-side Pages"},{"location":"sp/docs/client-side-pages/#pnpspclientsidepages","text":"The ability to manage client-side pages is a capability introduced in version 1.0.2 of @pnp/sp. Through the methods described you can add and edit \"modern\" pages in SharePoint sites.","title":"@pnp/sp/clientsidepages"},{"location":"sp/docs/client-side-pages/#add-client-side-page","text":"Using the addClientSidePage you can add a new client side page to a site, specifying the filename. import { sp } from \"@pnp/sp\" ; const page = await sp . web . addClientSidePage ( `file-name` ); // OR const page = await sp . web . addClientSidePage ( `file-name` , `Page Display Title` ); Added in 1.0.5 you can also add a client side page using the list path. This gets around potential language issues with list title. You must specify the list path when calling this method in addition to the new page's filename. import { sp } from \"@pnp/sp\" ; const page = await sp . web . addClientSidePageByPath ( `file-name` , \"/sites/dev/SitePages\" );","title":"Add Client-side page"},{"location":"sp/docs/client-side-pages/#load-client-side-page","text":"You can also load an existing page based on the file representing that page. Note that the static fromFile returns a promise which resolves so the loaded page. Here we are showing use of the getFileByServerRelativeUrl method to get the File instance, but any of the ways of getting a File instance will work. Also note we are passing the File instance, not the file content. import { sp , ClientSidePage , } from \"@pnp/sp\" ; const page = await ClientSidePage . fromFile ( sp . web . getFileByServerRelativeUrl ( \"/sites/dev/SitePages/ExistingFile.aspx\" )); The remaining examples below reference a variable \"page\" which is assumed to be a ClientSidePage instance loaded through one of the above means.","title":"Load Client-side page"},{"location":"sp/docs/client-side-pages/#add-controls","text":"A client-side page is made up of sections, which have columns, which contain controls. A new page will have none of these and an existing page may have any combination of these. There are a few rules to understand how sections and columns layout on a page for display. A section is a horizontal piece of a page that extends 100% of the page width. A page with multiple sections will stack these sections based on the section's order property - a 1 based index. Within a section you can have one or more columns. Each column is ordered left to right based on the column's order property. The width of each column is controlled by the factor property whose value is one of 0, 2, 4, 6, 8, 10, or 12. The columns in a section should have factors that add up to 12. Meaning if you wanted to have two equal columns you can set a factor of 6 for each. A page can have empty columns. import { sp , ClientSideText , } from \"@pnp/sp\" ; // this code adds a section, and then adds a control to that section. The control is added to the section's defaultColumn, and if there are no columns a single // column of factor 12 is created as a default. Here we add the ClientSideText part page . addSection (). addControl ( new ClientSideText ( \"@pnp/sp is a great library!\" )); // here we add a section, add two columns, and add a text control to the second section so it will appear on the right of the page // add and get a reference to a new section const section = page . addSection (); // add a column of factor 6 section . addColumn ( 6 ); // add and get a reference to a new column of factor 6 const column = section . addColumn ( 6 ); // add a text control to the second new column column . addControl ( new ClientSideText ( \"Be sure to check out the @pnp docs at https://pnp.github.io/pnpjs/\" )); // we need to save our content changes await page . save ();","title":"Add Controls"},{"location":"sp/docs/client-side-pages/#add-client-side-web-parts","text":"Beyond the text control above you can also add any of the available client-side web parts in a given site. To find out what web parts are available you first call the web's getClientSideWebParts method. Once you have a list of parts you need to find the defintion you want to use, here we get the Embed web part whose's id is \"490d7c76-1824-45b2-9de3-676421c997fa\" (at least in one farm, your mmv). import { sp , ClientSideWebpart , ClientSideWebpartPropertyTypes , } from \"@pnp/sp\" ; // this will be a ClientSidePageComponent array // this can be cached on the client in production scenarios const partDefs = await sp . web . getClientSideWebParts (); // find the definition we want, here by id const partDef = partDefs . filter ( c => c . Id === \"490d7c76-1824-45b2-9de3-676421c997fa\" ); // optionally ensure you found the def if ( partDef . length < 1 ) { // we didn't find it so we throw an error throw new Error ( \"Could not find the web part\" ); } // create a ClientWebPart instance from the definition const part = ClientSideWebpart . fromComponentDef ( partDef [ 0 ]); // set the properties on the web part. Here we have imported the ClientSideWebpartPropertyTypes module and can use that to type // the available settings object. You can use your own types or help us out and add some typings to the module :). // here for the embed web part we only have to supply an embedCode - in this case a youtube video. part . setProperties < ClientSideWebpartPropertyTypes . Embed > ({ embedCode : \"https://www.youtube.com/watch?v=IWQFZ7Lx-rg\" , }); // we add that part to a new section page . addSection (). addControl ( part ); // save our content changes back to the server await page . save ();","title":"Add Client-side Web Parts"},{"location":"sp/docs/client-side-pages/#find-controls","text":"Added in 1.0.3 You can use the either of the two available method to locate controls within a page. These method search through all sections, columns, and controls returning the first instance that meets the supplied criteria. import { ClientSideWebPart } from \"@pnp/sp\" ; // find a control by instance id const control1 = page . findControlById ( \"b99bfccc-164e-4d3d-9b96-da48db62eb78\" ); // type the returned control const control2 = page . findControlById < ClientSideWebPart > ( \"c99bfccc-164e-4d3d-9b96-da48db62eb78\" ); const control3 = page . findControlById < ClientSideText > ( \"a99bfccc-164e-4d3d-9b96-da48db62eb78\" ); // use any predicate to find a control const control4 = page2 . findControl < ClientSideWebpart > (( c : CanvasControl ) => { // any logic you wish can be used on the control here // return true to return that control return c . order > 3 ; });","title":"Find Controls"},{"location":"sp/docs/client-side-pages/#control-comments","text":"You can choose to enable or disable comments on a page using these methods // indicates if comments are disabled, not valid until the page is loaded (Added in _1.0.3_) page . commentsDisabled // enable comments await page . enableComments (); // disable comments await page . disableComments ();","title":"Control Comments"},{"location":"sp/docs/client-side-pages/#likeunlike-client-side-page-get-like-information-about-page","text":"Added in 1.2.4 You can like or unlike a modern page. You can also get information about the likes (i.e like Count and which users liked the page) // Like a Client-side page (Added in _1.2.4_) await page . like (); // Unlike a Client-side page await page . unlike (); // Get liked by information such as like count and user's who liked the page await page . getLikedByInformation ();","title":"Like/Unlike Client-side page, get like information about page"},{"location":"sp/docs/client-side-pages/#sample","text":"The below sample shows the process to add a Yammer feed webpart to the page. The properties required as well as the data version are found by adding the part using the UI and reviewing the values. Some or all of these may be discoverable using Yammer APIs . An identical process can be used to add web parts of any type by adjusting the definition, data version, and properties appropriately. // get webpart defs const defs = await sp . web . getClientSideWebParts (); // this is the id of the definition in my farm const yammerPartDef = defs . filter ( d => d . Id === \"31e9537e-f9dc-40a4-8834-0e3b7df418bc\" )[ 0 ]; // page file const file = sp . web . getFileByServerRelativePath ( \"/sites/dev/SitePages/Testing_kVKF.aspx\" ); // create page instance const page = await ClientSidePage . fromFile ( file ); // create part instance from definition const part = ClientSideWebpart . fromComponentDef ( yammerPartDef ); // update data version part . dataVersion = \"1.5\" ; // set the properties required part . setProperties ({ feedType : 0 , isSuiteConnected : false , mode : 2 , networkId : 9999999 , yammerEmbedContainerHeight : 400 , yammerFeedURL : \"\" , yammerGroupId : - 1 , yammerGroupMugshotUrl : \"https://mug0.assets-yammer.com/mugshot/images/{width}x{height}/all_company.png\" , yammerGroupName : \"All Company\" , yammerGroupUrl : \"https://www.yammer.com/{tenant}/#/threads/company?type=general\" , }); // add to the section/column you want page . sections [ 0 ]. addControl ( part ); // persist changes page . save ();","title":"Sample"},{"location":"sp/docs/comments-likes/","text":"@pnp/sp/comments and likes \u00b6 Likes and comments in the context of modern sites are based on list items, meaning the operations branch from the Item class. To load an item you can refer to the guidance in the items article . If you want to set the likes or comments on a modern page and don't know the item id but do know the url you can first load the file and then use the getItem method to get an item instance: These APIs are currently in BETA and are subject to change or may not work on all tenants. import { sp } from \"@pnp/sp\" ; const item = await sp . web . getFileByServerRelativeUrl ( \"/sites/dev/SitePages/Test_8q5L.aspx\" ). getItem (); // as an example, or any of the below options await item . like (); The below examples use a variable named \"item\" which is taken to represent an instance of the Item class. Comments \u00b6 Get Comments \u00b6 const comments = await item . comments . get (); You can also get the comments merged with instances of the Comment class to immediately start accessing the properties and methods: import { spODataEntityArray , Comment , CommentData } from \"@pnp/sp\" ; const comments = await item . comments . get ( spODataEntityArray < Comment , CommentData > ( Comment )); // these will be Comment instances in the array comments [ 0 ]. replies . add ({ text : \"#PnPjs is pretty ok!\" }); //load the top 20 replies and comments for an item including likedBy information const comments = await item . comments . expand ( \"replies\" , \"likedBy\" , \"replies/likedBy\" ). top ( 20 ). get (); Add Comment \u00b6 // you can add a comment as a string item . comments . add ( \"string comment\" ); // or you can add it as an object to include mentions item . comments . add ({ text : \"comment from object property\" }); Delete a Comment \u00b6 import { spODataEntityArray , Comment , CommentData } from \"@pnp/sp\" ; const comments = await item . comments . get ( spODataEntityArray < Comment , CommentData > ( Comment )); // these will be Comment instances in the array comments [ 0 ]. delete () Like Comment \u00b6 import { spODataEntityArray , Comment , CommentData } from \"@pnp/sp\" ; const comments = await item . comments . get ( spODataEntityArray < Comment , CommentData > ( Comment )); // these will be Comment instances in the array comments [ 0 ]. like () Unlike Comment \u00b6 import { spODataEntityArray , Comment , CommentData } from \"@pnp/sp\" ; const comments = await item . comments . get ( spODataEntityArray < Comment , CommentData > ( Comment )); comments [ 0 ]. unlike () Reply to a Comment \u00b6 import { spODataEntityArray , Comment , CommentData } from \"@pnp/sp\" ; const comments = await item . comments . get ( spODataEntityArray < Comment , CommentData > ( Comment )); const comment : Comment & CommentData = await comments [ 0 ]. replies . add ({ text : \"#PnPjs is pretty ok!\" }); Load Replies to a Comment \u00b6 import { spODataEntityArray , Comment , CommentData } from \"@pnp/sp\" ; const comments = await item . comments . get ( spODataEntityArray < Comment , CommentData > ( Comment )); const replies = await comments [ 0 ]. replies . get (); Like \u00b6 You can like items and comments on items. See above for how to like or unlike a comment. Below you can see how to like and unlike an items, as well as get the liked by data. import { LikeData } from \"@pnp/sp\" ; // like an item await item . like (); // unlike an item await item . unlike (); // get the liked by information const likedByData : LikeData [] = await item . getLikedBy ();","title":"Comments and Likes"},{"location":"sp/docs/comments-likes/#pnpspcomments-and-likes","text":"Likes and comments in the context of modern sites are based on list items, meaning the operations branch from the Item class. To load an item you can refer to the guidance in the items article . If you want to set the likes or comments on a modern page and don't know the item id but do know the url you can first load the file and then use the getItem method to get an item instance: These APIs are currently in BETA and are subject to change or may not work on all tenants. import { sp } from \"@pnp/sp\" ; const item = await sp . web . getFileByServerRelativeUrl ( \"/sites/dev/SitePages/Test_8q5L.aspx\" ). getItem (); // as an example, or any of the below options await item . like (); The below examples use a variable named \"item\" which is taken to represent an instance of the Item class.","title":"@pnp/sp/comments and likes"},{"location":"sp/docs/comments-likes/#comments","text":"","title":"Comments"},{"location":"sp/docs/comments-likes/#get-comments","text":"const comments = await item . comments . get (); You can also get the comments merged with instances of the Comment class to immediately start accessing the properties and methods: import { spODataEntityArray , Comment , CommentData } from \"@pnp/sp\" ; const comments = await item . comments . get ( spODataEntityArray < Comment , CommentData > ( Comment )); // these will be Comment instances in the array comments [ 0 ]. replies . add ({ text : \"#PnPjs is pretty ok!\" }); //load the top 20 replies and comments for an item including likedBy information const comments = await item . comments . expand ( \"replies\" , \"likedBy\" , \"replies/likedBy\" ). top ( 20 ). get ();","title":"Get Comments"},{"location":"sp/docs/comments-likes/#add-comment","text":"// you can add a comment as a string item . comments . add ( \"string comment\" ); // or you can add it as an object to include mentions item . comments . add ({ text : \"comment from object property\" });","title":"Add Comment"},{"location":"sp/docs/comments-likes/#delete-a-comment","text":"import { spODataEntityArray , Comment , CommentData } from \"@pnp/sp\" ; const comments = await item . comments . get ( spODataEntityArray < Comment , CommentData > ( Comment )); // these will be Comment instances in the array comments [ 0 ]. delete ()","title":"Delete a Comment"},{"location":"sp/docs/comments-likes/#like-comment","text":"import { spODataEntityArray , Comment , CommentData } from \"@pnp/sp\" ; const comments = await item . comments . get ( spODataEntityArray < Comment , CommentData > ( Comment )); // these will be Comment instances in the array comments [ 0 ]. like ()","title":"Like Comment"},{"location":"sp/docs/comments-likes/#unlike-comment","text":"import { spODataEntityArray , Comment , CommentData } from \"@pnp/sp\" ; const comments = await item . comments . get ( spODataEntityArray < Comment , CommentData > ( Comment )); comments [ 0 ]. unlike ()","title":"Unlike Comment"},{"location":"sp/docs/comments-likes/#reply-to-a-comment","text":"import { spODataEntityArray , Comment , CommentData } from \"@pnp/sp\" ; const comments = await item . comments . get ( spODataEntityArray < Comment , CommentData > ( Comment )); const comment : Comment & CommentData = await comments [ 0 ]. replies . add ({ text : \"#PnPjs is pretty ok!\" });","title":"Reply to a Comment"},{"location":"sp/docs/comments-likes/#load-replies-to-a-comment","text":"import { spODataEntityArray , Comment , CommentData } from \"@pnp/sp\" ; const comments = await item . comments . get ( spODataEntityArray < Comment , CommentData > ( Comment )); const replies = await comments [ 0 ]. replies . get ();","title":"Load Replies to a Comment"},{"location":"sp/docs/comments-likes/#like","text":"You can like items and comments on items. See above for how to like or unlike a comment. Below you can see how to like and unlike an items, as well as get the liked by data. import { LikeData } from \"@pnp/sp\" ; // like an item await item . like (); // unlike an item await item . unlike (); // get the liked by information const likedByData : LikeData [] = await item . getLikedBy ();","title":"Like"},{"location":"sp/docs/content-types/","text":"@pnp/sp/content types \u00b6 Set Folder Unique Content Type Order \u00b6 interface OrderData { ContentTypeOrder : { StringValue : string }[]; UniqueContentTypeOrder ?: { StringValue : string }[]; } const folder = sp . web . lists . getById ( \"{list id guid}\" ). rootFolder ; // here you need to see if there are unique content type orders already or just the default const existingOrders = await folder . select ( \"ContentTypeOrder\" , \"UniqueContentTypeOrder\" ). get < OrderData > (); const activeOrder = existingOrders . UniqueContentTypeOrder ? existingOrders.UniqueContentTypeOrder : existingOrders.ContentTypeOrder ; // manipulate the order here however you want (I am just reversing the array as an example) const newOrder = activeOrder . reverse (); // update the content type order thusly: await folder . update ({ UniqueContentTypeOrder : { __metadata : { type : \"Collection(SP.ContentTypeId)\" }, results : newOrder , }, });","title":"Content Types"},{"location":"sp/docs/content-types/#pnpspcontent-types","text":"","title":"@pnp/sp/content types"},{"location":"sp/docs/content-types/#set-folder-unique-content-type-order","text":"interface OrderData { ContentTypeOrder : { StringValue : string }[]; UniqueContentTypeOrder ?: { StringValue : string }[]; } const folder = sp . web . lists . getById ( \"{list id guid}\" ). rootFolder ; // here you need to see if there are unique content type orders already or just the default const existingOrders = await folder . select ( \"ContentTypeOrder\" , \"UniqueContentTypeOrder\" ). get < OrderData > (); const activeOrder = existingOrders . UniqueContentTypeOrder ? existingOrders.UniqueContentTypeOrder : existingOrders.ContentTypeOrder ; // manipulate the order here however you want (I am just reversing the array as an example) const newOrder = activeOrder . reverse (); // update the content type order thusly: await folder . update ({ UniqueContentTypeOrder : { __metadata : { type : \"Collection(SP.ContentTypeId)\" }, results : newOrder , }, });","title":"Set Folder Unique Content Type Order"},{"location":"sp/docs/entity-merging/","text":"@pnp/sp - entity merging \u00b6 Sometimes when we make a query entity's data we would like then to immediately run other commands on the returned entity. To have data returned as its represending type we make use of the spODataEntity and spODataEntityArray parsers. The below approach works for all instance types such as List, Web, Item, or Field as examples. Request a single entity \u00b6 If we are loading a single entity we use the spODataEntity method. Here we show loading a list item using the Item class and a simple get query. import { sp , spODataEntity , Item } from \"@pnp/sp\" ; // interface defining the returned properites interface MyProps { Id : number ; } try { // get a list item laoded with data and merged into an instance of Item const item = await sp . web . lists . getByTitle ( \"ListTitle\" ). items . getById ( 1 ). get ( spODataEntity < Item , MyProps > ( Item )); // log the item id, all properties specified in MyProps will be type checked Logger . write ( `Item id: ${ item . Id } ` ); // now we can call update because we have an instance of the Item type to work with as well await item . update ({ Title : \"New title.\" , }); } catch ( e ) { Logger . error ( e ); } Request a collection \u00b6 The same pattern works when requesting a collection of objects with the exception of using the spODataEntityArray method. import { sp , spODataEntityArray , Item } from \"@pnp/sp\" ; // interface defining the returned properites interface MyProps { Id : number ; Title : string ; } try { // get a list item laoded with data and merged into an instance of Item const items = await sp . web . lists . getByTitle ( \"ListTitle\" ). items . select ( \"Id\" , \"Title\" ). get ( spODataEntityArray < Item , MyProps > ( Item )); Logger . write ( `Item id: ${ items . length } ` ); Logger . write ( `Item id: ${ items [ 0 ]. Title } ` ); // now we can call update because we have an instance of the Item type to work with as well await items [ 0 ]. update ({ Title : \"New title.\" , }); } catch ( e ) { Logger . error ( e ); } Use with Item getPaged \u00b6 Added in 1.3.4 Starting with 1.3.4 you can now include entity merging in the getPaged command as shown below. This approach will work with any objects matching the required factory pattern. // create Item instances with the defined property Title const items = await sp . web . lists . getByTitle ( \"BigList\" ). items . select ( \"Title\" ). getPaged ( spODataEntityArray < Item , { Title : string } > ( Item )); console . log ( items . results . length ); // now invoke methods on the Item object const perms = await items . results [ 0 ]. getCurrentUserEffectivePermissions (); console . log ( JSON . stringify ( perms , null , 2 )); // you can also type the result slightly differently if you prefer this, but the results are the same functionally. const items2 = await sp . web . lists . getByTitle ( \"BigList\" ). items . select ( \"Title\" ). getPaged < ( Item & { Title : string })[] > ( spODataEntityArray ( Item ));","title":"Entity Merging"},{"location":"sp/docs/entity-merging/#pnpsp-entity-merging","text":"Sometimes when we make a query entity's data we would like then to immediately run other commands on the returned entity. To have data returned as its represending type we make use of the spODataEntity and spODataEntityArray parsers. The below approach works for all instance types such as List, Web, Item, or Field as examples.","title":"@pnp/sp - entity merging"},{"location":"sp/docs/entity-merging/#request-a-single-entity","text":"If we are loading a single entity we use the spODataEntity method. Here we show loading a list item using the Item class and a simple get query. import { sp , spODataEntity , Item } from \"@pnp/sp\" ; // interface defining the returned properites interface MyProps { Id : number ; } try { // get a list item laoded with data and merged into an instance of Item const item = await sp . web . lists . getByTitle ( \"ListTitle\" ). items . getById ( 1 ). get ( spODataEntity < Item , MyProps > ( Item )); // log the item id, all properties specified in MyProps will be type checked Logger . write ( `Item id: ${ item . Id } ` ); // now we can call update because we have an instance of the Item type to work with as well await item . update ({ Title : \"New title.\" , }); } catch ( e ) { Logger . error ( e ); }","title":"Request a single entity"},{"location":"sp/docs/entity-merging/#request-a-collection","text":"The same pattern works when requesting a collection of objects with the exception of using the spODataEntityArray method. import { sp , spODataEntityArray , Item } from \"@pnp/sp\" ; // interface defining the returned properites interface MyProps { Id : number ; Title : string ; } try { // get a list item laoded with data and merged into an instance of Item const items = await sp . web . lists . getByTitle ( \"ListTitle\" ). items . select ( \"Id\" , \"Title\" ). get ( spODataEntityArray < Item , MyProps > ( Item )); Logger . write ( `Item id: ${ items . length } ` ); Logger . write ( `Item id: ${ items [ 0 ]. Title } ` ); // now we can call update because we have an instance of the Item type to work with as well await items [ 0 ]. update ({ Title : \"New title.\" , }); } catch ( e ) { Logger . error ( e ); }","title":"Request a collection"},{"location":"sp/docs/entity-merging/#use-with-item-getpaged","text":"Added in 1.3.4 Starting with 1.3.4 you can now include entity merging in the getPaged command as shown below. This approach will work with any objects matching the required factory pattern. // create Item instances with the defined property Title const items = await sp . web . lists . getByTitle ( \"BigList\" ). items . select ( \"Title\" ). getPaged ( spODataEntityArray < Item , { Title : string } > ( Item )); console . log ( items . results . length ); // now invoke methods on the Item object const perms = await items . results [ 0 ]. getCurrentUserEffectivePermissions (); console . log ( JSON . stringify ( perms , null , 2 )); // you can also type the result slightly differently if you prefer this, but the results are the same functionally. const items2 = await sp . web . lists . getByTitle ( \"BigList\" ). items . select ( \"Title\" ). getPaged < ( Item & { Title : string })[] > ( spODataEntityArray ( Item ));","title":"Use with Item getPaged"},{"location":"sp/docs/features/","text":"@pnp/sp/features \u00b6 Features are used by SharePoint to package a set of functionality and either enable (activate) or disable (deactivate) that functionality based on requirements for a specific site. You can manage feature activation using the library as shown below. Note that the features collection only contains active features. List all Features \u00b6 import { sp } from \"@pnp/sp\" ; let web = sp . web ; // get all the active features web . features . get (). then ( f => { console . log ( f ); }); // select properties using odata operators web . features . select ( \"DisplayName\" , \"DefinitionId\" ). get (). then ( f => { console . log ( f ); }); // get a particular feature by id web . features . getById ( \"87294c72-f260-42f3-a41b-981a2ffce37a\" ). select ( \"DisplayName\" , \"DefinitionId\" ). get (). then ( f => { console . log ( f ); }); // get features using odata operators web . features . filter ( \"DisplayName eq 'MDSFeature'\" ). get (). then ( f => { console . log ( f ); }); Activate a Feature \u00b6 To activate a feature you must know the feature id. You can optionally force activation - if you aren't sure don't use force. import { sp } from \"@pnp/sp\" ; let web = sp . web ; // activate the minimum download strategy feature web . features . add ( \"87294c72-f260-42f3-a41b-981a2ffce37a\" ). then ( f => { console . log ( f ); }); Deactivate a Feature \u00b6 import { sp } from \"@pnp/sp\" ; let web = sp . web ; web . features . remove ( \"87294c72-f260-42f3-a41b-981a2ffce37a\" ). then ( f => { console . log ( f ); }); // you can also deactivate a feature but going through the collection's remove method is faster web . features . getById ( \"87294c72-f260-42f3-a41b-981a2ffce37a\" ). deactivate (). then ( f => { console . log ( f ); });","title":"Features"},{"location":"sp/docs/features/#pnpspfeatures","text":"Features are used by SharePoint to package a set of functionality and either enable (activate) or disable (deactivate) that functionality based on requirements for a specific site. You can manage feature activation using the library as shown below. Note that the features collection only contains active features.","title":"@pnp/sp/features"},{"location":"sp/docs/features/#list-all-features","text":"import { sp } from \"@pnp/sp\" ; let web = sp . web ; // get all the active features web . features . get (). then ( f => { console . log ( f ); }); // select properties using odata operators web . features . select ( \"DisplayName\" , \"DefinitionId\" ). get (). then ( f => { console . log ( f ); }); // get a particular feature by id web . features . getById ( \"87294c72-f260-42f3-a41b-981a2ffce37a\" ). select ( \"DisplayName\" , \"DefinitionId\" ). get (). then ( f => { console . log ( f ); }); // get features using odata operators web . features . filter ( \"DisplayName eq 'MDSFeature'\" ). get (). then ( f => { console . log ( f ); });","title":"List all Features"},{"location":"sp/docs/features/#activate-a-feature","text":"To activate a feature you must know the feature id. You can optionally force activation - if you aren't sure don't use force. import { sp } from \"@pnp/sp\" ; let web = sp . web ; // activate the minimum download strategy feature web . features . add ( \"87294c72-f260-42f3-a41b-981a2ffce37a\" ). then ( f => { console . log ( f ); });","title":"Activate a Feature"},{"location":"sp/docs/features/#deactivate-a-feature","text":"import { sp } from \"@pnp/sp\" ; let web = sp . web ; web . features . remove ( \"87294c72-f260-42f3-a41b-981a2ffce37a\" ). then ( f => { console . log ( f ); }); // you can also deactivate a feature but going through the collection's remove method is faster web . features . getById ( \"87294c72-f260-42f3-a41b-981a2ffce37a\" ). deactivate (). then ( f => { console . log ( f ); });","title":"Deactivate a Feature"},{"location":"sp/docs/fields/","text":"@pnp/sp/fields \u00b6 Fields allow you to store typed information within a SharePoint list. There are many types of fields and the library seeks to simplify working with the most common types. Fields exist in both site collections (site columns) or lists (list columns) and you can add/modify/delete them at either of these levels. Get Fields \u00b6 import { sp } from \"@pnp/sp\" ; let web = sp . web ; // get all the fields in a web web . fields . get (). then ( f => { console . log ( f ); }); // you can use odata operators on the fields collection web . fields . select ( \"Title\" , \"InternalName\" , \"TypeAsString\" ). top ( 10 ). orderBy ( \"Id\" ). get (). then ( f => { console . log ( f ); }); // get all the available fields in a web (includes parent web's fields) web . availablefields . get (). then ( f => { console . log ( f ); }); // get the fields in a list web . lists . getByTitle ( \"MyList\" ). fields . get (). then ( f => { console . log ( f ); }); // you can also get individual fields using getById, getByTitle, or getByInternalNameOrTitle web . fields . getById ( \"dee9c205-2537-44d6-94e2-7c957e6ebe6e\" ). get (). then ( f => { console . log ( f ); }); web . fields . getByTitle ( \"MyField4\" ). get (). then ( f => { console . log ( f ); }); web . fields . getByInternalNameOrTitle ( \"MyField4\" ). get (). then ( f => { console . log ( f ); }); Filtering Fields \u00b6 Sometimes you only want a subset of fields from the collection. Below are some examples of using the filter operator with the fields collection. import { sp } from '@pnp/sp' ; const list = sp . web . lists . getByTitle ( 'Custom' ); // Fields which can be updated const filter1 = `Hidden eq false and ReadOnlyField eq false` ; list . fields . select ( 'InternalName' ). filter ( filter1 ). get (). then ( fields => { console . log ( `Can be updated: ${ fields . map ( f => f . InternalName ). join ( ', ' ) } ` ); // Title, ...Custom, ContentType, Attachments }); // Only custom field const filter2 = `Hidden eq false and CanBeDeleted eq true` ; list . fields . select ( 'InternalName' ). filter ( filter2 ). get (). then ( fields => { console . log ( `Custom fields: ${ fields . map ( f => f . InternalName ). join ( ', ' ) } ` ); // ...Custom }); // Application specific fields const includeFields = [ 'Title' , 'Author' , 'Editor' , 'Modified' , 'Created' ]; const filter3 = `Hidden eq false and (ReadOnlyField eq false or ( ${ includeFields . map ( field => `InternalName eq ' ${ field } '` ). join ( ' or ' ) } ))` ; list . fields . select ( 'InternalName' ). filter ( filter3 ). get (). then ( fields => { console . log ( `Application specific: ${ fields . map ( f => f . InternalName ). join ( ', ' ) } ` ); // Title, ...Custom, ContentType, Modified, Created, Author, Editor, Attachments }); // Fields in a view list . defaultView . fields . select ( 'Items' ). get (). then ( f => { const fields = ( f as any ). Items . results || ( f as any ). Items ; console . log ( `Fields in a view: ${ fields . join ( ', ' ) } ` ); }); Add Fields \u00b6 You can add fields using the add, createFieldAsXml, or one of the type specific methods. Functionally there is no difference, however one method may be easier given a certain scenario. import { sp } from \"@pnp/sp\" ; let web = sp . web ; // if you use add you _must_ include the correct FieldTypeKind in the extended properties web . fields . add ( \"MyField1\" , \"SP.FieldText\" , { Group : \"~Example\" , FieldTypeKind : 2 , Filterable : true , Hidden : false , EnforceUniqueValues : true , }). then ( f => { console . log ( f ); }); // you can also use the addText or any of the other type specific methods on the collection web . fields . addText ( \"MyField2\" , 75 , { Group : \"~Example\" }). then ( f => { console . log ( f ); }); // if you have the field schema (for example from an old elements file) you can use createFieldAsXml let xml = `` ; web . fields . createFieldAsXml ( xml ). then ( f => { console . log ( f ); }); // the same operations work on a list's fields collection web . lists . getByTitle ( \"MyList\" ). fields . addText ( \"MyField5\" , 100 ). then ( f => { console . log ( f ); }); // Create a lookup field, and a dependent lookup field web . lists . getByTitle ( \"MyList\" ). fields . addLookup ( \"MyLookup\" , \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\" , \"MyLookupTargetField\" ). then ( f => { console . log ( f ); // Create the dependent lookup field return web . lists . getByTitle ( \"MyList\" ). fields . addDependentLookupField ( \"MyLookup_ID\" , f . Id , \"ID\" ); }). then ( fDep => { console . log ( fDep ); }); Adding Multiline Text Fields with FullHtml \u00b6 Because the RichTextMode property is not exposed to the clients we cannot set this value via the API directly. The work around is to use the createFieldAsXml method as shown below import { sp } from \"@pnp/sp\" ; let web = sp . web ; const fieldAddResult = await web . fields . createFieldAsXml ( `` ); Update a Field \u00b6 You can also update the properties of a field in both webs and lists, but not all properties are able to be updated after creation. You can review this list for details. import { sp } from \"@pnp/sp\" ; let web = sp . web ; web . fields . getByTitle ( \"MyField4\" ). update ({ Description : \"A new description\" , }). then ( f => { console . log ( f ); }); Update a Url/Picture Field \u00b6 When updating a URL or Picture field you need to include the __metadata descriptor as shown below. import { sp } from \"@pnp/sp\" ; const data = { \"My_Field_Name\" : { \"__metadata\" : { \"type\" : \"SP.FieldUrlValue\" }, \"Description\" : \"A Pretty picture\" , \"Url\" : \"https://tenant.sharepoint.com/sites/dev/Style%20Library/DSC_0024.JPG\" , }, }; await sp . web . lists . getByTitle ( \"MyListTitle\" ). items . getById ( 1 ). update ( data ); Delete a Field \u00b6 import { sp } from \"@pnp/sp\" ; let web = sp . web ; web . fields . getByTitle ( \"MyField4\" ). delete (). then ( f => { console . log ( f ); });","title":"Fields"},{"location":"sp/docs/fields/#pnpspfields","text":"Fields allow you to store typed information within a SharePoint list. There are many types of fields and the library seeks to simplify working with the most common types. Fields exist in both site collections (site columns) or lists (list columns) and you can add/modify/delete them at either of these levels.","title":"@pnp/sp/fields"},{"location":"sp/docs/fields/#get-fields","text":"import { sp } from \"@pnp/sp\" ; let web = sp . web ; // get all the fields in a web web . fields . get (). then ( f => { console . log ( f ); }); // you can use odata operators on the fields collection web . fields . select ( \"Title\" , \"InternalName\" , \"TypeAsString\" ). top ( 10 ). orderBy ( \"Id\" ). get (). then ( f => { console . log ( f ); }); // get all the available fields in a web (includes parent web's fields) web . availablefields . get (). then ( f => { console . log ( f ); }); // get the fields in a list web . lists . getByTitle ( \"MyList\" ). fields . get (). then ( f => { console . log ( f ); }); // you can also get individual fields using getById, getByTitle, or getByInternalNameOrTitle web . fields . getById ( \"dee9c205-2537-44d6-94e2-7c957e6ebe6e\" ). get (). then ( f => { console . log ( f ); }); web . fields . getByTitle ( \"MyField4\" ). get (). then ( f => { console . log ( f ); }); web . fields . getByInternalNameOrTitle ( \"MyField4\" ). get (). then ( f => { console . log ( f ); });","title":"Get Fields"},{"location":"sp/docs/fields/#filtering-fields","text":"Sometimes you only want a subset of fields from the collection. Below are some examples of using the filter operator with the fields collection. import { sp } from '@pnp/sp' ; const list = sp . web . lists . getByTitle ( 'Custom' ); // Fields which can be updated const filter1 = `Hidden eq false and ReadOnlyField eq false` ; list . fields . select ( 'InternalName' ). filter ( filter1 ). get (). then ( fields => { console . log ( `Can be updated: ${ fields . map ( f => f . InternalName ). join ( ', ' ) } ` ); // Title, ...Custom, ContentType, Attachments }); // Only custom field const filter2 = `Hidden eq false and CanBeDeleted eq true` ; list . fields . select ( 'InternalName' ). filter ( filter2 ). get (). then ( fields => { console . log ( `Custom fields: ${ fields . map ( f => f . InternalName ). join ( ', ' ) } ` ); // ...Custom }); // Application specific fields const includeFields = [ 'Title' , 'Author' , 'Editor' , 'Modified' , 'Created' ]; const filter3 = `Hidden eq false and (ReadOnlyField eq false or ( ${ includeFields . map ( field => `InternalName eq ' ${ field } '` ). join ( ' or ' ) } ))` ; list . fields . select ( 'InternalName' ). filter ( filter3 ). get (). then ( fields => { console . log ( `Application specific: ${ fields . map ( f => f . InternalName ). join ( ', ' ) } ` ); // Title, ...Custom, ContentType, Modified, Created, Author, Editor, Attachments }); // Fields in a view list . defaultView . fields . select ( 'Items' ). get (). then ( f => { const fields = ( f as any ). Items . results || ( f as any ). Items ; console . log ( `Fields in a view: ${ fields . join ( ', ' ) } ` ); });","title":"Filtering Fields"},{"location":"sp/docs/fields/#add-fields","text":"You can add fields using the add, createFieldAsXml, or one of the type specific methods. Functionally there is no difference, however one method may be easier given a certain scenario. import { sp } from \"@pnp/sp\" ; let web = sp . web ; // if you use add you _must_ include the correct FieldTypeKind in the extended properties web . fields . add ( \"MyField1\" , \"SP.FieldText\" , { Group : \"~Example\" , FieldTypeKind : 2 , Filterable : true , Hidden : false , EnforceUniqueValues : true , }). then ( f => { console . log ( f ); }); // you can also use the addText or any of the other type specific methods on the collection web . fields . addText ( \"MyField2\" , 75 , { Group : \"~Example\" }). then ( f => { console . log ( f ); }); // if you have the field schema (for example from an old elements file) you can use createFieldAsXml let xml = `` ; web . fields . createFieldAsXml ( xml ). then ( f => { console . log ( f ); }); // the same operations work on a list's fields collection web . lists . getByTitle ( \"MyList\" ). fields . addText ( \"MyField5\" , 100 ). then ( f => { console . log ( f ); }); // Create a lookup field, and a dependent lookup field web . lists . getByTitle ( \"MyList\" ). fields . addLookup ( \"MyLookup\" , \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\" , \"MyLookupTargetField\" ). then ( f => { console . log ( f ); // Create the dependent lookup field return web . lists . getByTitle ( \"MyList\" ). fields . addDependentLookupField ( \"MyLookup_ID\" , f . Id , \"ID\" ); }). then ( fDep => { console . log ( fDep ); });","title":"Add Fields"},{"location":"sp/docs/fields/#adding-multiline-text-fields-with-fullhtml","text":"Because the RichTextMode property is not exposed to the clients we cannot set this value via the API directly. The work around is to use the createFieldAsXml method as shown below import { sp } from \"@pnp/sp\" ; let web = sp . web ; const fieldAddResult = await web . fields . createFieldAsXml ( `` );","title":"Adding Multiline Text Fields with FullHtml"},{"location":"sp/docs/fields/#update-a-field","text":"You can also update the properties of a field in both webs and lists, but not all properties are able to be updated after creation. You can review this list for details. import { sp } from \"@pnp/sp\" ; let web = sp . web ; web . fields . getByTitle ( \"MyField4\" ). update ({ Description : \"A new description\" , }). then ( f => { console . log ( f ); });","title":"Update a Field"},{"location":"sp/docs/fields/#update-a-urlpicture-field","text":"When updating a URL or Picture field you need to include the __metadata descriptor as shown below. import { sp } from \"@pnp/sp\" ; const data = { \"My_Field_Name\" : { \"__metadata\" : { \"type\" : \"SP.FieldUrlValue\" }, \"Description\" : \"A Pretty picture\" , \"Url\" : \"https://tenant.sharepoint.com/sites/dev/Style%20Library/DSC_0024.JPG\" , }, }; await sp . web . lists . getByTitle ( \"MyListTitle\" ). items . getById ( 1 ). update ( data );","title":"Update a Url/Picture Field"},{"location":"sp/docs/fields/#delete-a-field","text":"import { sp } from \"@pnp/sp\" ; let web = sp . web ; web . fields . getByTitle ( \"MyField4\" ). delete (). then ( f => { console . log ( f ); });","title":"Delete a Field"},{"location":"sp/docs/files/","text":"@pnp/sp/files \u00b6 One of the more challenging tasks on the client side is working with SharePoint files, especially if they are large files. We have added some methods to the library to help and their use is outlined below. Reading Files \u00b6 Reading files from the client using REST is covered in the below examples. The important thing to remember is choosing which format you want the file in so you can appropriately process it. You can retrieve a file as Blob, Buffer, JSON, or Text. If you have a special requirement you could also write your own parser . import { sp } from \"@pnp/sp\" ; sp . web . getFileByServerRelativeUrl ( \"/sites/dev/documents/file.avi\" ). getBlob (). then (( blob : Blob ) => {}); sp . web . getFileByServerRelativeUrl ( \"/sites/dev/documents/file.avi\" ). getBuffer (). then (( buffer : ArrayBuffer ) => {}); sp . web . getFileByServerRelativeUrl ( \"/sites/dev/documents/file.json\" ). getJSON (). then (( json : any ) => {}); sp . web . getFileByServerRelativeUrl ( \"/sites/dev/documents/file.txt\" ). getText (). then (( text : string ) => {}); // all of these also work from a file object no matter how you access it sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/documents\" ). files . getByName ( \"file.txt\" ). getText (). then (( text : string ) => {}); Adding Files \u00b6 Likewise you can add files using one of two methods, add or addChunked. The second is appropriate for larger files, generally larger than 10 MB but this may differ based on your bandwidth/latency so you can adjust the code to use the chunked method. The below example shows getting the file object from an input and uploading it to SharePoint, choosing the upload method based on file size. declare var require : ( s : string ) => any ; import { ConsoleListener , Web , Logger , LogLevel , ODataRaw } from \"@pnp/sp\" ; import { auth } from \"./auth\" ; let $ = require ( \"jquery\" ); let siteUrl = \"https://mytenant.sharepoint.com/sites/dev\" ; // comment this out for non-node execution // auth(siteUrl); Logger . subscribe ( new ConsoleListener ()); Logger . activeLogLevel = LogLevel . Verbose ; let web = new Web ( siteUrl ); $ (() => { $ ( \"#testingdiv\" ). append ( \"\" ); $ ( \"#thebuttontodoit\" ). on ( 'click' , ( e ) => { e . preventDefault (); let input = < HTMLInputElement > document . getElementById ( \"thefileinput\" ); let file = input . files [ 0 ]; // you can adjust this number to control what size files are uploaded in chunks if ( file . size <= 10485760 ) { // small upload web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared%20Documents/test/\" ). files . add ( file . name , file , true ). then ( _ => Logger . write ( \"done\" )); } else { // large upload web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared%20Documents/test/\" ). files . addChunked ( file . name , file , data => { Logger . log ({ data : data , level : LogLevel.Verbose , message : \"progress\" }); }, true ). then ( _ => Logger . write ( \"done!\" )); } }); }); Setting Associated Item Values \u00b6 You can also update the file properties of a newly uploaded file using code similar to the below snippet: import { sp } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared%20Documents/test/\" ). files . add ( file . name , file , true ). then ( f => { f . file . getItem (). then ( item => { item . update ({ Title : \"A Title\" , OtherField : \"My Other Value\" }); }); }); Update File Content \u00b6 You can of course use similar methods to update existing files as shown below: import { sp } from \"@pnp/sp\" ; sp . web . getFileByServerRelativeUrl ( \"/sites/dev/documents/test.txt\" ). setContent ( \"New string content for the file.\" ); sp . web . getFileByServerRelativeUrl ( \"/sites/dev/documents/test.mp4\" ). setContentChunked ( file ); Check in, Check out, and Approve & Deny \u00b6 The library provides helper methods for checking in, checking out, and approving files. Examples of these methods are shown below. Check In \u00b6 Check in takes two optional arguments, comment and check in type. import { sp , CheckinType } from \"@pnp/sp\" ; // default options with empty comment and CheckinType.Major sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). checkin (). then ( _ => { console . log ( \"File checked in!\" ); }); // supply a comment (< 1024 chars) and using default check in type CheckinType.Major sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). checkin ( \"A comment\" ). then ( _ => { console . log ( \"File checked in!\" ); }); // Supply both comment and check in type sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). checkin ( \"A comment\" , CheckinType . Overwrite ). then ( _ => { console . log ( \"File checked in!\" ); }); Check Out \u00b6 Check out takes no arguments. import { sp } from \"@pnp/sp\" ; sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). checkout (). then ( _ => { console . log ( \"File checked out!\" ); }); Approve and Deny \u00b6 You can also approve or deny files in libraries that use approval. Approve takes a single required argument of comment, the comment is optional for deny. import { sp } from \"@pnp/sp\" ; sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). approve ( \"Approval Comment\" ). then ( _ => { console . log ( \"File approved!\" ); }); // deny with no comment sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). deny (). then ( _ => { console . log ( \"File denied!\" ); }); // deny with a supplied comment. sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). deny ( \"Deny comment\" ). then ( _ => { console . log ( \"File denied!\" ); }); Publish and Unpublish \u00b6 You can both publish and unpublish a file using the library. Both methods take an optional comment argument. import { sp } from \"@pnp/sp\" ; // publish with no comment sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). publish (). then ( _ => { console . log ( \"File published!\" ); }); // publish with a supplied comment. sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). publish ( \"Publish comment\" ). then ( _ => { console . log ( \"File published!\" ); }); // unpublish with no comment sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). unpublish (). then ( _ => { console . log ( \"File unpublished!\" ); }); // unpublish with a supplied comment. sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). unpublish ( \"Unpublish comment\" ). then ( _ => { console . log ( \"File unpublished!\" ); }); Advanced Upload Options \u00b6 Both the addChunked and setContentChunked methods support options beyond just supplying the file content. progress function \u00b6 A method that is called each time a chunk is uploaded and provides enough information to report progress or update a progress bar easily. The method has the signature: (data: ChunkedFileUploadProgressData) => void The data interface is: export interface ChunkedFileUploadProgressData { stage : \"starting\" | \"continue\" | \"finishing\" ; blockNumber : number ; totalBlocks : number ; chunkSize : number ; currentPointer : number ; fileSize : number ; } chunkSize \u00b6 This property controls the size of the individual chunks and is defaulted to 10485760 bytes (10 MB). You can adjust this based on your bandwidth needs - especially if writing code for mobile uploads or you are seeing frequent timeouts. getItem \u00b6 This method allows you to get the item associated with this file. You can optionally specify one or more select fields. The result will be merged with a new Item instance so you will have both the returned property values and chaining ability in a single object. import { sp } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). getItem (). then ( item => { console . log ( item ); }); sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). getItem ( \"Title\" , \"Modified\" ). then ( item => { console . log ( item ); }); sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). getItem (). then ( item => { // you can also chain directly off this item instance item . getCurrentUserEffectivePermissions (). then ( perms => { console . log ( perms ); }); }); You can also supply a generic typing parameter and the resulting type will be a union type of Item and the generic type parameter. This allows you to have proper intellisense and type checking. import { sp } from \"@pnp/sp\" ; // also supports typing the objects so your type will be a union type sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). getItem < { Id : number , Title : string } > ( \"Id\" , \"Title\" ). then ( item => { // You get intellisense and proper typing of the returned object console . log ( `Id: ${ item . Id } -- ${ item . Title } ` ); // You can also chain directly off this item instance item . getCurrentUserEffectivePermissions (). then ( perms => { console . log ( perms ); }); });","title":"Files"},{"location":"sp/docs/files/#pnpspfiles","text":"One of the more challenging tasks on the client side is working with SharePoint files, especially if they are large files. We have added some methods to the library to help and their use is outlined below.","title":"@pnp/sp/files"},{"location":"sp/docs/files/#reading-files","text":"Reading files from the client using REST is covered in the below examples. The important thing to remember is choosing which format you want the file in so you can appropriately process it. You can retrieve a file as Blob, Buffer, JSON, or Text. If you have a special requirement you could also write your own parser . import { sp } from \"@pnp/sp\" ; sp . web . getFileByServerRelativeUrl ( \"/sites/dev/documents/file.avi\" ). getBlob (). then (( blob : Blob ) => {}); sp . web . getFileByServerRelativeUrl ( \"/sites/dev/documents/file.avi\" ). getBuffer (). then (( buffer : ArrayBuffer ) => {}); sp . web . getFileByServerRelativeUrl ( \"/sites/dev/documents/file.json\" ). getJSON (). then (( json : any ) => {}); sp . web . getFileByServerRelativeUrl ( \"/sites/dev/documents/file.txt\" ). getText (). then (( text : string ) => {}); // all of these also work from a file object no matter how you access it sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/documents\" ). files . getByName ( \"file.txt\" ). getText (). then (( text : string ) => {});","title":"Reading Files"},{"location":"sp/docs/files/#adding-files","text":"Likewise you can add files using one of two methods, add or addChunked. The second is appropriate for larger files, generally larger than 10 MB but this may differ based on your bandwidth/latency so you can adjust the code to use the chunked method. The below example shows getting the file object from an input and uploading it to SharePoint, choosing the upload method based on file size. declare var require : ( s : string ) => any ; import { ConsoleListener , Web , Logger , LogLevel , ODataRaw } from \"@pnp/sp\" ; import { auth } from \"./auth\" ; let $ = require ( \"jquery\" ); let siteUrl = \"https://mytenant.sharepoint.com/sites/dev\" ; // comment this out for non-node execution // auth(siteUrl); Logger . subscribe ( new ConsoleListener ()); Logger . activeLogLevel = LogLevel . Verbose ; let web = new Web ( siteUrl ); $ (() => { $ ( \"#testingdiv\" ). append ( \"\" ); $ ( \"#thebuttontodoit\" ). on ( 'click' , ( e ) => { e . preventDefault (); let input = < HTMLInputElement > document . getElementById ( \"thefileinput\" ); let file = input . files [ 0 ]; // you can adjust this number to control what size files are uploaded in chunks if ( file . size <= 10485760 ) { // small upload web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared%20Documents/test/\" ). files . add ( file . name , file , true ). then ( _ => Logger . write ( \"done\" )); } else { // large upload web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared%20Documents/test/\" ). files . addChunked ( file . name , file , data => { Logger . log ({ data : data , level : LogLevel.Verbose , message : \"progress\" }); }, true ). then ( _ => Logger . write ( \"done!\" )); } }); });","title":"Adding Files"},{"location":"sp/docs/files/#setting-associated-item-values","text":"You can also update the file properties of a newly uploaded file using code similar to the below snippet: import { sp } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared%20Documents/test/\" ). files . add ( file . name , file , true ). then ( f => { f . file . getItem (). then ( item => { item . update ({ Title : \"A Title\" , OtherField : \"My Other Value\" }); }); });","title":"Setting Associated Item Values"},{"location":"sp/docs/files/#update-file-content","text":"You can of course use similar methods to update existing files as shown below: import { sp } from \"@pnp/sp\" ; sp . web . getFileByServerRelativeUrl ( \"/sites/dev/documents/test.txt\" ). setContent ( \"New string content for the file.\" ); sp . web . getFileByServerRelativeUrl ( \"/sites/dev/documents/test.mp4\" ). setContentChunked ( file );","title":"Update File Content"},{"location":"sp/docs/files/#check-in-check-out-and-approve-deny","text":"The library provides helper methods for checking in, checking out, and approving files. Examples of these methods are shown below.","title":"Check in, Check out, and Approve & Deny"},{"location":"sp/docs/files/#check-in","text":"Check in takes two optional arguments, comment and check in type. import { sp , CheckinType } from \"@pnp/sp\" ; // default options with empty comment and CheckinType.Major sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). checkin (). then ( _ => { console . log ( \"File checked in!\" ); }); // supply a comment (< 1024 chars) and using default check in type CheckinType.Major sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). checkin ( \"A comment\" ). then ( _ => { console . log ( \"File checked in!\" ); }); // Supply both comment and check in type sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). checkin ( \"A comment\" , CheckinType . Overwrite ). then ( _ => { console . log ( \"File checked in!\" ); });","title":"Check In"},{"location":"sp/docs/files/#check-out","text":"Check out takes no arguments. import { sp } from \"@pnp/sp\" ; sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). checkout (). then ( _ => { console . log ( \"File checked out!\" ); });","title":"Check Out"},{"location":"sp/docs/files/#approve-and-deny","text":"You can also approve or deny files in libraries that use approval. Approve takes a single required argument of comment, the comment is optional for deny. import { sp } from \"@pnp/sp\" ; sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). approve ( \"Approval Comment\" ). then ( _ => { console . log ( \"File approved!\" ); }); // deny with no comment sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). deny (). then ( _ => { console . log ( \"File denied!\" ); }); // deny with a supplied comment. sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). deny ( \"Deny comment\" ). then ( _ => { console . log ( \"File denied!\" ); });","title":"Approve and Deny"},{"location":"sp/docs/files/#publish-and-unpublish","text":"You can both publish and unpublish a file using the library. Both methods take an optional comment argument. import { sp } from \"@pnp/sp\" ; // publish with no comment sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). publish (). then ( _ => { console . log ( \"File published!\" ); }); // publish with a supplied comment. sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). publish ( \"Publish comment\" ). then ( _ => { console . log ( \"File published!\" ); }); // unpublish with no comment sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). unpublish (). then ( _ => { console . log ( \"File unpublished!\" ); }); // unpublish with a supplied comment. sp . web . getFileByServerRelativeUrl ( \"/sites/dev/shared documents/file.txt\" ). unpublish ( \"Unpublish comment\" ). then ( _ => { console . log ( \"File unpublished!\" ); });","title":"Publish and Unpublish"},{"location":"sp/docs/files/#advanced-upload-options","text":"Both the addChunked and setContentChunked methods support options beyond just supplying the file content.","title":"Advanced Upload Options"},{"location":"sp/docs/files/#progress-function","text":"A method that is called each time a chunk is uploaded and provides enough information to report progress or update a progress bar easily. The method has the signature: (data: ChunkedFileUploadProgressData) => void The data interface is: export interface ChunkedFileUploadProgressData { stage : \"starting\" | \"continue\" | \"finishing\" ; blockNumber : number ; totalBlocks : number ; chunkSize : number ; currentPointer : number ; fileSize : number ; }","title":"progress function"},{"location":"sp/docs/files/#chunksize","text":"This property controls the size of the individual chunks and is defaulted to 10485760 bytes (10 MB). You can adjust this based on your bandwidth needs - especially if writing code for mobile uploads or you are seeing frequent timeouts.","title":"chunkSize"},{"location":"sp/docs/files/#getitem","text":"This method allows you to get the item associated with this file. You can optionally specify one or more select fields. The result will be merged with a new Item instance so you will have both the returned property values and chaining ability in a single object. import { sp } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). getItem (). then ( item => { console . log ( item ); }); sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). getItem ( \"Title\" , \"Modified\" ). then ( item => { console . log ( item ); }); sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). getItem (). then ( item => { // you can also chain directly off this item instance item . getCurrentUserEffectivePermissions (). then ( perms => { console . log ( perms ); }); }); You can also supply a generic typing parameter and the resulting type will be a union type of Item and the generic type parameter. This allows you to have proper intellisense and type checking. import { sp } from \"@pnp/sp\" ; // also supports typing the objects so your type will be a union type sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). getItem < { Id : number , Title : string } > ( \"Id\" , \"Title\" ). then ( item => { // You get intellisense and proper typing of the returned object console . log ( `Id: ${ item . Id } -- ${ item . Title } ` ); // You can also chain directly off this item instance item . getCurrentUserEffectivePermissions (). then ( perms => { console . log ( perms ); }); });","title":"getItem"},{"location":"sp/docs/items/","text":"@pnp/sp/items \u00b6 GET \u00b6 Getting items from a list is one of the basic actions that most applications require. This is made easy through the library and the following examples demonstrate these actions. Basic Get \u00b6 import { sp } from \"@pnp/sp\" ; // get all the items from a list sp . web . lists . getByTitle ( \"My List\" ). items . get (). then (( items : any []) => { console . log ( items ); }); // get a specific item by id sp . web . lists . getByTitle ( \"My List\" ). items . getById ( 1 ). get (). then (( item : any ) => { console . log ( item ); }); // use odata operators for more efficient queries sp . web . lists . getByTitle ( \"My List\" ). items . select ( \"Title\" , \"Description\" ). top ( 5 ). orderBy ( \"Modified\" , true ). get (). then (( items : any []) => { console . log ( items ); }); Get Paged Items \u00b6 Working with paging can be a challenge as it is based on skip tokens and item ids, something that is hard to guess at runtime. To simplify things you can use the getPaged method on the Items class to assist. Note that there isn't a way to move backwards in the collection, this is by design. The pattern you should use to support backwards navigation in the results is to cache the results into a local array and use the standard array operators to get previous pages. Alternatively you can append the results to the UI, but this can have performance impact for large result sets. import { sp } from \"@pnp/sp\" ; // basic case to get paged items form a list let items = await sp . web . lists . getByTitle ( \"BigList\" ). items . getPaged (); // you can also provide a type for the returned values instead of any let items = await sp . web . lists . getByTitle ( \"BigList\" ). items . getPaged < { Title : string }[] > (); // the query also works with select to choose certain fields and top to set the page size let items = await sp . web . lists . getByTitle ( \"BigList\" ). items . select ( \"Title\" , \"Description\" ). top ( 50 ). getPaged < { Title : string }[] > (); // the results object will have two properties and one method: // the results property will be an array of the items returned if ( items . results . length > 0 ) { console . log ( \"We got results!\" ); for ( let i = 0 ; i < items . results . length ; i ++ ) { // type checking works here if we specify the return type console . log ( items . results [ i ]. Title ); } } // the hasNext property is used with the getNext method to handle paging // hasNext will be true so long as there are additional results if ( items . hasNext ) { // this will carry over the type specified in the original query for the results array items = await items . getNext (); console . log ( items . results . length ); } getListItemChangesSinceToken \u00b6 The GetListItemChangesSinceToken method allows clients to track changes on a list. Changes, including deleted items, are returned along with a token that represents the moment in time when those changes were requested. By including this token when you call GetListItemChangesSinceToken, the server looks for only those changes that have occurred since the token was generated. Sending a GetListItemChangesSinceToken request without including a token returns the list schema, the full list contents and a token. import { sp } from \"@pnp/sp\" ; // Using RowLimit. Enables paging let changes = await sp . web . lists . getByTitle ( \"BigList\" ). getListItemChangesSinceToken ({ RowLimit : '5' }); // Use QueryOptions to make a XML-style query. // Because it's XML we need to escape special characters // Instead of & we use & in the query let changes = await sp . web . lists . getByTitle ( \"BigList\" ). getListItemChangesSinceToken ({ QueryOptions : '' }); // Get everything. Using null with ChangeToken gets everything let changes = await sp . web . lists . getByTitle ( \"BigList\" ). getListItemChangesSinceToken ({ ChangeToken : null }); Get All Items \u00b6 Added in 1.0.2 Using the items collection's getAll method you can get all of the items in a list regardless of the size of the list. Sample usage is shown below. Only the odata operations top, select, and filter are supported. usingCaching and inBatch are ignored - you will need to handle caching the results on your own. This method will write a warning to the Logger and should not frequently be used. Instead the standard paging operations should be used. import { sp } from \"@pnp/sp\" ; // basic usage sp . web . lists . getByTitle ( \"BigList\" ). items . getAll (). then (( allItems : any []) => { // how many did we get console . log ( allItems . length ); }); // set page size sp . web . lists . getByTitle ( \"BigList\" ). items . getAll ( 4000 ). then (( allItems : any []) => { // how many did we get console . log ( allItems . length ); }); // use select and top. top will set page size and override the any value passed to getAll sp . web . lists . getByTitle ( \"BigList\" ). items . select ( \"Title\" ). top ( 4000 ). getAll (). then (( allItems : any []) => { // how many did we get console . log ( allItems . length ); }); // we can also use filter as a supported odata operation, but this will likely fail on large lists sp . web . lists . getByTitle ( \"BigList\" ). items . select ( \"Title\" ). filter ( \"Title eq 'Test'\" ). getAll (). then (( allItems : any []) => { // how many did we get console . log ( allItems . length ); }); Retrieving Lookup Fields \u00b6 When working with lookup fields you need to use the expand operator along with select to get the related fields from the lookup column. This works for both the items collection and item instances. import { sp } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( \"LookupList\" ). items . select ( \"Title\" , \"Lookup/Title\" , \"Lookup/ID\" ). expand ( \"Lookup\" ). get (). then (( items : any []) => { console . log ( items ); }); sp . web . lists . getByTitle ( \"LookupList\" ). items . getById ( 1 ). select ( \"Title\" , \"Lookup/Title\" , \"Lookup/ID\" ). expand ( \"Lookup\" ). get (). then (( item : any ) => { console . log ( item ); }); Retrieving PublishingPageImage \u00b6 The PublishingPageImage and some other publishing-related fields aren't stored in normal fields, rather in the MetaInfo field. To get these values you need to use the technique shown below, and originally outlined in this thread . Note that a lot of information can be stored in this field so will pull back potentially a significant amount of data, so limit the rows as possible to aid performance. import { Web } from \"@pnp/sp\" ; const w = new Web ( \"https://{publishing site url}\" ); w . lists . getByTitle ( \"Pages\" ). items . select ( \"Title\" , \"FileRef\" , \"FieldValuesAsText/MetaInfo\" ) . expand ( \"FieldValuesAsText\" ) . get (). then ( r => { // look through the returned items. for ( var i = 0 ; i < r . length ; i ++ ) { // the title field value console . log ( r [ i ]. Title ); // find the value in the MetaInfo string using regex const matches = /PublishingPageImage:SW\\|(.*?)\\r\\n/ig . exec ( r [ i ]. FieldValuesAsText . MetaInfo ); if ( matches !== null && matches . length > 1 ) { // this wil be the value of the PublishingPageImage field console . log ( matches [ 1 ]); } } }). catch ( e => { console . error ( e ); }); Add Items \u00b6 There are several ways to add items to a list. The simplest just uses the add method of the items collection passing in the properties as a plain object. import { sp , ItemAddResult } from \"@pnp/sp\" ; // add an item to the list sp . web . lists . getByTitle ( \"My List\" ). items . add ({ Title : \"Title\" , Description : \"Description\" }). then (( iar : ItemAddResult ) => { console . log ( iar ); }); Content Type \u00b6 You can also set the content type id when you create an item as shown in the example below: import { sp } from \"@pnp/sp\" ; sp . web . lists . getById ( \"4D5A36EA-6E84-4160-8458-65C436DB765C\" ). items . add ({ Title : \"Test 1\" , ContentTypeId : \"0x01030058FD86C279252341AB303852303E4DAF\" }); User Fields \u00b6 There are two types of user fields, those that allow a single value and those that allow multiple. For both types, you first need to determine the Id field name, which you can do by doing a GET REST request on an existing item. Typically the value will be the user field internal name with \"Id\" appended. So in our example, we have two fields User1 and User2 so the Id fields are User1Id and User2Id. Next, you need to remember there are two types of user fields, those that take a single value and those that allow multiple - these are updated in different ways. For single value user fields you supply just the user's id. For multiple value fields, you need to supply an object with a \"results\" property and an array. Examples for both are shown below. import { sp } from \"@pnp/sp\" ; import { getGUID } from \"@pnp/core\" ; sp . web . lists . getByTitle ( \"PeopleFields\" ). items . add ({ Title : getGUID (), User1Id : 9 , // allows a single user User2Id : { results : [ 16 , 45 ] // allows multiple users } }). then ( i => { console . log ( i ); }); If you want to update or add user field values when using validateUpdateListItem you need to use the form shown below. You can specify multiple values in the array. import { sp } from \"@pnp/sp\" ; const result = await sp . web . lists . getByTitle ( \"UserFieldList\" ). items . getById ( 1 ). validateUpdateListItem ([{ FieldName : \"UserField\" , FieldValue : JSON.stringify ([{ \"Key\" : \"i:0#.f|membership|person@tenant.com\" }]), }, { FieldName : \"Title\" , FieldValue : \"Test - Updated\" , }]); Lookup Fields \u00b6 What is said for User Fields is, in general, relevant to Lookup Fields: - Lookup Field types: - Single-valued lookup - Multiple-valued lookup - Id suffix should be appended to the end of lookup's EntityPropertyName in payloads - Numeric Ids for lookups' items should be passed as values import { sp } from \"@pnp/sp\" ; import { getGUID } from \"@pnp/core\" ; sp . web . lists . getByTitle ( \"LookupFields\" ). items . add ({ Title : getGUID (), LookupFieldId : 2 , // allows a single lookup value MuptiLookupFieldId : { results : [ 1 , 56 ] // allows multiple lookup value } }). then ( console . log ). catch ( console . log ); Add Multiple Items \u00b6 import { sp } from \"@pnp/sp\" ; let list = sp . web . lists . getByTitle ( \"rapidadd\" ); list . getListItemEntityTypeFullName (). then ( entityTypeFullName => { let batch = sp . web . createBatch (); list . items . inBatch ( batch ). add ({ Title : \"Batch 6\" }, entityTypeFullName ). then ( b => { console . log ( b ); }); list . items . inBatch ( batch ). add ({ Title : \"Batch 7\" }, entityTypeFullName ). then ( b => { console . log ( b ); }); batch . execute (). then ( d => console . log ( \"Done\" )); }); Update \u00b6 The update method is very similar to the add method in that it takes a plain object representing the fields to update. The property names are the internal names of the fields. If you aren't sure you can always do a get request for an item in the list and see the field names that come back - you would use these same names to update the item. import { sp } from \"@pnp/sp\" ; let list = sp . web . lists . getByTitle ( \"MyList\" ); list . items . getById ( 1 ). update ({ Title : \"My New Title\" , Description : \"Here is a new description\" }). then ( i => { console . log ( i ); }); Getting and updating a collection using filter \u00b6 import { sp } from \"@pnp/sp\" ; // you are getting back a collection here sp . web . lists . getByTitle ( \"MyList\" ). items . top ( 1 ). filter ( \"Title eq 'A Title'\" ). get (). then (( items : any []) => { // see if we got something if ( items . length > 0 ) { sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( items [ 0 ]. Id ). update ({ Title : \"Updated Title\" , }). then ( result => { // here you will have updated the item console . log ( JSON . stringify ( result )); }); } }); Update Multiple Items \u00b6 This approach avoids multiple calls for the same list's entity type name. import { sp } from \"@pnp/sp\" ; let list = sp . web . lists . getByTitle ( \"rapidupdate\" ); list . getListItemEntityTypeFullName (). then ( entityTypeFullName => { let batch = sp . web . createBatch (); // note requirement of \"*\" eTag param - or use a specific eTag value as needed list . items . getById ( 1 ). inBatch ( batch ). update ({ Title : \"Batch 6\" }, \"*\" , entityTypeFullName ). then ( b => { console . log ( b ); }); list . items . getById ( 2 ). inBatch ( batch ). update ({ Title : \"Batch 7\" }, \"*\" , entityTypeFullName ). then ( b => { console . log ( b ); }); batch . execute (). then ( d => console . log ( \"Done\" )); }); Recycle \u00b6 Sending an item to the Recycle Bin is as simple as calling the .recycle method. import { sp } from \"@pnp/sp\" ; let list = sp . web . lists . getByTitle ( \"MyList\" ); list . items . getById ( 1 ). recycle (). then ( _ => {}); Delete \u00b6 Delete is as simple as calling the .delete method. It optionally takes an eTag if you need to manage concurrency. import { sp } from \"@pnp/sp\" ; let list = sp . web . lists . getByTitle ( \"MyList\" ); list . items . getById ( 1 ). delete (). then ( _ => {}); Resolving field names \u00b6 It's a very common mistake trying wrong field names in the requests. Field's EntityPropertyName value should be used. The easiest way to get know EntityPropertyName is to use the following snippet: import { sp } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( '[Lists_Title]' ) . fields . select ( 'Title, EntityPropertyName' ) . filter ( `Hidden eq false and Title eq '[Field's_Display_Name]'` ) . get () . then ( response => { console . log ( response . map ( field => { return { Title : field.Title , EntityPropertyName : field.EntityPropertyName }; })); }) . catch ( console . log ); Lookup fields' names should be ended with additional Id suffix. E.g. for Editor EntityPropertyName EditorId should be used.","title":"List Items"},{"location":"sp/docs/items/#pnpspitems","text":"","title":"@pnp/sp/items"},{"location":"sp/docs/items/#get","text":"Getting items from a list is one of the basic actions that most applications require. This is made easy through the library and the following examples demonstrate these actions.","title":"GET"},{"location":"sp/docs/items/#basic-get","text":"import { sp } from \"@pnp/sp\" ; // get all the items from a list sp . web . lists . getByTitle ( \"My List\" ). items . get (). then (( items : any []) => { console . log ( items ); }); // get a specific item by id sp . web . lists . getByTitle ( \"My List\" ). items . getById ( 1 ). get (). then (( item : any ) => { console . log ( item ); }); // use odata operators for more efficient queries sp . web . lists . getByTitle ( \"My List\" ). items . select ( \"Title\" , \"Description\" ). top ( 5 ). orderBy ( \"Modified\" , true ). get (). then (( items : any []) => { console . log ( items ); });","title":"Basic Get"},{"location":"sp/docs/items/#get-paged-items","text":"Working with paging can be a challenge as it is based on skip tokens and item ids, something that is hard to guess at runtime. To simplify things you can use the getPaged method on the Items class to assist. Note that there isn't a way to move backwards in the collection, this is by design. The pattern you should use to support backwards navigation in the results is to cache the results into a local array and use the standard array operators to get previous pages. Alternatively you can append the results to the UI, but this can have performance impact for large result sets. import { sp } from \"@pnp/sp\" ; // basic case to get paged items form a list let items = await sp . web . lists . getByTitle ( \"BigList\" ). items . getPaged (); // you can also provide a type for the returned values instead of any let items = await sp . web . lists . getByTitle ( \"BigList\" ). items . getPaged < { Title : string }[] > (); // the query also works with select to choose certain fields and top to set the page size let items = await sp . web . lists . getByTitle ( \"BigList\" ). items . select ( \"Title\" , \"Description\" ). top ( 50 ). getPaged < { Title : string }[] > (); // the results object will have two properties and one method: // the results property will be an array of the items returned if ( items . results . length > 0 ) { console . log ( \"We got results!\" ); for ( let i = 0 ; i < items . results . length ; i ++ ) { // type checking works here if we specify the return type console . log ( items . results [ i ]. Title ); } } // the hasNext property is used with the getNext method to handle paging // hasNext will be true so long as there are additional results if ( items . hasNext ) { // this will carry over the type specified in the original query for the results array items = await items . getNext (); console . log ( items . results . length ); }","title":"Get Paged Items"},{"location":"sp/docs/items/#getlistitemchangessincetoken","text":"The GetListItemChangesSinceToken method allows clients to track changes on a list. Changes, including deleted items, are returned along with a token that represents the moment in time when those changes were requested. By including this token when you call GetListItemChangesSinceToken, the server looks for only those changes that have occurred since the token was generated. Sending a GetListItemChangesSinceToken request without including a token returns the list schema, the full list contents and a token. import { sp } from \"@pnp/sp\" ; // Using RowLimit. Enables paging let changes = await sp . web . lists . getByTitle ( \"BigList\" ). getListItemChangesSinceToken ({ RowLimit : '5' }); // Use QueryOptions to make a XML-style query. // Because it's XML we need to escape special characters // Instead of & we use & in the query let changes = await sp . web . lists . getByTitle ( \"BigList\" ). getListItemChangesSinceToken ({ QueryOptions : '' }); // Get everything. Using null with ChangeToken gets everything let changes = await sp . web . lists . getByTitle ( \"BigList\" ). getListItemChangesSinceToken ({ ChangeToken : null });","title":"getListItemChangesSinceToken"},{"location":"sp/docs/items/#get-all-items","text":"Added in 1.0.2 Using the items collection's getAll method you can get all of the items in a list regardless of the size of the list. Sample usage is shown below. Only the odata operations top, select, and filter are supported. usingCaching and inBatch are ignored - you will need to handle caching the results on your own. This method will write a warning to the Logger and should not frequently be used. Instead the standard paging operations should be used. import { sp } from \"@pnp/sp\" ; // basic usage sp . web . lists . getByTitle ( \"BigList\" ). items . getAll (). then (( allItems : any []) => { // how many did we get console . log ( allItems . length ); }); // set page size sp . web . lists . getByTitle ( \"BigList\" ). items . getAll ( 4000 ). then (( allItems : any []) => { // how many did we get console . log ( allItems . length ); }); // use select and top. top will set page size and override the any value passed to getAll sp . web . lists . getByTitle ( \"BigList\" ). items . select ( \"Title\" ). top ( 4000 ). getAll (). then (( allItems : any []) => { // how many did we get console . log ( allItems . length ); }); // we can also use filter as a supported odata operation, but this will likely fail on large lists sp . web . lists . getByTitle ( \"BigList\" ). items . select ( \"Title\" ). filter ( \"Title eq 'Test'\" ). getAll (). then (( allItems : any []) => { // how many did we get console . log ( allItems . length ); });","title":"Get All Items"},{"location":"sp/docs/items/#retrieving-lookup-fields","text":"When working with lookup fields you need to use the expand operator along with select to get the related fields from the lookup column. This works for both the items collection and item instances. import { sp } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( \"LookupList\" ). items . select ( \"Title\" , \"Lookup/Title\" , \"Lookup/ID\" ). expand ( \"Lookup\" ). get (). then (( items : any []) => { console . log ( items ); }); sp . web . lists . getByTitle ( \"LookupList\" ). items . getById ( 1 ). select ( \"Title\" , \"Lookup/Title\" , \"Lookup/ID\" ). expand ( \"Lookup\" ). get (). then (( item : any ) => { console . log ( item ); });","title":"Retrieving Lookup Fields"},{"location":"sp/docs/items/#retrieving-publishingpageimage","text":"The PublishingPageImage and some other publishing-related fields aren't stored in normal fields, rather in the MetaInfo field. To get these values you need to use the technique shown below, and originally outlined in this thread . Note that a lot of information can be stored in this field so will pull back potentially a significant amount of data, so limit the rows as possible to aid performance. import { Web } from \"@pnp/sp\" ; const w = new Web ( \"https://{publishing site url}\" ); w . lists . getByTitle ( \"Pages\" ). items . select ( \"Title\" , \"FileRef\" , \"FieldValuesAsText/MetaInfo\" ) . expand ( \"FieldValuesAsText\" ) . get (). then ( r => { // look through the returned items. for ( var i = 0 ; i < r . length ; i ++ ) { // the title field value console . log ( r [ i ]. Title ); // find the value in the MetaInfo string using regex const matches = /PublishingPageImage:SW\\|(.*?)\\r\\n/ig . exec ( r [ i ]. FieldValuesAsText . MetaInfo ); if ( matches !== null && matches . length > 1 ) { // this wil be the value of the PublishingPageImage field console . log ( matches [ 1 ]); } } }). catch ( e => { console . error ( e ); });","title":"Retrieving PublishingPageImage"},{"location":"sp/docs/items/#add-items","text":"There are several ways to add items to a list. The simplest just uses the add method of the items collection passing in the properties as a plain object. import { sp , ItemAddResult } from \"@pnp/sp\" ; // add an item to the list sp . web . lists . getByTitle ( \"My List\" ). items . add ({ Title : \"Title\" , Description : \"Description\" }). then (( iar : ItemAddResult ) => { console . log ( iar ); });","title":"Add Items"},{"location":"sp/docs/items/#content-type","text":"You can also set the content type id when you create an item as shown in the example below: import { sp } from \"@pnp/sp\" ; sp . web . lists . getById ( \"4D5A36EA-6E84-4160-8458-65C436DB765C\" ). items . add ({ Title : \"Test 1\" , ContentTypeId : \"0x01030058FD86C279252341AB303852303E4DAF\" });","title":"Content Type"},{"location":"sp/docs/items/#user-fields","text":"There are two types of user fields, those that allow a single value and those that allow multiple. For both types, you first need to determine the Id field name, which you can do by doing a GET REST request on an existing item. Typically the value will be the user field internal name with \"Id\" appended. So in our example, we have two fields User1 and User2 so the Id fields are User1Id and User2Id. Next, you need to remember there are two types of user fields, those that take a single value and those that allow multiple - these are updated in different ways. For single value user fields you supply just the user's id. For multiple value fields, you need to supply an object with a \"results\" property and an array. Examples for both are shown below. import { sp } from \"@pnp/sp\" ; import { getGUID } from \"@pnp/core\" ; sp . web . lists . getByTitle ( \"PeopleFields\" ). items . add ({ Title : getGUID (), User1Id : 9 , // allows a single user User2Id : { results : [ 16 , 45 ] // allows multiple users } }). then ( i => { console . log ( i ); }); If you want to update or add user field values when using validateUpdateListItem you need to use the form shown below. You can specify multiple values in the array. import { sp } from \"@pnp/sp\" ; const result = await sp . web . lists . getByTitle ( \"UserFieldList\" ). items . getById ( 1 ). validateUpdateListItem ([{ FieldName : \"UserField\" , FieldValue : JSON.stringify ([{ \"Key\" : \"i:0#.f|membership|person@tenant.com\" }]), }, { FieldName : \"Title\" , FieldValue : \"Test - Updated\" , }]);","title":"User Fields"},{"location":"sp/docs/items/#lookup-fields","text":"What is said for User Fields is, in general, relevant to Lookup Fields: - Lookup Field types: - Single-valued lookup - Multiple-valued lookup - Id suffix should be appended to the end of lookup's EntityPropertyName in payloads - Numeric Ids for lookups' items should be passed as values import { sp } from \"@pnp/sp\" ; import { getGUID } from \"@pnp/core\" ; sp . web . lists . getByTitle ( \"LookupFields\" ). items . add ({ Title : getGUID (), LookupFieldId : 2 , // allows a single lookup value MuptiLookupFieldId : { results : [ 1 , 56 ] // allows multiple lookup value } }). then ( console . log ). catch ( console . log );","title":"Lookup Fields"},{"location":"sp/docs/items/#add-multiple-items","text":"import { sp } from \"@pnp/sp\" ; let list = sp . web . lists . getByTitle ( \"rapidadd\" ); list . getListItemEntityTypeFullName (). then ( entityTypeFullName => { let batch = sp . web . createBatch (); list . items . inBatch ( batch ). add ({ Title : \"Batch 6\" }, entityTypeFullName ). then ( b => { console . log ( b ); }); list . items . inBatch ( batch ). add ({ Title : \"Batch 7\" }, entityTypeFullName ). then ( b => { console . log ( b ); }); batch . execute (). then ( d => console . log ( \"Done\" )); });","title":"Add Multiple Items"},{"location":"sp/docs/items/#update","text":"The update method is very similar to the add method in that it takes a plain object representing the fields to update. The property names are the internal names of the fields. If you aren't sure you can always do a get request for an item in the list and see the field names that come back - you would use these same names to update the item. import { sp } from \"@pnp/sp\" ; let list = sp . web . lists . getByTitle ( \"MyList\" ); list . items . getById ( 1 ). update ({ Title : \"My New Title\" , Description : \"Here is a new description\" }). then ( i => { console . log ( i ); });","title":"Update"},{"location":"sp/docs/items/#getting-and-updating-a-collection-using-filter","text":"import { sp } from \"@pnp/sp\" ; // you are getting back a collection here sp . web . lists . getByTitle ( \"MyList\" ). items . top ( 1 ). filter ( \"Title eq 'A Title'\" ). get (). then (( items : any []) => { // see if we got something if ( items . length > 0 ) { sp . web . lists . getByTitle ( \"MyList\" ). items . getById ( items [ 0 ]. Id ). update ({ Title : \"Updated Title\" , }). then ( result => { // here you will have updated the item console . log ( JSON . stringify ( result )); }); } });","title":"Getting and updating a collection using filter"},{"location":"sp/docs/items/#update-multiple-items","text":"This approach avoids multiple calls for the same list's entity type name. import { sp } from \"@pnp/sp\" ; let list = sp . web . lists . getByTitle ( \"rapidupdate\" ); list . getListItemEntityTypeFullName (). then ( entityTypeFullName => { let batch = sp . web . createBatch (); // note requirement of \"*\" eTag param - or use a specific eTag value as needed list . items . getById ( 1 ). inBatch ( batch ). update ({ Title : \"Batch 6\" }, \"*\" , entityTypeFullName ). then ( b => { console . log ( b ); }); list . items . getById ( 2 ). inBatch ( batch ). update ({ Title : \"Batch 7\" }, \"*\" , entityTypeFullName ). then ( b => { console . log ( b ); }); batch . execute (). then ( d => console . log ( \"Done\" )); });","title":"Update Multiple Items"},{"location":"sp/docs/items/#recycle","text":"Sending an item to the Recycle Bin is as simple as calling the .recycle method. import { sp } from \"@pnp/sp\" ; let list = sp . web . lists . getByTitle ( \"MyList\" ); list . items . getById ( 1 ). recycle (). then ( _ => {});","title":"Recycle"},{"location":"sp/docs/items/#delete","text":"Delete is as simple as calling the .delete method. It optionally takes an eTag if you need to manage concurrency. import { sp } from \"@pnp/sp\" ; let list = sp . web . lists . getByTitle ( \"MyList\" ); list . items . getById ( 1 ). delete (). then ( _ => {});","title":"Delete"},{"location":"sp/docs/items/#resolving-field-names","text":"It's a very common mistake trying wrong field names in the requests. Field's EntityPropertyName value should be used. The easiest way to get know EntityPropertyName is to use the following snippet: import { sp } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( '[Lists_Title]' ) . fields . select ( 'Title, EntityPropertyName' ) . filter ( `Hidden eq false and Title eq '[Field's_Display_Name]'` ) . get () . then ( response => { console . log ( response . map ( field => { return { Title : field.Title , EntityPropertyName : field.EntityPropertyName }; })); }) . catch ( console . log ); Lookup fields' names should be ended with additional Id suffix. E.g. for Editor EntityPropertyName EditorId should be used.","title":"Resolving field names"},{"location":"sp/docs/navigation-service/","text":"@pnp/sp/navigation service \u00b6 The global navigation service located at \"_api/navigation\" provides access to the SiteMapProvider instances available in a given site collection. getMenuState \u00b6 The MenuState service operation returns a Menu-State (dump) of a SiteMapProvider on a site. It will return an exception if the SiteMapProvider cannot be found on the site, the SiteMapProvider does not implement the IEditableSiteMapProvider interface or the SiteMapNode key cannot be found within the provider hierarchy. The IEditableSiteMapProvider also supports Custom Properties which is an optional feature. What will be return in the custom properties is up to the IEditableSiteMapProvider implementation and can differ for for each SiteMapProvider implementation. The custom properties can be requested by providing a comma seperated string of property names like: property1,property2,property3\\,containingcomma NOTE: the , seperator can be escaped using the \\ as escape character as done in the example above. The string above would split like: property1 property2 * property3,containingcomma import { sp } from \"@pnp/sp\" ; // Will return a menu state of the default SiteMapProvider 'SPSiteMapProvider' where the dump starts a the RootNode (within the site) with a depth of 10 levels. sp . navigation . getMenuState (). then ( r => { console . log ( JSON . stringify ( r , null , 4 )); }). catch ( console . error ); // Will return the menu state of the 'SPSiteMapProvider', starting with the node with the key '1002' with a depth of 5 sp . navigation . getMenuState ( \"1002\" , 5 ). then ( r => { console . log ( JSON . stringify ( r , null , 4 )); }). catch ( console . error ); // Will return the menu state of the 'CurrentNavSiteMapProviderNoEncode' from the root node of the provider with a depth of 5 sp . navigation . getMenuState ( null , 5 , \"CurrentNavSiteMapProviderNoEncode\" ). then ( r => { console . log ( JSON . stringify ( r , null , 4 )); }). catch ( console . error ); getMenuNodeKey \u00b6 Tries to get a SiteMapNode.Key for a given URL within a site collection. If the SiteMapNode cannot be found an Exception is returned. The method is using SiteMapProvider.FindSiteMapNodeFromKey(string rawUrl) to lookup the SiteMapNode. Depending on the actual implementation of FindSiteMapNodeFromKey the matching can differ for different SiteMapProviders. import { sp } from \"@pnp/sp\" ; sp . navigation . getMenuNodeKey ( \"/sites/dev/Lists/SPPnPJSExampleList/AllItems.aspx\" ). then ( r => { console . log ( JSON . stringify ( r , null , 4 )); }). catch ( console . error );","title":"Navigation Service"},{"location":"sp/docs/navigation-service/#pnpspnavigation-service","text":"The global navigation service located at \"_api/navigation\" provides access to the SiteMapProvider instances available in a given site collection.","title":"@pnp/sp/navigation service"},{"location":"sp/docs/navigation-service/#getmenustate","text":"The MenuState service operation returns a Menu-State (dump) of a SiteMapProvider on a site. It will return an exception if the SiteMapProvider cannot be found on the site, the SiteMapProvider does not implement the IEditableSiteMapProvider interface or the SiteMapNode key cannot be found within the provider hierarchy. The IEditableSiteMapProvider also supports Custom Properties which is an optional feature. What will be return in the custom properties is up to the IEditableSiteMapProvider implementation and can differ for for each SiteMapProvider implementation. The custom properties can be requested by providing a comma seperated string of property names like: property1,property2,property3\\,containingcomma NOTE: the , seperator can be escaped using the \\ as escape character as done in the example above. The string above would split like: property1 property2 * property3,containingcomma import { sp } from \"@pnp/sp\" ; // Will return a menu state of the default SiteMapProvider 'SPSiteMapProvider' where the dump starts a the RootNode (within the site) with a depth of 10 levels. sp . navigation . getMenuState (). then ( r => { console . log ( JSON . stringify ( r , null , 4 )); }). catch ( console . error ); // Will return the menu state of the 'SPSiteMapProvider', starting with the node with the key '1002' with a depth of 5 sp . navigation . getMenuState ( \"1002\" , 5 ). then ( r => { console . log ( JSON . stringify ( r , null , 4 )); }). catch ( console . error ); // Will return the menu state of the 'CurrentNavSiteMapProviderNoEncode' from the root node of the provider with a depth of 5 sp . navigation . getMenuState ( null , 5 , \"CurrentNavSiteMapProviderNoEncode\" ). then ( r => { console . log ( JSON . stringify ( r , null , 4 )); }). catch ( console . error );","title":"getMenuState"},{"location":"sp/docs/navigation-service/#getmenunodekey","text":"Tries to get a SiteMapNode.Key for a given URL within a site collection. If the SiteMapNode cannot be found an Exception is returned. The method is using SiteMapProvider.FindSiteMapNodeFromKey(string rawUrl) to lookup the SiteMapNode. Depending on the actual implementation of FindSiteMapNodeFromKey the matching can differ for different SiteMapProviders. import { sp } from \"@pnp/sp\" ; sp . navigation . getMenuNodeKey ( \"/sites/dev/Lists/SPPnPJSExampleList/AllItems.aspx\" ). then ( r => { console . log ( JSON . stringify ( r , null , 4 )); }). catch ( console . error );","title":"getMenuNodeKey"},{"location":"sp/docs/permissions/","text":"@pnp/sp - permissions \u00b6 A common task is to determine if a user or the current user has a certain permission level. It is a great idea to check before performing a task such as creating a list to ensure a user can without getting back an error. This allows you to provide a better experience to the user. Permissions in SharePoint are assigned to the set of securable objects which include Site, Web, List, and List Item. These are the four level to which unique permissions can be assigned. As such @pnp/sp provides a set of methods defined in the QueryableSecurable class to handle these permissions. These examples all use the Web to get the values, however the methods work identically on all securables. Get Role Assignments \u00b6 This gets a collection of all the role assignments on a given securable. The property returns a RoleAssignments collection which supports the OData collection operators. import { sp } from \"@pnp/sp\" ; import { Logger } from \"@pnp/logging\" ; sp . web . roleAssignments . get (). then ( roles => { Logger . writeJSON ( roles ); }); First Unique Ancestor Securable Object \u00b6 This method can be used to find the securable parent up the hierarchy that has unique permissions. If everything inherits permissions this will be the Site. If a sub web has unique permissions it will be the web, and so on. import { sp } from \"@pnp/sp\" ; import { Logger } from \"@pnp/logging\" ; sp . web . firstUniqueAncestorSecurableObject . get (). then ( obj => { Logger . writeJSON ( obj ); }); User Effective Permissions \u00b6 This method returns the BasePermissions for a given user or the current user. This value contains the High and Low values for a user on the securable you have queried. import { sp } from \"@pnp/sp\" ; import { Logger } from \"@pnp/logging\" ; sp . web . getUserEffectivePermissions ( \"i:0#.f|membership|user@site.com\" ). then ( perms => { Logger . writeJSON ( perms ); }); sp . web . getCurrentUserEffectivePermissions (). then ( perms => { Logger . writeJSON ( perms ); }); User Has Permissions \u00b6 Because the High and Low values in the BasePermission don't obviously mean anything you can use these methods along with the PermissionKind enumeration to check actual rights on the securable. import { sp , PermissionKind } from \"@pnp/sp\" ; sp . web . userHasPermissions ( \"i:0#.f|membership|user@site.com\" , PermissionKind . ApproveItems ). then ( perms => { console . log ( perms ); }); sp . web . currentUserHasPermissions ( PermissionKind . ApproveItems ). then ( perms => { console . log ( perms ); }); Has Permissions \u00b6 If you need to check multiple permissions it can be more efficient to get the BasePermissions once and then use the hasPermissions method to check them as shown below. import { sp , PermissionKind } from \"@pnp/sp\" ; sp . web . getCurrentUserEffectivePermissions (). then ( perms => { if ( sp . web . hasPermissions ( perms , PermissionKind . AddListItems ) && sp . web . hasPermissions ( perms , PermissionKind . DeleteVersions )) { // ... } });","title":"Permissions"},{"location":"sp/docs/permissions/#pnpsp-permissions","text":"A common task is to determine if a user or the current user has a certain permission level. It is a great idea to check before performing a task such as creating a list to ensure a user can without getting back an error. This allows you to provide a better experience to the user. Permissions in SharePoint are assigned to the set of securable objects which include Site, Web, List, and List Item. These are the four level to which unique permissions can be assigned. As such @pnp/sp provides a set of methods defined in the QueryableSecurable class to handle these permissions. These examples all use the Web to get the values, however the methods work identically on all securables.","title":"@pnp/sp - permissions"},{"location":"sp/docs/permissions/#get-role-assignments","text":"This gets a collection of all the role assignments on a given securable. The property returns a RoleAssignments collection which supports the OData collection operators. import { sp } from \"@pnp/sp\" ; import { Logger } from \"@pnp/logging\" ; sp . web . roleAssignments . get (). then ( roles => { Logger . writeJSON ( roles ); });","title":"Get Role Assignments"},{"location":"sp/docs/permissions/#first-unique-ancestor-securable-object","text":"This method can be used to find the securable parent up the hierarchy that has unique permissions. If everything inherits permissions this will be the Site. If a sub web has unique permissions it will be the web, and so on. import { sp } from \"@pnp/sp\" ; import { Logger } from \"@pnp/logging\" ; sp . web . firstUniqueAncestorSecurableObject . get (). then ( obj => { Logger . writeJSON ( obj ); });","title":"First Unique Ancestor Securable Object"},{"location":"sp/docs/permissions/#user-effective-permissions","text":"This method returns the BasePermissions for a given user or the current user. This value contains the High and Low values for a user on the securable you have queried. import { sp } from \"@pnp/sp\" ; import { Logger } from \"@pnp/logging\" ; sp . web . getUserEffectivePermissions ( \"i:0#.f|membership|user@site.com\" ). then ( perms => { Logger . writeJSON ( perms ); }); sp . web . getCurrentUserEffectivePermissions (). then ( perms => { Logger . writeJSON ( perms ); });","title":"User Effective Permissions"},{"location":"sp/docs/permissions/#user-has-permissions","text":"Because the High and Low values in the BasePermission don't obviously mean anything you can use these methods along with the PermissionKind enumeration to check actual rights on the securable. import { sp , PermissionKind } from \"@pnp/sp\" ; sp . web . userHasPermissions ( \"i:0#.f|membership|user@site.com\" , PermissionKind . ApproveItems ). then ( perms => { console . log ( perms ); }); sp . web . currentUserHasPermissions ( PermissionKind . ApproveItems ). then ( perms => { console . log ( perms ); });","title":"User Has Permissions"},{"location":"sp/docs/permissions/#has-permissions","text":"If you need to check multiple permissions it can be more efficient to get the BasePermissions once and then use the hasPermissions method to check them as shown below. import { sp , PermissionKind } from \"@pnp/sp\" ; sp . web . getCurrentUserEffectivePermissions (). then ( perms => { if ( sp . web . hasPermissions ( perms , PermissionKind . AddListItems ) && sp . web . hasPermissions ( perms , PermissionKind . DeleteVersions )) { // ... } });","title":"Has Permissions"},{"location":"sp/docs/profiles/","text":"@pnp/sp/profiles \u00b6 The profile services allows to to work with the SharePoint User Profile Store. Profiles \u00b6 Profiles is accessed directly from the root sp object. import { sp } from \"@pnp/sp\" ; GET \u00b6 Get profile properties for a specific user \u00b6 getPropertiesFor(loginName: string): Promise; sp . profiles . getPropertiesFor ( loginName ). then (( profile : any ) => { console . log ( profile . DisplayName ); console . log ( profile . Email ); console . log ( profile . Title ); console . log ( profile . UserProfileProperties . length ); // Properties are stored in inconvenient Key/Value pairs, // so parse into an object called userProperties var properties = {}; profile . UserProfileProperties . forEach ( function ( prop ) { properties [ prop . Key ] = prop . Value ; }); profile . userProperties = properties ; } Get a specific property for a specific user \u00b6 getUserProfilePropertyFor(loginName: string, propertyName: string): Promise; sp . profiles . getUserProfilePropertyFor ( loginName , propName ). then (( prop : string ) => { console . log ( prop ); }; Find whether a user is following another user \u00b6 isFollowing(follower: string, followee: string): Promise; sp . profiles . isFollowing ( follower , followee ). then (( followed : boolean ) => { console . log ( followed ); }; Find out who a user is following \u00b6 getPeopleFollowedBy(loginName: string): Promise; sp . profiles . getPeopleFollowedBy ( loginName ). then (( followed : any []) => { console . log ( followed . length ); }; Find out if the current user is followed by another user \u00b6 amIFollowedBy(loginName: string): Promise; Returns a boolean indicating if the current user is followed by the user with loginName. Get a specific property for the specified user. sp . profiles . amIFollowedBy ( loginName ). then (( followed : boolean ) => { console . log ( followed ); }; Get the people who are following the specified user \u00b6 getFollowersFor(loginName: string): Promise; sp . profiles . getFollowersFor ( loginName ). then (( followed : any ) => { console . log ( followed . length ); }; SET \u00b6 Set a single value property value \u00b6 setSingleValueProfileProperty(accountName: string, propertyName: string, propertyValue: string) Set a user's user profile property. sp . profiles . setSingleValueProfileProperty ( accountName , propertyName , propertyValue ); Set multi valued User Profile property \u00b6 setMultiValuedProfileProperty(accountName: string, propertyName: string, propertyValues: string[]): Promise; sp . profiles . setSingleValueProfileProperty ( accountName , propertyName , propertyValues ); Upload and set the user profile picture \u00b6 Users can upload a picture to their own profile only). Not supported for batching. Blob data representing the user's picture in BMP, JPEG, or PNG format of up to 4.76MB setMyProfilePic(profilePicSource: Blob): Promise;","title":"Profiles"},{"location":"sp/docs/profiles/#pnpspprofiles","text":"The profile services allows to to work with the SharePoint User Profile Store.","title":"@pnp/sp/profiles"},{"location":"sp/docs/profiles/#profiles","text":"Profiles is accessed directly from the root sp object. import { sp } from \"@pnp/sp\" ;","title":"Profiles"},{"location":"sp/docs/profiles/#get","text":"","title":"GET"},{"location":"sp/docs/profiles/#get-profile-properties-for-a-specific-user","text":"getPropertiesFor(loginName: string): Promise; sp . profiles . getPropertiesFor ( loginName ). then (( profile : any ) => { console . log ( profile . DisplayName ); console . log ( profile . Email ); console . log ( profile . Title ); console . log ( profile . UserProfileProperties . length ); // Properties are stored in inconvenient Key/Value pairs, // so parse into an object called userProperties var properties = {}; profile . UserProfileProperties . forEach ( function ( prop ) { properties [ prop . Key ] = prop . Value ; }); profile . userProperties = properties ; }","title":"Get profile properties for a specific user"},{"location":"sp/docs/profiles/#get-a-specific-property-for-a-specific-user","text":"getUserProfilePropertyFor(loginName: string, propertyName: string): Promise; sp . profiles . getUserProfilePropertyFor ( loginName , propName ). then (( prop : string ) => { console . log ( prop ); };","title":"Get a specific property for a specific user"},{"location":"sp/docs/profiles/#find-whether-a-user-is-following-another-user","text":"isFollowing(follower: string, followee: string): Promise; sp . profiles . isFollowing ( follower , followee ). then (( followed : boolean ) => { console . log ( followed ); };","title":"Find whether a user is following another user"},{"location":"sp/docs/profiles/#find-out-who-a-user-is-following","text":"getPeopleFollowedBy(loginName: string): Promise; sp . profiles . getPeopleFollowedBy ( loginName ). then (( followed : any []) => { console . log ( followed . length ); };","title":"Find out who a user is following"},{"location":"sp/docs/profiles/#find-out-if-the-current-user-is-followed-by-another-user","text":"amIFollowedBy(loginName: string): Promise; Returns a boolean indicating if the current user is followed by the user with loginName. Get a specific property for the specified user. sp . profiles . amIFollowedBy ( loginName ). then (( followed : boolean ) => { console . log ( followed ); };","title":"Find out if the current user is followed by another user"},{"location":"sp/docs/profiles/#get-the-people-who-are-following-the-specified-user","text":"getFollowersFor(loginName: string): Promise; sp . profiles . getFollowersFor ( loginName ). then (( followed : any ) => { console . log ( followed . length ); };","title":"Get the people who are following the specified user"},{"location":"sp/docs/profiles/#set","text":"","title":"SET"},{"location":"sp/docs/profiles/#set-a-single-value-property-value","text":"setSingleValueProfileProperty(accountName: string, propertyName: string, propertyValue: string) Set a user's user profile property. sp . profiles . setSingleValueProfileProperty ( accountName , propertyName , propertyValue );","title":"Set a single value property value"},{"location":"sp/docs/profiles/#set-multi-valued-user-profile-property","text":"setMultiValuedProfileProperty(accountName: string, propertyName: string, propertyValues: string[]): Promise; sp . profiles . setSingleValueProfileProperty ( accountName , propertyName , propertyValues );","title":"Set multi valued User Profile property"},{"location":"sp/docs/profiles/#upload-and-set-the-user-profile-picture","text":"Users can upload a picture to their own profile only). Not supported for batching. Blob data representing the user's picture in BMP, JPEG, or PNG format of up to 4.76MB setMyProfilePic(profilePicSource: Blob): Promise;","title":"Upload and set the user profile picture"},{"location":"sp/docs/related-items/","text":"@pnp/sp/relateditems \u00b6 Related items are used in Task and Workflow lists (as well as others) to track items that have relationships similar to database relationships. All methods chain off the Web's relatedItems property as shown below: getRelatedItems \u00b6 Expects the named library to exist within the contextual web. import { sp , RelatedItem } from \"@pnp/sp\" ; sp . web . relatedItems . getRelatedItems ( \"Documents\" , 1 ). then (( result : RelatedItem []) => { console . log ( result ); }); getPageOneRelatedItems \u00b6 Expects the named library to exist within the contextual web. import { sp , RelatedItem } from \"@pnp/sp\" ; sp . web . relatedItems . getPageOneRelatedItems ( \"Documents\" , 1 ). then (( result : RelatedItem []) => { console . log ( result ); }); addSingleLink \u00b6 import { sp } from \"@pnp/sp\" ; sp . web . relatedItems . addSingleLink ( \"RelatedItemsList1\" , 2 , \"https://site.sharepoint.com/sites/dev/subsite\" , \"RelatedItemsList2\" , 1 , \"https://site.sharepoint.com/sites/dev\" ). then ( _ => { // ... return is void }); sp . web . relatedItems . addSingleLink ( \"RelatedItemsList1\" , 2 , \"https://site.sharepoint.com/sites/dev/subsite\" , \"RelatedItemsList2\" , 1 , \"https://site.sharepoint.com/sites/dev\" , true ). then ( _ => { // ... return is void }); addSingleLinkToUrl \u00b6 Adds a related item link from an item specified by list name and item id, to an item specified by url import { sp } from \"@pnp/sp\" ; sp . web . relatedItems . addSingleLinkToUrl ( \"RelatedItemsList1\" , 2 , \"https://site.sharepoint.com/sites/dev/subsite/Documents/test.txt\" ). then ( _ => { // ... return is void }); sp . web . relatedItems . addSingleLinkToUrl ( \"RelatedItemsList1\" , 2 , \"https://site.sharepoint.com/sites/dev/subsite/Documents/test.txt\" , true ). then ( _ => { // ... return is void }); addSingleLinkFromUrl \u00b6 Adds a related item link from an item specified by url, to an item specified by list name and item id import { sp } from \"@pnp/sp\" ; sp . web . relatedItems . addSingleLinkFromUrl ( \"https://site.sharepoint.com/sites/dev/subsite/Documents/test.txt\" , \"RelatedItemsList1\" , 2 ). then ( _ => { // ... return is void }); sp . web . relatedItems . addSingleLinkFromUrl ( \"https://site.sharepoint.com/sites/dev/subsite/Documents/test.txt\" , \"RelatedItemsList1\" , 2 , true ). then ( _ => { // ... return is void }); deleteSingleLink \u00b6 import { sp } from \"@pnp/sp\" ; sp . web . relatedItems . deleteSingleLink ( \"RelatedItemsList1\" , 2 , \"https://site.sharepoint.com/sites/dev/subsite\" , \"RelatedItemsList2\" , 1 , \"https://site.sharepoint.com/sites/dev\" ). then ( _ => { // ... return is void }); sp . web . relatedItems . deleteSingleLink ( \"RelatedItemsList1\" , 2 , \"https://site.sharepoint.com/sites/dev/subsite\" , \"RelatedItemsList2\" , 1 , \"https://site.sharepoint.com/sites/dev\" , true ). then ( _ => { // ... return is void });","title":"Related Items"},{"location":"sp/docs/related-items/#pnpsprelateditems","text":"Related items are used in Task and Workflow lists (as well as others) to track items that have relationships similar to database relationships. All methods chain off the Web's relatedItems property as shown below:","title":"@pnp/sp/relateditems"},{"location":"sp/docs/related-items/#getrelateditems","text":"Expects the named library to exist within the contextual web. import { sp , RelatedItem } from \"@pnp/sp\" ; sp . web . relatedItems . getRelatedItems ( \"Documents\" , 1 ). then (( result : RelatedItem []) => { console . log ( result ); });","title":"getRelatedItems"},{"location":"sp/docs/related-items/#getpageonerelateditems","text":"Expects the named library to exist within the contextual web. import { sp , RelatedItem } from \"@pnp/sp\" ; sp . web . relatedItems . getPageOneRelatedItems ( \"Documents\" , 1 ). then (( result : RelatedItem []) => { console . log ( result ); });","title":"getPageOneRelatedItems"},{"location":"sp/docs/related-items/#addsinglelink","text":"import { sp } from \"@pnp/sp\" ; sp . web . relatedItems . addSingleLink ( \"RelatedItemsList1\" , 2 , \"https://site.sharepoint.com/sites/dev/subsite\" , \"RelatedItemsList2\" , 1 , \"https://site.sharepoint.com/sites/dev\" ). then ( _ => { // ... return is void }); sp . web . relatedItems . addSingleLink ( \"RelatedItemsList1\" , 2 , \"https://site.sharepoint.com/sites/dev/subsite\" , \"RelatedItemsList2\" , 1 , \"https://site.sharepoint.com/sites/dev\" , true ). then ( _ => { // ... return is void });","title":"addSingleLink"},{"location":"sp/docs/related-items/#addsinglelinktourl","text":"Adds a related item link from an item specified by list name and item id, to an item specified by url import { sp } from \"@pnp/sp\" ; sp . web . relatedItems . addSingleLinkToUrl ( \"RelatedItemsList1\" , 2 , \"https://site.sharepoint.com/sites/dev/subsite/Documents/test.txt\" ). then ( _ => { // ... return is void }); sp . web . relatedItems . addSingleLinkToUrl ( \"RelatedItemsList1\" , 2 , \"https://site.sharepoint.com/sites/dev/subsite/Documents/test.txt\" , true ). then ( _ => { // ... return is void });","title":"addSingleLinkToUrl"},{"location":"sp/docs/related-items/#addsinglelinkfromurl","text":"Adds a related item link from an item specified by url, to an item specified by list name and item id import { sp } from \"@pnp/sp\" ; sp . web . relatedItems . addSingleLinkFromUrl ( \"https://site.sharepoint.com/sites/dev/subsite/Documents/test.txt\" , \"RelatedItemsList1\" , 2 ). then ( _ => { // ... return is void }); sp . web . relatedItems . addSingleLinkFromUrl ( \"https://site.sharepoint.com/sites/dev/subsite/Documents/test.txt\" , \"RelatedItemsList1\" , 2 , true ). then ( _ => { // ... return is void });","title":"addSingleLinkFromUrl"},{"location":"sp/docs/related-items/#deletesinglelink","text":"import { sp } from \"@pnp/sp\" ; sp . web . relatedItems . deleteSingleLink ( \"RelatedItemsList1\" , 2 , \"https://site.sharepoint.com/sites/dev/subsite\" , \"RelatedItemsList2\" , 1 , \"https://site.sharepoint.com/sites/dev\" ). then ( _ => { // ... return is void }); sp . web . relatedItems . deleteSingleLink ( \"RelatedItemsList1\" , 2 , \"https://site.sharepoint.com/sites/dev/subsite\" , \"RelatedItemsList2\" , 1 , \"https://site.sharepoint.com/sites/dev\" , true ). then ( _ => { // ... return is void });","title":"deleteSingleLink"},{"location":"sp/docs/search/","text":"@pnp/sp/search \u00b6 Using search you can access content throughout your organization in a secure and consistent manner. The library provides support for searching and search suggest - as well as some interfaces and helper classes to make building your queries and processing responses easier. Search \u00b6 Search is accessed directly from the root sp object and can take either a string representing the query text, a plain object matching the SearchQuery interface, or a SearchQueryBuilder instance. The first two are shown below. import { sp , SearchQuery , SearchResults } from \"@pnp/sp\" ; // text search using SharePoint default values for other parameters sp . search ( \"test\" ). then (( r : SearchResults ) => { console . log ( r . ElapsedTime ); console . log ( r . RowCount ); console . log ( r . PrimarySearchResults ); }); // define a search query object matching the SearchQuery interface sp . search ( < SearchQuery > { Querytext : \"test\" , RowLimit : 10 , EnableInterleaving : true , }). then (( r : SearchResults ) => { console . log ( r . ElapsedTime ); console . log ( r . RowCount ); console . log ( r . PrimarySearchResults ); }); Search Result Caching \u00b6 Added in 1.1.5 As of version 1.1.5 you can also use the searchWithCaching method to enable cache support for your search results this option works with any of the options for providing a query, just replace \"search\" with \"searchWithCaching\" in your method chain and gain all the benefits of caching. The second parameter is optional and allows you to specify the cache options import { sp , SearchQuery , SearchResults , SearchQueryBuilder } from \"@pnp/sp\" ; sp . searchWithCaching ( < SearchQuery > { Querytext : \"test\" , RowLimit : 10 , EnableInterleaving : true , }). then (( r : SearchResults ) => { console . log ( r . ElapsedTime ); console . log ( r . RowCount ); console . log ( r . PrimarySearchResults ); }); const builder = SearchQueryBuilder (). text ( \"test\" ). rowLimit ( 3 ); // supply a search query builder and caching options sp . searchWithCaching ( builder , { key : \"mykey\" , expiration : dateAdd ( new Date (), \"month\" , 1 ) }). then ( r2 => { console . log ( r2 . TotalRows ); }); Paging with SearchResults.getPage \u00b6 Paging is controlled by a start row and page size parameter. You can specify both arguments in your initial query however you can use the getPage method to jump to any page. The second parameter page size is optional and will use the previous RowLimit or default to 10. import { sp , SearchQueryBuilder , SearchResults } from \"@pnp/sp\" ; // this will hold our current results let currentResults : SearchResults = null ; let page = 1 ; // triggered on page load through some means function onStart() { // construct our query that will be throughout the paging process, likely from user input const q = SearchQueryBuilder . create ( \"test\" ). rowLimit ( 5 ); sp . search ( q ). then (( r : SearchResults ) => { currentResults = r ; // update the current results page = 1 ; // reset if needed // update UI with data... }); } // triggered by an event function next() { currentResults . getPage ( ++ page ). then (( r : SearchResults ) => { currentResults = r ; // update the current results // update UI with data... }); } // triggered by an event function prev() { currentResults . getPage ( -- page ). then (( r : SearchResults ) => { currentResults = r ; // update the current results // update UI with data... }); } SearchQueryBuilder \u00b6 The SearchQueryBuilder allows you to build your queries in a fluent manner. It also accepts constructor arguments for query text and a base query plain object, should you have a shared configuration for queries in an application you can define them once. The methods and properties match those on the SearchQuery interface. Boolean properties add the flag to the query while methods require that you supply one or more arguments. Also arguments supplied later in the chain will overwrite previous values. import { SearchQueryBuilder } from \"@pnp/sp\" ; // basic usage let q = SearchQueryBuilder (). text ( \"test\" ). rowLimit ( 4 ). enablePhonetic ; sp . search ( q ). then ( h => { /* ... */ }); // provide a default query text in the create() let q2 = SearchQueryBuilder ( \"text\" ). rowLimit ( 4 ). enablePhonetic ; sp . search ( q2 ). then ( h => { /* ... */ }); // provide query text and a template // shared settings across queries const appSearchSettings : SearchQuery = { EnablePhonetic : true , HiddenConstraints : \"reports\" }; let q3 = SearchQueryBuilder ( \"test\" , appSearchSettings ). enableQueryRules ; let q4 = SearchQueryBuilder ( \"financial data\" , appSearchSettings ). enableSorting . enableStemming ; sp . search ( q3 ). then ( h => { /* ... */ }); sp . search ( q4 ). then ( h => { /* ... */ }); Search Suggest \u00b6 Search suggest works in much the same way as search, except against the suggest end point. It takes a string or a plain object that matches SearchSuggestQuery. import { sp , SearchSuggestQuery , SearchSuggestResult } from \"@pnp/sp\" ; sp . searchSuggest ( \"test\" ). then (( r : SearchSuggestResult ) => { console . log ( r ); }); sp . searchSuggest ( < SearchSuggestQuery > { querytext : \"test\" , count : 5 , }). then (( r : SearchSuggestResult ) => { console . log ( r ); });","title":"Search"},{"location":"sp/docs/search/#pnpspsearch","text":"Using search you can access content throughout your organization in a secure and consistent manner. The library provides support for searching and search suggest - as well as some interfaces and helper classes to make building your queries and processing responses easier.","title":"@pnp/sp/search"},{"location":"sp/docs/search/#search","text":"Search is accessed directly from the root sp object and can take either a string representing the query text, a plain object matching the SearchQuery interface, or a SearchQueryBuilder instance. The first two are shown below. import { sp , SearchQuery , SearchResults } from \"@pnp/sp\" ; // text search using SharePoint default values for other parameters sp . search ( \"test\" ). then (( r : SearchResults ) => { console . log ( r . ElapsedTime ); console . log ( r . RowCount ); console . log ( r . PrimarySearchResults ); }); // define a search query object matching the SearchQuery interface sp . search ( < SearchQuery > { Querytext : \"test\" , RowLimit : 10 , EnableInterleaving : true , }). then (( r : SearchResults ) => { console . log ( r . ElapsedTime ); console . log ( r . RowCount ); console . log ( r . PrimarySearchResults ); });","title":"Search"},{"location":"sp/docs/search/#search-result-caching","text":"Added in 1.1.5 As of version 1.1.5 you can also use the searchWithCaching method to enable cache support for your search results this option works with any of the options for providing a query, just replace \"search\" with \"searchWithCaching\" in your method chain and gain all the benefits of caching. The second parameter is optional and allows you to specify the cache options import { sp , SearchQuery , SearchResults , SearchQueryBuilder } from \"@pnp/sp\" ; sp . searchWithCaching ( < SearchQuery > { Querytext : \"test\" , RowLimit : 10 , EnableInterleaving : true , }). then (( r : SearchResults ) => { console . log ( r . ElapsedTime ); console . log ( r . RowCount ); console . log ( r . PrimarySearchResults ); }); const builder = SearchQueryBuilder (). text ( \"test\" ). rowLimit ( 3 ); // supply a search query builder and caching options sp . searchWithCaching ( builder , { key : \"mykey\" , expiration : dateAdd ( new Date (), \"month\" , 1 ) }). then ( r2 => { console . log ( r2 . TotalRows ); });","title":"Search Result Caching"},{"location":"sp/docs/search/#paging-with-searchresultsgetpage","text":"Paging is controlled by a start row and page size parameter. You can specify both arguments in your initial query however you can use the getPage method to jump to any page. The second parameter page size is optional and will use the previous RowLimit or default to 10. import { sp , SearchQueryBuilder , SearchResults } from \"@pnp/sp\" ; // this will hold our current results let currentResults : SearchResults = null ; let page = 1 ; // triggered on page load through some means function onStart() { // construct our query that will be throughout the paging process, likely from user input const q = SearchQueryBuilder . create ( \"test\" ). rowLimit ( 5 ); sp . search ( q ). then (( r : SearchResults ) => { currentResults = r ; // update the current results page = 1 ; // reset if needed // update UI with data... }); } // triggered by an event function next() { currentResults . getPage ( ++ page ). then (( r : SearchResults ) => { currentResults = r ; // update the current results // update UI with data... }); } // triggered by an event function prev() { currentResults . getPage ( -- page ). then (( r : SearchResults ) => { currentResults = r ; // update the current results // update UI with data... }); }","title":"Paging with SearchResults.getPage"},{"location":"sp/docs/search/#searchquerybuilder","text":"The SearchQueryBuilder allows you to build your queries in a fluent manner. It also accepts constructor arguments for query text and a base query plain object, should you have a shared configuration for queries in an application you can define them once. The methods and properties match those on the SearchQuery interface. Boolean properties add the flag to the query while methods require that you supply one or more arguments. Also arguments supplied later in the chain will overwrite previous values. import { SearchQueryBuilder } from \"@pnp/sp\" ; // basic usage let q = SearchQueryBuilder (). text ( \"test\" ). rowLimit ( 4 ). enablePhonetic ; sp . search ( q ). then ( h => { /* ... */ }); // provide a default query text in the create() let q2 = SearchQueryBuilder ( \"text\" ). rowLimit ( 4 ). enablePhonetic ; sp . search ( q2 ). then ( h => { /* ... */ }); // provide query text and a template // shared settings across queries const appSearchSettings : SearchQuery = { EnablePhonetic : true , HiddenConstraints : \"reports\" }; let q3 = SearchQueryBuilder ( \"test\" , appSearchSettings ). enableQueryRules ; let q4 = SearchQueryBuilder ( \"financial data\" , appSearchSettings ). enableSorting . enableStemming ; sp . search ( q3 ). then ( h => { /* ... */ }); sp . search ( q4 ). then ( h => { /* ... */ });","title":"SearchQueryBuilder"},{"location":"sp/docs/search/#search-suggest","text":"Search suggest works in much the same way as search, except against the suggest end point. It takes a string or a plain object that matches SearchSuggestQuery. import { sp , SearchSuggestQuery , SearchSuggestResult } from \"@pnp/sp\" ; sp . searchSuggest ( \"test\" ). then (( r : SearchSuggestResult ) => { console . log ( r ); }); sp . searchSuggest ( < SearchSuggestQuery > { querytext : \"test\" , count : 5 , }). then (( r : SearchSuggestResult ) => { console . log ( r ); });","title":"Search Suggest"},{"location":"sp/docs/sharing/","text":"@pnp/sp/sharing \u00b6 Note: This API is still considered \"beta\" meaning it may change and some behaviors may differ across tenants by version. It is also supported only in SharePoint Online. One of the newer abilities in SharePoint is the ability to share webs, files, or folders with both internal and external folks. It is important to remember that these settings are managed at the tenant level and override anything you may supply as an argument to these methods. If you receive an InvalidOperationException when using these methods please check your tenant sharing settings to ensure sharing is not blocked before submitting an issue. getShareLink \u00b6 Applies to: Item, Folder, File Creates a sharing link for the given resource with an optional expiration. import { sp , SharingLinkKind , ShareLinkResponse } from \"@pnp/sp\" ; import { dateAdd } from \"@pnp/core\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/folder1\" ). getShareLink ( SharingLinkKind . AnonymousView ). then ((( result : ShareLinkResponse ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/folder1\" ). getShareLink ( SharingLinkKind . AnonymousView , dateAdd ( new Date (), \"day\" , 5 )). then (( result : ShareLinkResponse ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); shareWith \u00b6 Applies to: Item, Folder, File, Web Shares the given resource with the specified permissions (View or Edit) and optionally sends an email to the users. You can supply a single string for the loginnames parameter or an array of loginnames. The folder method takes an optional parameter \"shareEverything\" which determines if the shared permissions are pushed down to all items in the folder, even those with unique permissions. import { sp , SharingResult , SharingRole } from \"@pnp/sp\" ; sp . web . shareWith ( \"i:0#.f|membership|user@site.com\" ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . shareWith ( \"i:0#.f|membership|user@site.com\" , SharingRole . Edit ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/folder1\" ). shareWith ( \"i:0#.f|membership|user@site.com\" ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). shareWith ( \"i:0#.f|membership|user@site.com\" , SharingRole . Edit , true , true ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . getFileByServerRelativeUrl ( \"/sites/dev/Shared Documents/test.txt\" ). shareWith ( \"i:0#.f|membership|user@site.com\" ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . getFileByServerRelativeUrl ( \"/sites/dev/Shared Documents/test.txt\" ). shareWith ( \"i:0#.f|membership|user@site.com\" , SharingRole . Edit ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); shareObject & shareObjectRaw \u00b6 Applies to: Web Allows you to share any shareable object in a web by providing the appropriate parameters. These two methods differ in that shareObject will try and fix up your query based on the supplied parameters where shareObjectRaw will send your supplied json object directly to the server. The later method is provided for the greatest amount of flexibility. import { sp , SharingResult , SharingRole } from \"@pnp/sp\" ; sp . web . shareObject ( \"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\" , \"i:0#.f|membership|user@site.com\" , SharingRole . View ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . shareObjectRaw ({ url : \"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\" , peoplePickerInput : [{ Key : \"i:0#.f|membership|user@site.com\" }], roleValue : \"role: 1973741327\" , groupId : 0 , propagateAcl : false , sendEmail : true , includeAnonymousLinkInEmail : false , emailSubject : \"subject\" , emailBody : \"body\" , useSimplifiedRoles : true , }); unshareObject \u00b6 Applies to: Web import { sp , SharingResult } from \"@pnp/sp\" ; sp . web . unshareObject ( \"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\" ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); checkSharingPermissions \u00b6 Applies to: Item, Folder, File Checks Permissions on the list of Users and returns back role the users have on the Item. import { sp , SharingEntityPermission } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). checkSharingPermissions ([{ alias : \"i:0#.f|membership|user@site.com\" }]). then (( result : SharingEntityPermission []) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); getSharingInformation \u00b6 Applies to: Item, Folder, File Get Sharing Information. import { sp , SharingInformation } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). getSharingInformation (). then (( result : SharingInformation ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); getObjectSharingSettings \u00b6 Applies to: Item, Folder, File Gets the sharing settings import { sp , ObjectSharingSettings } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). getObjectSharingSettings (). then (( result : ObjectSharingSettings ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); unshare \u00b6 Applies to: Item, Folder, File Unshares a given resource import { sp , SharingResult } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). unshare (). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); deleteSharingLinkByKind \u00b6 Applies to: Item, Folder, File import { sp , SharingLinkKind , SharingResult } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). deleteSharingLinkByKind ( SharingLinkKind . AnonymousEdit ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); unshareLink \u00b6 Applies to: Item, Folder, File import { sp , SharingLinkKind } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). unshareLink ( SharingLinkKind . AnonymousEdit ). then ( _ => { console . log ( \"done\" ); }). catch ( e => { console . error ( e ); }); sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). unshareLink ( SharingLinkKind . AnonymousEdit , \"12345\" ). then ( _ => { console . log ( \"done\" ); }). catch ( e => { console . error ( e ); });","title":"Sharing"},{"location":"sp/docs/sharing/#pnpspsharing","text":"Note: This API is still considered \"beta\" meaning it may change and some behaviors may differ across tenants by version. It is also supported only in SharePoint Online. One of the newer abilities in SharePoint is the ability to share webs, files, or folders with both internal and external folks. It is important to remember that these settings are managed at the tenant level and override anything you may supply as an argument to these methods. If you receive an InvalidOperationException when using these methods please check your tenant sharing settings to ensure sharing is not blocked before submitting an issue.","title":"@pnp/sp/sharing"},{"location":"sp/docs/sharing/#getsharelink","text":"Applies to: Item, Folder, File Creates a sharing link for the given resource with an optional expiration. import { sp , SharingLinkKind , ShareLinkResponse } from \"@pnp/sp\" ; import { dateAdd } from \"@pnp/core\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/folder1\" ). getShareLink ( SharingLinkKind . AnonymousView ). then ((( result : ShareLinkResponse ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/folder1\" ). getShareLink ( SharingLinkKind . AnonymousView , dateAdd ( new Date (), \"day\" , 5 )). then (( result : ShareLinkResponse ) => { console . log ( result ); }). catch ( e => { console . error ( e ); });","title":"getShareLink"},{"location":"sp/docs/sharing/#sharewith","text":"Applies to: Item, Folder, File, Web Shares the given resource with the specified permissions (View or Edit) and optionally sends an email to the users. You can supply a single string for the loginnames parameter or an array of loginnames. The folder method takes an optional parameter \"shareEverything\" which determines if the shared permissions are pushed down to all items in the folder, even those with unique permissions. import { sp , SharingResult , SharingRole } from \"@pnp/sp\" ; sp . web . shareWith ( \"i:0#.f|membership|user@site.com\" ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . shareWith ( \"i:0#.f|membership|user@site.com\" , SharingRole . Edit ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/folder1\" ). shareWith ( \"i:0#.f|membership|user@site.com\" ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). shareWith ( \"i:0#.f|membership|user@site.com\" , SharingRole . Edit , true , true ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . getFileByServerRelativeUrl ( \"/sites/dev/Shared Documents/test.txt\" ). shareWith ( \"i:0#.f|membership|user@site.com\" ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . getFileByServerRelativeUrl ( \"/sites/dev/Shared Documents/test.txt\" ). shareWith ( \"i:0#.f|membership|user@site.com\" , SharingRole . Edit ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); });","title":"shareWith"},{"location":"sp/docs/sharing/#shareobject-shareobjectraw","text":"Applies to: Web Allows you to share any shareable object in a web by providing the appropriate parameters. These two methods differ in that shareObject will try and fix up your query based on the supplied parameters where shareObjectRaw will send your supplied json object directly to the server. The later method is provided for the greatest amount of flexibility. import { sp , SharingResult , SharingRole } from \"@pnp/sp\" ; sp . web . shareObject ( \"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\" , \"i:0#.f|membership|user@site.com\" , SharingRole . View ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); }); sp . web . shareObjectRaw ({ url : \"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\" , peoplePickerInput : [{ Key : \"i:0#.f|membership|user@site.com\" }], roleValue : \"role: 1973741327\" , groupId : 0 , propagateAcl : false , sendEmail : true , includeAnonymousLinkInEmail : false , emailSubject : \"subject\" , emailBody : \"body\" , useSimplifiedRoles : true , });","title":"shareObject & shareObjectRaw"},{"location":"sp/docs/sharing/#unshareobject","text":"Applies to: Web import { sp , SharingResult } from \"@pnp/sp\" ; sp . web . unshareObject ( \"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\" ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); });","title":"unshareObject"},{"location":"sp/docs/sharing/#checksharingpermissions","text":"Applies to: Item, Folder, File Checks Permissions on the list of Users and returns back role the users have on the Item. import { sp , SharingEntityPermission } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). checkSharingPermissions ([{ alias : \"i:0#.f|membership|user@site.com\" }]). then (( result : SharingEntityPermission []) => { console . log ( result ); }). catch ( e => { console . error ( e ); });","title":"checkSharingPermissions"},{"location":"sp/docs/sharing/#getsharinginformation","text":"Applies to: Item, Folder, File Get Sharing Information. import { sp , SharingInformation } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). getSharingInformation (). then (( result : SharingInformation ) => { console . log ( result ); }). catch ( e => { console . error ( e ); });","title":"getSharingInformation"},{"location":"sp/docs/sharing/#getobjectsharingsettings","text":"Applies to: Item, Folder, File Gets the sharing settings import { sp , ObjectSharingSettings } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). getObjectSharingSettings (). then (( result : ObjectSharingSettings ) => { console . log ( result ); }). catch ( e => { console . error ( e ); });","title":"getObjectSharingSettings"},{"location":"sp/docs/sharing/#unshare","text":"Applies to: Item, Folder, File Unshares a given resource import { sp , SharingResult } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). unshare (). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); });","title":"unshare"},{"location":"sp/docs/sharing/#deletesharinglinkbykind","text":"Applies to: Item, Folder, File import { sp , SharingLinkKind , SharingResult } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). deleteSharingLinkByKind ( SharingLinkKind . AnonymousEdit ). then (( result : SharingResult ) => { console . log ( result ); }). catch ( e => { console . error ( e ); });","title":"deleteSharingLinkByKind"},{"location":"sp/docs/sharing/#unsharelink","text":"Applies to: Item, Folder, File import { sp , SharingLinkKind } from \"@pnp/sp\" ; sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). unshareLink ( SharingLinkKind . AnonymousEdit ). then ( _ => { console . log ( \"done\" ); }). catch ( e => { console . error ( e ); }); sp . web . getFolderByServerRelativeUrl ( \"/sites/dev/Shared Documents/test\" ). unshareLink ( SharingLinkKind . AnonymousEdit , \"12345\" ). then ( _ => { console . log ( \"done\" ); }). catch ( e => { console . error ( e ); });","title":"unshareLink"},{"location":"sp/docs/sitedesigns/","text":"@pnp/sp/sitedesigns \u00b6 You can create site designs to provide reusable lists, themes, layouts, pages, or custom actions so that your users can quickly build new SharePoint sites with the features they need. Check out SharePoint site design and site script overview for more information. Site Designs \u00b6 Create a new site design \u00b6 import { sp } from \"@pnp/sp\" ; // WebTemplate: 64 Team site template, 68 Communication site template const siteDesign = await sp . siteDesigns . createSiteDesign ({ SiteScriptIds : [ \"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\" ], Title : \"SiteDesign001\" , WebTemplate : \"64\" , }); console . log ( siteDesign . Title ); Applying a site design to a site \u00b6 import { sp } from \"@pnp/sp\" ; // Limited to 30 actions in a site script, but runs synchronously await sp . siteDesigns . applySiteDesign ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" , \"https://contoso.sharepoint.com/sites/teamsite-pnpjs001\" ); // Better use the following method for 300 actions in a site script const task = await sp . web . addSiteDesignTask ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" ); Retrieval \u00b6 import { sp } from \"@pnp/sp\" ; // Retrieving all site designs const allSiteDesigns = await sp . siteDesigns . getSiteDesigns (); console . log ( `Total site designs: ${ allSiteDesigns . length } ` ); // Retrieving a single site design by Id const siteDesign = await sp . siteDesigns . getSiteDesignMetadata ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" ); console . log ( siteDesign . Title ); Update and delete \u00b6 import { sp } from \"@pnp/sp\" ; // Update const updatedSiteDesign = await sp . siteDesigns . updateSiteDesign ({ Id : \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" , Title : \"SiteDesignUpdatedTitle001\" }); // Delete await sp . siteDesigns . deleteSiteDesign ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" ); Setting Rights/Permissions \u00b6 import { sp } from \"@pnp/sp\" ; // Get const rights = await sp . siteDesigns . getSiteDesignRights ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" ); console . log ( rights . length > 0 ? rights [ 0 ]. PrincipalName : \"\" ); // Grant await sp . siteDesigns . grantSiteDesignRights ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" , [ \"user@contoso.onmicrosoft.com\" ]); // Revoke await sp . siteDesigns . revokeSiteDesignRights ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" , [ \"user@contoso.onmicrosoft.com\" ]); // Reset all view rights const rights = await sp . siteDesigns . getSiteDesignRights ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" ); await sp . siteDesigns . revokeSiteDesignRights ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" , rights . map ( u => u . PrincipalName )); Get a history of site designs that have run on a web \u00b6 import { sp } from \"@pnp/sp\" ; const runs = await sp . web . getSiteDesignRuns (); const runs2 = await sp . siteDesigns . getSiteDesignRun ( \"https://TENANT.sharepoint.com/sites/mysite\" ); // Get runs specific to a site design const runs3 = await sp . web . getSiteDesignRuns ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" ); const runs4 = await sp . siteDesigns . getSiteDesignRun ( \"https://TENANT.sharepoint.com/sites/mysite\" , \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" ); // For more information about the site script actions const runStatus = await sp . web . getSiteDesignRunStatus ( runs [ 0 ]. ID ); const runStatus2 = await sp . siteDesigns . getSiteDesignRunStatus ( \"https://TENANT.sharepoint.com/sites/mysite\" , runs [ 0 ]. ID ); Site Scripts \u00b6 Create a new site script \u00b6 import { sp } from \"@pnp/sp\" ; const sitescriptContent = { \"$schema\" : \"schema.json\" , \"actions\" : [ { \"themeName\" : \"Theme Name 123\" , \"verb\" : \"applyTheme\" , }, ], \"bindata\" : {}, \"version\" : 1 , }; const siteScript = await sp . siteScripts . createSiteScript ( \"Title\" , \"description\" , sitescriptContent ); console . log ( siteScript . Title ); Retrieval \u00b6 import { sp } from \"@pnp/sp\" ; // Retrieving all site scripts const allSiteScripts = await sp . siteScripts . getSiteScripts (); console . log ( allSiteScripts . length > 0 ? allSiteScripts [ 0 ]. Title : \"\" ); // Retrieving a single site script by Id const siteScript = await sp . siteScripts . getSiteScriptMetadata ( \"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\" ); console . log ( siteScript . Title ); Update and delete \u00b6 import { sp } from \"@pnp/sp\" ; // Update const updatedSiteScript = await sp . siteScripts . updateSiteScript ({ Id : \"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\" , Title : \"New Title\" }); console . log ( updatedSiteScript . Title ); // Delete await sp . siteScripts . deleteSiteScript ( \"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\" ); Get site script from a list \u00b6 import { sp } from \"@pnp/sp\" ; // Using the absolute URL of the list const ss = await sp . siteScripts . getSiteScriptFromList ( \"https://TENANT.sharepoint.com/Lists/mylist\" ); // Using the PnPjs web object to fetch the site script from a specific list const ss2 = await sp . web . lists . getByTitle ( \"mylist\" ). getSiteScript (); Get site script from a web \u00b6 import { sp } from \"@pnp/sp\" ; const extractInfo = { IncludeBranding : true , IncludeLinksToExportedItems : true , IncludeRegionalSettings : true , IncludeSiteExternalSharingCapability : true , IncludeTheme : true , IncludedLists : [ \"Lists/MyList\" ] }; const ss = await sp . siteScripts . getSiteScriptFromWeb ( \"https://TENANT.sharepoint.com/sites/mysite\" , extractInfo ); // Using the PnPjs web object to fetch the site script from a specific web const ss2 = await sp . web . getSiteScript ( extractInfo );","title":"Site Designs"},{"location":"sp/docs/sitedesigns/#pnpspsitedesigns","text":"You can create site designs to provide reusable lists, themes, layouts, pages, or custom actions so that your users can quickly build new SharePoint sites with the features they need. Check out SharePoint site design and site script overview for more information.","title":"@pnp/sp/sitedesigns"},{"location":"sp/docs/sitedesigns/#site-designs","text":"","title":"Site Designs"},{"location":"sp/docs/sitedesigns/#create-a-new-site-design","text":"import { sp } from \"@pnp/sp\" ; // WebTemplate: 64 Team site template, 68 Communication site template const siteDesign = await sp . siteDesigns . createSiteDesign ({ SiteScriptIds : [ \"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\" ], Title : \"SiteDesign001\" , WebTemplate : \"64\" , }); console . log ( siteDesign . Title );","title":"Create a new site design"},{"location":"sp/docs/sitedesigns/#applying-a-site-design-to-a-site","text":"import { sp } from \"@pnp/sp\" ; // Limited to 30 actions in a site script, but runs synchronously await sp . siteDesigns . applySiteDesign ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" , \"https://contoso.sharepoint.com/sites/teamsite-pnpjs001\" ); // Better use the following method for 300 actions in a site script const task = await sp . web . addSiteDesignTask ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" );","title":"Applying a site design to a site"},{"location":"sp/docs/sitedesigns/#retrieval","text":"import { sp } from \"@pnp/sp\" ; // Retrieving all site designs const allSiteDesigns = await sp . siteDesigns . getSiteDesigns (); console . log ( `Total site designs: ${ allSiteDesigns . length } ` ); // Retrieving a single site design by Id const siteDesign = await sp . siteDesigns . getSiteDesignMetadata ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" ); console . log ( siteDesign . Title );","title":"Retrieval"},{"location":"sp/docs/sitedesigns/#update-and-delete","text":"import { sp } from \"@pnp/sp\" ; // Update const updatedSiteDesign = await sp . siteDesigns . updateSiteDesign ({ Id : \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" , Title : \"SiteDesignUpdatedTitle001\" }); // Delete await sp . siteDesigns . deleteSiteDesign ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" );","title":"Update and delete"},{"location":"sp/docs/sitedesigns/#setting-rightspermissions","text":"import { sp } from \"@pnp/sp\" ; // Get const rights = await sp . siteDesigns . getSiteDesignRights ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" ); console . log ( rights . length > 0 ? rights [ 0 ]. PrincipalName : \"\" ); // Grant await sp . siteDesigns . grantSiteDesignRights ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" , [ \"user@contoso.onmicrosoft.com\" ]); // Revoke await sp . siteDesigns . revokeSiteDesignRights ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" , [ \"user@contoso.onmicrosoft.com\" ]); // Reset all view rights const rights = await sp . siteDesigns . getSiteDesignRights ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" ); await sp . siteDesigns . revokeSiteDesignRights ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" , rights . map ( u => u . PrincipalName ));","title":"Setting Rights/Permissions"},{"location":"sp/docs/sitedesigns/#get-a-history-of-site-designs-that-have-run-on-a-web","text":"import { sp } from \"@pnp/sp\" ; const runs = await sp . web . getSiteDesignRuns (); const runs2 = await sp . siteDesigns . getSiteDesignRun ( \"https://TENANT.sharepoint.com/sites/mysite\" ); // Get runs specific to a site design const runs3 = await sp . web . getSiteDesignRuns ( \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" ); const runs4 = await sp . siteDesigns . getSiteDesignRun ( \"https://TENANT.sharepoint.com/sites/mysite\" , \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\" ); // For more information about the site script actions const runStatus = await sp . web . getSiteDesignRunStatus ( runs [ 0 ]. ID ); const runStatus2 = await sp . siteDesigns . getSiteDesignRunStatus ( \"https://TENANT.sharepoint.com/sites/mysite\" , runs [ 0 ]. ID );","title":"Get a history of site designs that have run on a web"},{"location":"sp/docs/sitedesigns/#site-scripts","text":"","title":"Site Scripts"},{"location":"sp/docs/sitedesigns/#create-a-new-site-script","text":"import { sp } from \"@pnp/sp\" ; const sitescriptContent = { \"$schema\" : \"schema.json\" , \"actions\" : [ { \"themeName\" : \"Theme Name 123\" , \"verb\" : \"applyTheme\" , }, ], \"bindata\" : {}, \"version\" : 1 , }; const siteScript = await sp . siteScripts . createSiteScript ( \"Title\" , \"description\" , sitescriptContent ); console . log ( siteScript . Title );","title":"Create a new site script"},{"location":"sp/docs/sitedesigns/#retrieval_1","text":"import { sp } from \"@pnp/sp\" ; // Retrieving all site scripts const allSiteScripts = await sp . siteScripts . getSiteScripts (); console . log ( allSiteScripts . length > 0 ? allSiteScripts [ 0 ]. Title : \"\" ); // Retrieving a single site script by Id const siteScript = await sp . siteScripts . getSiteScriptMetadata ( \"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\" ); console . log ( siteScript . Title );","title":"Retrieval"},{"location":"sp/docs/sitedesigns/#update-and-delete_1","text":"import { sp } from \"@pnp/sp\" ; // Update const updatedSiteScript = await sp . siteScripts . updateSiteScript ({ Id : \"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\" , Title : \"New Title\" }); console . log ( updatedSiteScript . Title ); // Delete await sp . siteScripts . deleteSiteScript ( \"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\" );","title":"Update and delete"},{"location":"sp/docs/sitedesigns/#get-site-script-from-a-list","text":"import { sp } from \"@pnp/sp\" ; // Using the absolute URL of the list const ss = await sp . siteScripts . getSiteScriptFromList ( \"https://TENANT.sharepoint.com/Lists/mylist\" ); // Using the PnPjs web object to fetch the site script from a specific list const ss2 = await sp . web . lists . getByTitle ( \"mylist\" ). getSiteScript ();","title":"Get site script from a list"},{"location":"sp/docs/sitedesigns/#get-site-script-from-a-web","text":"import { sp } from \"@pnp/sp\" ; const extractInfo = { IncludeBranding : true , IncludeLinksToExportedItems : true , IncludeRegionalSettings : true , IncludeSiteExternalSharingCapability : true , IncludeTheme : true , IncludedLists : [ \"Lists/MyList\" ] }; const ss = await sp . siteScripts . getSiteScriptFromWeb ( \"https://TENANT.sharepoint.com/sites/mysite\" , extractInfo ); // Using the PnPjs web object to fetch the site script from a specific web const ss2 = await sp . web . getSiteScript ( extractInfo );","title":"Get site script from a web"},{"location":"sp/docs/sites/","text":"@pnp/sp/site - Site properties \u00b6 Site collection are one of the fundamental entry points while working with SharePoint. Sites serve as container for webs, lists, features and other entity types. Get context information for the current site collection \u00b6 Using the library, you can get the context information of the current site collection import { sp } from \"@pnp/sp\" ; sp . site . getContextInfo (). then ( d => { console . log ( d . FormDigestValue ); }); Get document libraries of a web \u00b6 Using the library, you can get a list of the document libraries present in the a given web. Note: Works only in SharePoint online import { sp } from \"@pnp/sp\" ; sp . site . getDocumentLibraries ( \"https://tenant.sharepoint.com/sites/test/subsite\" ). then (( d : DocumentLibraryInformation []) => { // iterate over the array of doc lib }); Open Web By Id \u00b6 Because this method is a POST request you can chain off it directly. You will get back the full web properties in the data property of the return object. You can also chain directly off the returned Web instance on the web property. sp . site . openWebById ( \"111ca453-90f5-482e-a381-cee1ff383c9e\" ). then ( w => { //we got all the data from the web as well console . log ( w . data ); // we can chain w . web . select ( \"Title\" ). get (). then ( w2 => { // ... }); }); Get site collection url from page \u00b6 Using the library, you can get the site collection url by providing a page url import { sp } from \"@pnp/sp\" ; sp . site . getWebUrlFromPageUrl ( \"https://tenant.sharepoint.com/sites/test/Pages/test.aspx\" ). then ( d => { console . log ( d ); }); Join a hub site \u00b6 Added in 1.2.4 Note: Works only in SharePoint online Join the current site collection to a hub site collection import { sp , Site } from \"@pnp/sp\" ; var site = new Site ( \"https://tenant.sharepoint.com/sites/HubSite/\" ); var hubSiteID = \"\" ; site . select ( \"ID\" ). get (). then ( d => { // get ID of the hub site collection hubSiteID = d . Id ; // associate the current site collection the hub site collection sp . site . joinHubSite ( hubSiteID ). then ( d => { console . log ( d ); }); }); Disassociate the current site collection from a hub site collection \u00b6 Added in 1.2.4 Note: Works only in SharePoint online import { sp } from \"@pnp/sp\" ; sp . site . joinHubSite ( \"00000000-0000-0000-0000-000000000000\" ). then ( d => { console . log ( d ); }); Register a hub site \u00b6 Added in 1.2.4 Note: Works only in SharePoint online Registers the current site collection as a hub site collection import { sp } from \"@pnp/sp\" ; sp . site . registerHubSite (). then ( d => { console . log ( d ); }); Un-Register a hub site \u00b6 Added in 1.2.4 Note: Works only in SharePoint online Un-Registers the current site collection as a hub site collection import { sp } from \"@pnp/sp\" ; sp . site . unRegisterHubSite (). then ( d => { console . log ( d ); }); Create a modern communication site \u00b6 Added in 1.2.6 Note: Works only in SharePoint online Creates a modern communication site. Property Type Required Description Title string yes The title of the site to create. lcid number yes The default language to use for the site. shareByEmailEnabled boolean yes If set to true, it will enable sharing files via Email. By default it is set to false url string yes The fully qualified URL (e.g. https://yourtenant.sharepoint.com/sites/mysitecollection) of the site. description string no The description of the communication site. classification string no The Site classification to use. For instance 'Contoso Classified'. See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information siteDesignId string no The Guid of the site design to be used. You can use the below default OOTB GUIDs: Topic: null Showcase: 6142d2a0-63a5-4ba0-aede-d9fefca2c767 Blank: f6cc5403-0d63-442e-96c0-285923709ffc hubSiteId string no The Guid of the already existing Hub site owner string no Required when using app-only context. Owner principal name e.g. user@tenant.onmicrosoft.com import { sp } from \"@pnp/sp\" ; const s = await sp . site . createCommunicationSite ( \"Title\" , 1033 , true , \"https://tenant.sharepoint.com/sites/commSite\" , \"Description\" , \"HBI\" , \"f6cc5403-0d63-442e-96c0-285923709ffc\" , \"a00ec589-ea9f-4dba-a34e-67e78d41e509\" , \"user@TENANT.onmicrosoft.com\" ); Create a modern team site \u00b6 Added in 1.2.6 Note: Works only in SharePoint online. It wont work with App only tokens Creates a modern team site backed by O365 group. Property Type Required Description displayName string yes The title/displayName of the site to be created. alias string yes Alias of the underlying Office 365 Group. isPublic boolean yes Defines whether the Office 365 Group will be public (default), or private. lcid number yes The language to use for the site. If not specified will default to English (1033). description string no The description of the modern team site. classification string no The Site classification to use. For instance 'Contoso Classified'. See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information owners string array (string[]) no The Owners of the site to be created hubSiteId string no The Guid of the already existing Hub site import { sp } from \"@pnp/sp\" ; sp . site . createModernTeamSite ( \"displayName\" , \"alias\" , true , 1033 , \"description\" , \"HBI\" , [ \"user1@tenant.onmicrosoft.com\" , \"user2@tenant.onmicrosoft.com\" , \"user3@tenant.onmicrosoft.com\" ], \"a00ec589-ea9f-4dba-a34e-67e78d41e509\" ) . then ( d => { console . log ( d ); }); Delete a site collection \u00b6 import { sp } from \"@pnp/sp\" ; // Delete the current site await sp . site . delete (); // Specify which site to delete const siteUrl = \"https://tenant.sharepoint.com/sites/tstpnpsitecoldelete5\" ; const site2 = new Site ( siteUrl ); await site2 . delete ();","title":"Sites"},{"location":"sp/docs/sites/#pnpspsite-site-properties","text":"Site collection are one of the fundamental entry points while working with SharePoint. Sites serve as container for webs, lists, features and other entity types.","title":"@pnp/sp/site - Site properties"},{"location":"sp/docs/sites/#get-context-information-for-the-current-site-collection","text":"Using the library, you can get the context information of the current site collection import { sp } from \"@pnp/sp\" ; sp . site . getContextInfo (). then ( d => { console . log ( d . FormDigestValue ); });","title":"Get context information for the current site collection"},{"location":"sp/docs/sites/#get-document-libraries-of-a-web","text":"Using the library, you can get a list of the document libraries present in the a given web. Note: Works only in SharePoint online import { sp } from \"@pnp/sp\" ; sp . site . getDocumentLibraries ( \"https://tenant.sharepoint.com/sites/test/subsite\" ). then (( d : DocumentLibraryInformation []) => { // iterate over the array of doc lib });","title":"Get document libraries of a web"},{"location":"sp/docs/sites/#open-web-by-id","text":"Because this method is a POST request you can chain off it directly. You will get back the full web properties in the data property of the return object. You can also chain directly off the returned Web instance on the web property. sp . site . openWebById ( \"111ca453-90f5-482e-a381-cee1ff383c9e\" ). then ( w => { //we got all the data from the web as well console . log ( w . data ); // we can chain w . web . select ( \"Title\" ). get (). then ( w2 => { // ... }); });","title":"Open Web By Id"},{"location":"sp/docs/sites/#get-site-collection-url-from-page","text":"Using the library, you can get the site collection url by providing a page url import { sp } from \"@pnp/sp\" ; sp . site . getWebUrlFromPageUrl ( \"https://tenant.sharepoint.com/sites/test/Pages/test.aspx\" ). then ( d => { console . log ( d ); });","title":"Get site collection url from page"},{"location":"sp/docs/sites/#join-a-hub-site","text":"Added in 1.2.4 Note: Works only in SharePoint online Join the current site collection to a hub site collection import { sp , Site } from \"@pnp/sp\" ; var site = new Site ( \"https://tenant.sharepoint.com/sites/HubSite/\" ); var hubSiteID = \"\" ; site . select ( \"ID\" ). get (). then ( d => { // get ID of the hub site collection hubSiteID = d . Id ; // associate the current site collection the hub site collection sp . site . joinHubSite ( hubSiteID ). then ( d => { console . log ( d ); }); });","title":"Join a hub site"},{"location":"sp/docs/sites/#disassociate-the-current-site-collection-from-a-hub-site-collection","text":"Added in 1.2.4 Note: Works only in SharePoint online import { sp } from \"@pnp/sp\" ; sp . site . joinHubSite ( \"00000000-0000-0000-0000-000000000000\" ). then ( d => { console . log ( d ); });","title":"Disassociate the current site collection from a hub site collection"},{"location":"sp/docs/sites/#register-a-hub-site","text":"Added in 1.2.4 Note: Works only in SharePoint online Registers the current site collection as a hub site collection import { sp } from \"@pnp/sp\" ; sp . site . registerHubSite (). then ( d => { console . log ( d ); });","title":"Register a hub site"},{"location":"sp/docs/sites/#un-register-a-hub-site","text":"Added in 1.2.4 Note: Works only in SharePoint online Un-Registers the current site collection as a hub site collection import { sp } from \"@pnp/sp\" ; sp . site . unRegisterHubSite (). then ( d => { console . log ( d ); });","title":"Un-Register a hub site"},{"location":"sp/docs/sites/#create-a-modern-communication-site","text":"Added in 1.2.6 Note: Works only in SharePoint online Creates a modern communication site. Property Type Required Description Title string yes The title of the site to create. lcid number yes The default language to use for the site. shareByEmailEnabled boolean yes If set to true, it will enable sharing files via Email. By default it is set to false url string yes The fully qualified URL (e.g. https://yourtenant.sharepoint.com/sites/mysitecollection) of the site. description string no The description of the communication site. classification string no The Site classification to use. For instance 'Contoso Classified'. See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information siteDesignId string no The Guid of the site design to be used. You can use the below default OOTB GUIDs: Topic: null Showcase: 6142d2a0-63a5-4ba0-aede-d9fefca2c767 Blank: f6cc5403-0d63-442e-96c0-285923709ffc hubSiteId string no The Guid of the already existing Hub site owner string no Required when using app-only context. Owner principal name e.g. user@tenant.onmicrosoft.com import { sp } from \"@pnp/sp\" ; const s = await sp . site . createCommunicationSite ( \"Title\" , 1033 , true , \"https://tenant.sharepoint.com/sites/commSite\" , \"Description\" , \"HBI\" , \"f6cc5403-0d63-442e-96c0-285923709ffc\" , \"a00ec589-ea9f-4dba-a34e-67e78d41e509\" , \"user@TENANT.onmicrosoft.com\" );","title":"Create a modern communication site"},{"location":"sp/docs/sites/#create-a-modern-team-site","text":"Added in 1.2.6 Note: Works only in SharePoint online. It wont work with App only tokens Creates a modern team site backed by O365 group. Property Type Required Description displayName string yes The title/displayName of the site to be created. alias string yes Alias of the underlying Office 365 Group. isPublic boolean yes Defines whether the Office 365 Group will be public (default), or private. lcid number yes The language to use for the site. If not specified will default to English (1033). description string no The description of the modern team site. classification string no The Site classification to use. For instance 'Contoso Classified'. See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information owners string array (string[]) no The Owners of the site to be created hubSiteId string no The Guid of the already existing Hub site import { sp } from \"@pnp/sp\" ; sp . site . createModernTeamSite ( \"displayName\" , \"alias\" , true , 1033 , \"description\" , \"HBI\" , [ \"user1@tenant.onmicrosoft.com\" , \"user2@tenant.onmicrosoft.com\" , \"user3@tenant.onmicrosoft.com\" ], \"a00ec589-ea9f-4dba-a34e-67e78d41e509\" ) . then ( d => { console . log ( d ); });","title":"Create a modern team site"},{"location":"sp/docs/sites/#delete-a-site-collection","text":"import { sp } from \"@pnp/sp\" ; // Delete the current site await sp . site . delete (); // Specify which site to delete const siteUrl = \"https://tenant.sharepoint.com/sites/tstpnpsitecoldelete5\" ; const site2 = new Site ( siteUrl ); await site2 . delete ();","title":"Delete a site collection"},{"location":"sp/docs/social/","text":"@pnp/sp/social \u00b6 The social API allows you to track followed sites, people, and docs. Note, many of these methods only work with the context of a logged in user, and not with app-only permissions. getFollowedSitesUri \u00b6 Gets a URI to a site that lists the current user's followed sites. import { sp } from \"@pnp/sp\" ; const uri = await sp . social . getFollowedSitesUri (); getFollowedDocumentsUri \u00b6 Gets a URI to a site that lists the current user's followed documents. import { sp } from \"@pnp/sp\" ; const uri = await sp . social . getFollowedDocumentsUri (); follow \u00b6 Makes the current user start following a user, document, site, or tag import { sp , SocialActorType } from \"@pnp/sp\" ; // follow a site const r1 = await sp . social . follow ({ ActorType : SocialActorType.Site , ContentUri : \"htts://tenant.sharepoint.com/sites/site\" , }); // follow a person const r2 = await sp . social . follow ({ AccountName : \"i:0#.f|membership|person@tenant.com\" , ActorType : SocialActorType.User , }); // follow a doc const r3 = await sp . social . follow ({ ActorType : SocialActorType.Document , ContentUri : \"https://tenant.sharepoint.com/sites/dev/SitePages/Test.aspx\" , }); // follow a tag // You need the tag GUID to start following a tag. // You can't get the GUID by using the REST service, but you can use the .NET client object model or the JavaScript object model. // See How to get a tag's GUID based on the tag's name by using the JavaScript object model. // https://docs.microsoft.com/en-us/sharepoint/dev/general-development/follow-content-in-sharepoint#bk_getTagGuid const r4 = await sp . social . follow ({ ActorType : SocialActorType.Tag , TagGuid : \"19a4a484-c1dc-4bc5-8c93-bb96245ce928\" , }); isFollowed \u00b6 Indicates whether the current user is following a specified user, document, site, or tag import { sp , SocialActorType } from \"@pnp/sp\" ; // pass the same social actor struct as shown in follow example for each type const r = await sp . social . isFollowed ({ AccountName : \"i:0#.f|membership|person@tenant.com\" , ActorType : SocialActorType.User , }); stopFollowing \u00b6 Makes the current user stop following a user, document, site, or tag import { sp , SocialActorType } from \"@pnp/sp\" ; // pass the same social actor struct as shown in follow example for each type const r = await sp . social . stopFollowing ({ AccountName : \"i:0#.f|membership|person@tenant.com\" , ActorType : SocialActorType.User , }); my \u00b6 get \u00b6 Gets this user's social information import { sp } from \"@pnp/sp\" ; const r = await sp . social . my . get (); followed \u00b6 Gets users, documents, sites, and tags that the current user is following based on the supplied flags. import { sp , SocialActorTypes } from \"@pnp/sp\" ; // get all the followed documents const r1 = await sp . social . my . followed ( SocialActorTypes . Document ); // get all the followed documents and sites const r2 = await sp . social . my . followed ( SocialActorTypes . Document | SocialActorTypes . Site ); // get all the followed sites updated in the last 24 hours const r3 = await sp . social . my . followed ( SocialActorTypes . Site | SocialActorTypes . WithinLast24Hours ); followedCount \u00b6 Works as followed but returns on the count of actors specifed by the query import { sp , SocialActorTypes } from \"@pnp/sp\" ; // get the followed documents count const r = await sp . social . my . followedCount ( SocialActorTypes . Document ); followers \u00b6 Gets the users who are following the current user. import { sp } from \"@pnp/sp\" ; // get the followed documents count const r = await sp . social . my . followers (); suggestions \u00b6 Gets users who the current user might want to follow. import { sp } from \"@pnp/sp\" ; // get the followed documents count const r = await sp . social . my . suggestions ();","title":"Social"},{"location":"sp/docs/social/#pnpspsocial","text":"The social API allows you to track followed sites, people, and docs. Note, many of these methods only work with the context of a logged in user, and not with app-only permissions.","title":"@pnp/sp/social"},{"location":"sp/docs/social/#getfollowedsitesuri","text":"Gets a URI to a site that lists the current user's followed sites. import { sp } from \"@pnp/sp\" ; const uri = await sp . social . getFollowedSitesUri ();","title":"getFollowedSitesUri"},{"location":"sp/docs/social/#getfolloweddocumentsuri","text":"Gets a URI to a site that lists the current user's followed documents. import { sp } from \"@pnp/sp\" ; const uri = await sp . social . getFollowedDocumentsUri ();","title":"getFollowedDocumentsUri"},{"location":"sp/docs/social/#follow","text":"Makes the current user start following a user, document, site, or tag import { sp , SocialActorType } from \"@pnp/sp\" ; // follow a site const r1 = await sp . social . follow ({ ActorType : SocialActorType.Site , ContentUri : \"htts://tenant.sharepoint.com/sites/site\" , }); // follow a person const r2 = await sp . social . follow ({ AccountName : \"i:0#.f|membership|person@tenant.com\" , ActorType : SocialActorType.User , }); // follow a doc const r3 = await sp . social . follow ({ ActorType : SocialActorType.Document , ContentUri : \"https://tenant.sharepoint.com/sites/dev/SitePages/Test.aspx\" , }); // follow a tag // You need the tag GUID to start following a tag. // You can't get the GUID by using the REST service, but you can use the .NET client object model or the JavaScript object model. // See How to get a tag's GUID based on the tag's name by using the JavaScript object model. // https://docs.microsoft.com/en-us/sharepoint/dev/general-development/follow-content-in-sharepoint#bk_getTagGuid const r4 = await sp . social . follow ({ ActorType : SocialActorType.Tag , TagGuid : \"19a4a484-c1dc-4bc5-8c93-bb96245ce928\" , });","title":"follow"},{"location":"sp/docs/social/#isfollowed","text":"Indicates whether the current user is following a specified user, document, site, or tag import { sp , SocialActorType } from \"@pnp/sp\" ; // pass the same social actor struct as shown in follow example for each type const r = await sp . social . isFollowed ({ AccountName : \"i:0#.f|membership|person@tenant.com\" , ActorType : SocialActorType.User , });","title":"isFollowed"},{"location":"sp/docs/social/#stopfollowing","text":"Makes the current user stop following a user, document, site, or tag import { sp , SocialActorType } from \"@pnp/sp\" ; // pass the same social actor struct as shown in follow example for each type const r = await sp . social . stopFollowing ({ AccountName : \"i:0#.f|membership|person@tenant.com\" , ActorType : SocialActorType.User , });","title":"stopFollowing"},{"location":"sp/docs/social/#my","text":"","title":"my"},{"location":"sp/docs/social/#get","text":"Gets this user's social information import { sp } from \"@pnp/sp\" ; const r = await sp . social . my . get ();","title":"get"},{"location":"sp/docs/social/#followed","text":"Gets users, documents, sites, and tags that the current user is following based on the supplied flags. import { sp , SocialActorTypes } from \"@pnp/sp\" ; // get all the followed documents const r1 = await sp . social . my . followed ( SocialActorTypes . Document ); // get all the followed documents and sites const r2 = await sp . social . my . followed ( SocialActorTypes . Document | SocialActorTypes . Site ); // get all the followed sites updated in the last 24 hours const r3 = await sp . social . my . followed ( SocialActorTypes . Site | SocialActorTypes . WithinLast24Hours );","title":"followed"},{"location":"sp/docs/social/#followedcount","text":"Works as followed but returns on the count of actors specifed by the query import { sp , SocialActorTypes } from \"@pnp/sp\" ; // get the followed documents count const r = await sp . social . my . followedCount ( SocialActorTypes . Document );","title":"followedCount"},{"location":"sp/docs/social/#followers","text":"Gets the users who are following the current user. import { sp } from \"@pnp/sp\" ; // get the followed documents count const r = await sp . social . my . followers ();","title":"followers"},{"location":"sp/docs/social/#suggestions","text":"Gets users who the current user might want to follow. import { sp } from \"@pnp/sp\" ; // get the followed documents count const r = await sp . social . my . suggestions ();","title":"suggestions"},{"location":"sp/docs/sp-utilities-utility/","text":"@pnp/sp/utilities \u00b6 Through the REST api you are able to call a subset of the SP.Utilities.Utility methods. We have explicitly defined some of these methods and provided a method to call any others in a generic manner. These methods are exposed on pnp.sp.utility and support batching and caching. sendEmail \u00b6 This methods allows you to send an email based on the supplied arguments. The method takes a single argument, a plain object defined by the EmailProperties interface (shown below). EmailProperties \u00b6 export interface EmailProperties { To : string []; CC? : string []; BCC? : string []; Subject : string ; Body : string ; AdditionalHeaders? : TypedHash < string > ; From? : string ; } Usage \u00b6 You must define the To, Subject, and Body values - the remaining are optional. import { sp , EmailProperties } from \"@pnp/sp\" ; const emailProps : EmailProperties = { To : [ \"user@site.com\" ], CC : [ \"user2@site.com\" , \"user3@site.com\" ], Subject : \"This email is about...\" , Body : \"Here is the body. It supports html\" , }; sp . utility . sendEmail ( emailProps ). then ( _ => { console . log ( \"Email Sent!\" ); }); getCurrentUserEmailAddresses \u00b6 This method returns the current user's email addresses known to SharePoint. import { sp } from \"@pnp/sp\" ; sp . utility . getCurrentUserEmailAddresses (). then (( addressString : string ) => { console . log ( addressString ); }); resolvePrincipal \u00b6 Gets information about a principal that matches the specified Search criteria import { sp , PrincipalType , PrincipalSource , PrincipalInfo } from \"@pnp/sp\" ; sp . utility . resolvePrincipal ( \"user@site.com\" , PrincipalType . User , PrincipalSource . All , true , false ). then (( principal : PrincipalInfo ) => { console . log ( principal ); }); searchPrincipals \u00b6 Gets information about the principals that match the specified Search criteria. import { sp , PrincipalType , PrincipalSource , PrincipalInfo } from \"@pnp/sp\" ; sp . utility . searchPrincipals ( \"john\" , PrincipalType . User , PrincipalSource . All , \"\" , 10 ). then (( principals : PrincipalInfo []) => { console . log ( principals ); }); createEmailBodyForInvitation \u00b6 Gets the external (outside the firewall) URL to a document or resource in a site. import { sp } from \"@pnp/sp\" ; sp . utility . createEmailBodyForInvitation ( \"https://contoso.sharepoint.com/sites/dev/SitePages/DevHome.aspx\" ). then (( r : string ) => { console . log ( r ); }); expandGroupsToPrincipals \u00b6 Resolves the principals contained within the supplied groups import { sp , PrincipalInfo } from \"@pnp/sp\" ; sp . utility . expandGroupsToPrincipals ([ \"Dev Owners\" , \"Dev Members\" ]). then (( principals : PrincipalInfo []) => { console . log ( principals ); }); // optionally supply a max results count. Default is 30. sp . utility . expandGroupsToPrincipals ([ \"Dev Owners\" , \"Dev Members\" ], 10 ). then (( principals : PrincipalInfo []) => { console . log ( principals ); }); createWikiPage \u00b6 import { sp , CreateWikiPageResult } from \"@pnp/sp\" ; sp . utility . createWikiPage ({ ServerRelativeUrl : \"/sites/dev/SitePages/mynewpage.aspx\" , WikiHtmlContent : \"This is my page content. It supports rich html.\" , }). then (( result : CreateWikiPageResult ) => { // result contains the raw data returned by the service console . log ( result . data ); // result contains a File instance you can use to further update the new page result . file . get (). then ( f => { console . log ( f ); }); }); containsInvalidFileFolderChars \u00b6 Checks if file or folder name contains invalid characters import { sp } from \"@pnp/sp\" ; const isInvalid = sp . utility . containsInvalidFileFolderChars ( \"Filename?.txt\" ); console . log ( isInvalid ); // true stripInvalidFileFolderChars \u00b6 Removes invalid characters from file or folder name import { sp } from \"@pnp/sp\" ; const validName = sp . utility . stripInvalidFileFolderChars ( \"Filename?.txt\" ); console . log ( validName ); // Filename.txt Call Other Methods \u00b6 Even if a method does not have an explicit implementation on the utility api you can still call it using the UtilityMethod class. In this example we will show calling the GetLowerCaseString method, but the technique works for any of the utility methods. import { UtilityMethod } from \"@pnp/sp\" ; // the first parameter is the web url. You can use an empty string for the current web, // or specify it to call other web's. The second parameter is the method name. const method = new UtilityMethod ( \"\" , \"GetLowerCaseString\" ); // you must supply the correctly formatted parameters to the execute method which // is generic and types the result as the supplied generic type parameter. method . excute < string > ({ sourceValue : \"HeRe IS my StrINg\" , lcid : 1033 , }). then (( s : string ) => { console . log ( s ); });","title":"SP.Utilities.Utility"},{"location":"sp/docs/sp-utilities-utility/#pnpsputilities","text":"Through the REST api you are able to call a subset of the SP.Utilities.Utility methods. We have explicitly defined some of these methods and provided a method to call any others in a generic manner. These methods are exposed on pnp.sp.utility and support batching and caching.","title":"@pnp/sp/utilities"},{"location":"sp/docs/sp-utilities-utility/#sendemail","text":"This methods allows you to send an email based on the supplied arguments. The method takes a single argument, a plain object defined by the EmailProperties interface (shown below).","title":"sendEmail"},{"location":"sp/docs/sp-utilities-utility/#emailproperties","text":"export interface EmailProperties { To : string []; CC? : string []; BCC? : string []; Subject : string ; Body : string ; AdditionalHeaders? : TypedHash < string > ; From? : string ; }","title":"EmailProperties"},{"location":"sp/docs/sp-utilities-utility/#usage","text":"You must define the To, Subject, and Body values - the remaining are optional. import { sp , EmailProperties } from \"@pnp/sp\" ; const emailProps : EmailProperties = { To : [ \"user@site.com\" ], CC : [ \"user2@site.com\" , \"user3@site.com\" ], Subject : \"This email is about...\" , Body : \"Here is the body. It supports html\" , }; sp . utility . sendEmail ( emailProps ). then ( _ => { console . log ( \"Email Sent!\" ); });","title":"Usage"},{"location":"sp/docs/sp-utilities-utility/#getcurrentuseremailaddresses","text":"This method returns the current user's email addresses known to SharePoint. import { sp } from \"@pnp/sp\" ; sp . utility . getCurrentUserEmailAddresses (). then (( addressString : string ) => { console . log ( addressString ); });","title":"getCurrentUserEmailAddresses"},{"location":"sp/docs/sp-utilities-utility/#resolveprincipal","text":"Gets information about a principal that matches the specified Search criteria import { sp , PrincipalType , PrincipalSource , PrincipalInfo } from \"@pnp/sp\" ; sp . utility . resolvePrincipal ( \"user@site.com\" , PrincipalType . User , PrincipalSource . All , true , false ). then (( principal : PrincipalInfo ) => { console . log ( principal ); });","title":"resolvePrincipal"},{"location":"sp/docs/sp-utilities-utility/#searchprincipals","text":"Gets information about the principals that match the specified Search criteria. import { sp , PrincipalType , PrincipalSource , PrincipalInfo } from \"@pnp/sp\" ; sp . utility . searchPrincipals ( \"john\" , PrincipalType . User , PrincipalSource . All , \"\" , 10 ). then (( principals : PrincipalInfo []) => { console . log ( principals ); });","title":"searchPrincipals"},{"location":"sp/docs/sp-utilities-utility/#createemailbodyforinvitation","text":"Gets the external (outside the firewall) URL to a document or resource in a site. import { sp } from \"@pnp/sp\" ; sp . utility . createEmailBodyForInvitation ( \"https://contoso.sharepoint.com/sites/dev/SitePages/DevHome.aspx\" ). then (( r : string ) => { console . log ( r ); });","title":"createEmailBodyForInvitation"},{"location":"sp/docs/sp-utilities-utility/#expandgroupstoprincipals","text":"Resolves the principals contained within the supplied groups import { sp , PrincipalInfo } from \"@pnp/sp\" ; sp . utility . expandGroupsToPrincipals ([ \"Dev Owners\" , \"Dev Members\" ]). then (( principals : PrincipalInfo []) => { console . log ( principals ); }); // optionally supply a max results count. Default is 30. sp . utility . expandGroupsToPrincipals ([ \"Dev Owners\" , \"Dev Members\" ], 10 ). then (( principals : PrincipalInfo []) => { console . log ( principals ); });","title":"expandGroupsToPrincipals"},{"location":"sp/docs/sp-utilities-utility/#createwikipage","text":"import { sp , CreateWikiPageResult } from \"@pnp/sp\" ; sp . utility . createWikiPage ({ ServerRelativeUrl : \"/sites/dev/SitePages/mynewpage.aspx\" , WikiHtmlContent : \"This is my page content. It supports rich html.\" , }). then (( result : CreateWikiPageResult ) => { // result contains the raw data returned by the service console . log ( result . data ); // result contains a File instance you can use to further update the new page result . file . get (). then ( f => { console . log ( f ); }); });","title":"createWikiPage"},{"location":"sp/docs/sp-utilities-utility/#containsinvalidfilefolderchars","text":"Checks if file or folder name contains invalid characters import { sp } from \"@pnp/sp\" ; const isInvalid = sp . utility . containsInvalidFileFolderChars ( \"Filename?.txt\" ); console . log ( isInvalid ); // true","title":"containsInvalidFileFolderChars"},{"location":"sp/docs/sp-utilities-utility/#stripinvalidfilefolderchars","text":"Removes invalid characters from file or folder name import { sp } from \"@pnp/sp\" ; const validName = sp . utility . stripInvalidFileFolderChars ( \"Filename?.txt\" ); console . log ( validName ); // Filename.txt","title":"stripInvalidFileFolderChars"},{"location":"sp/docs/sp-utilities-utility/#call-other-methods","text":"Even if a method does not have an explicit implementation on the utility api you can still call it using the UtilityMethod class. In this example we will show calling the GetLowerCaseString method, but the technique works for any of the utility methods. import { UtilityMethod } from \"@pnp/sp\" ; // the first parameter is the web url. You can use an empty string for the current web, // or specify it to call other web's. The second parameter is the method name. const method = new UtilityMethod ( \"\" , \"GetLowerCaseString\" ); // you must supply the correctly formatted parameters to the execute method which // is generic and types the result as the supplied generic type parameter. method . excute < string > ({ sourceValue : \"HeRe IS my StrINg\" , lcid : 1033 , }). then (( s : string ) => { console . log ( s ); });","title":"Call Other Methods"},{"location":"sp/docs/tenant-properties/","text":"@pnp/sp/web - tenant properties \u00b6 You can set, read, and remove tenant properties using the methods shown below: setStorageEntity \u00b6 This method MUST be called in the context of the app catalog web or you will get an access denied message. import { Web } from \"@pnp/sp\" ; const w = new Web ( \"https://tenant.sharepoint.com/sites/appcatalog/\" ); // specify required key and value await w . setStorageEntity ( \"Test1\" , \"Value 1\" ); // specify optional description and comments await w . setStorageEntity ( \"Test2\" , \"Value 2\" , \"description\" , \"comments\" ); getStorageEntity \u00b6 This method can be used from any web to retrieve values previsouly set. import { sp , StorageEntity } from \"@pnp/sp\" ; const prop : StorageEntity = await sp . web . getStorageEntity ( \"Test1\" ); console . log ( prop . Value ); removeStorageEntity \u00b6 This method MUST be called in the context of the app catalog web or you will get an access denied message. import { Web } from \"@pnp/sp\" ; const w = new Web ( \"https://tenant.sharepoint.com/sites/appcatalog/\" ); await w . removeStorageEntity ( \"Test1\" );","title":"Tenant Properties"},{"location":"sp/docs/tenant-properties/#pnpspweb-tenant-properties","text":"You can set, read, and remove tenant properties using the methods shown below:","title":"@pnp/sp/web - tenant properties"},{"location":"sp/docs/tenant-properties/#setstorageentity","text":"This method MUST be called in the context of the app catalog web or you will get an access denied message. import { Web } from \"@pnp/sp\" ; const w = new Web ( \"https://tenant.sharepoint.com/sites/appcatalog/\" ); // specify required key and value await w . setStorageEntity ( \"Test1\" , \"Value 1\" ); // specify optional description and comments await w . setStorageEntity ( \"Test2\" , \"Value 2\" , \"description\" , \"comments\" );","title":"setStorageEntity"},{"location":"sp/docs/tenant-properties/#getstorageentity","text":"This method can be used from any web to retrieve values previsouly set. import { sp , StorageEntity } from \"@pnp/sp\" ; const prop : StorageEntity = await sp . web . getStorageEntity ( \"Test1\" ); console . log ( prop . Value );","title":"getStorageEntity"},{"location":"sp/docs/tenant-properties/#removestorageentity","text":"This method MUST be called in the context of the app catalog web or you will get an access denied message. import { Web } from \"@pnp/sp\" ; const w = new Web ( \"https://tenant.sharepoint.com/sites/appcatalog/\" ); await w . removeStorageEntity ( \"Test1\" );","title":"removeStorageEntity"},{"location":"sp/docs/views/","text":"@pnp/sp/views \u00b6 Views define the columns, ordering, and other details we see when we look at a list. You can have multiple views for a list, including private views - and one default view. Get a View's Properties \u00b6 To get a views properties you need to know it's id or title. You can use the standard OData operators as expected to select properties. For a list of the properties, please see this article . import { sp } from \"@pnp/sp\" ; // know a view's GUID id sp . web . lists . getByTitle ( \"Documents\" ). getView ( \"2B382C69-DF64-49C4-85F1-70FB9CECACFE\" ). select ( \"Title\" ). get (). then ( v => { console . log ( v ); }); // get by the display title of the view sp . web . lists . getByTitle ( \"Documents\" ). views . getByTitle ( \"All Documents\" ). select ( \"Title\" ). get (). then ( v => { console . log ( v ); }); Add a View \u00b6 To add a view you use the add method of the views collection. You must supply a title and can supply other parameters as well. import { sp , ViewAddResult } from \"@pnp/sp\" ; // create a new view with default fields and properties sp . web . lists . getByTitle ( \"Documents\" ). views . add ( \"My New View\" ). then ( v => { console . log ( v ); }); // create a new view with specific properties sp . web . lists . getByTitle ( \"Documents\" ). views . add ( \"My New View 2\" , false , { RowLimit : 10 , ViewQuery : \"\" , }). then (( v : ViewAddResult ) => { // manipulate the view's fields v . view . fields . removeAll (). then ( _ => { Promise . all ([ v . view . fields . add ( \"Title\" ), v . view . fields . add ( \"Modified\" ), ]). then ( _ => { console . log ( \"View created\" ); }); }); }); Update a View \u00b6 import { sp , ViewUpdateResult } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( \"Documents\" ). views . getByTitle ( \"My New View\" ). update ({ RowLimit : 20 , }). then (( v : ViewUpdateResult ) => { console . log ( v ); }); Set View XML \u00b6 Added in 1.2.6 import { sp } from \"@pnp/sp\" ; const viewXml : string = \"...\" ; await sp . web . lists . getByTitle ( \"Documents\" ). views . getByTitle ( \"My New View\" ). setViewXml ( viewXml ); Delete a View \u00b6 import { sp } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( \"Documents\" ). views . getByTitle ( \"My New View\" ). delete (). then ( _ => { console . log ( \"View deleted\" ); });","title":"Views"},{"location":"sp/docs/views/#pnpspviews","text":"Views define the columns, ordering, and other details we see when we look at a list. You can have multiple views for a list, including private views - and one default view.","title":"@pnp/sp/views"},{"location":"sp/docs/views/#get-a-views-properties","text":"To get a views properties you need to know it's id or title. You can use the standard OData operators as expected to select properties. For a list of the properties, please see this article . import { sp } from \"@pnp/sp\" ; // know a view's GUID id sp . web . lists . getByTitle ( \"Documents\" ). getView ( \"2B382C69-DF64-49C4-85F1-70FB9CECACFE\" ). select ( \"Title\" ). get (). then ( v => { console . log ( v ); }); // get by the display title of the view sp . web . lists . getByTitle ( \"Documents\" ). views . getByTitle ( \"All Documents\" ). select ( \"Title\" ). get (). then ( v => { console . log ( v ); });","title":"Get a View's Properties"},{"location":"sp/docs/views/#add-a-view","text":"To add a view you use the add method of the views collection. You must supply a title and can supply other parameters as well. import { sp , ViewAddResult } from \"@pnp/sp\" ; // create a new view with default fields and properties sp . web . lists . getByTitle ( \"Documents\" ). views . add ( \"My New View\" ). then ( v => { console . log ( v ); }); // create a new view with specific properties sp . web . lists . getByTitle ( \"Documents\" ). views . add ( \"My New View 2\" , false , { RowLimit : 10 , ViewQuery : \"\" , }). then (( v : ViewAddResult ) => { // manipulate the view's fields v . view . fields . removeAll (). then ( _ => { Promise . all ([ v . view . fields . add ( \"Title\" ), v . view . fields . add ( \"Modified\" ), ]). then ( _ => { console . log ( \"View created\" ); }); }); });","title":"Add a View"},{"location":"sp/docs/views/#update-a-view","text":"import { sp , ViewUpdateResult } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( \"Documents\" ). views . getByTitle ( \"My New View\" ). update ({ RowLimit : 20 , }). then (( v : ViewUpdateResult ) => { console . log ( v ); });","title":"Update a View"},{"location":"sp/docs/views/#set-view-xml","text":"Added in 1.2.6 import { sp } from \"@pnp/sp\" ; const viewXml : string = \"...\" ; await sp . web . lists . getByTitle ( \"Documents\" ). views . getByTitle ( \"My New View\" ). setViewXml ( viewXml );","title":"Set View XML"},{"location":"sp/docs/views/#delete-a-view","text":"import { sp } from \"@pnp/sp\" ; sp . web . lists . getByTitle ( \"Documents\" ). views . getByTitle ( \"My New View\" ). delete (). then ( _ => { console . log ( \"View deleted\" ); });","title":"Delete a View"},{"location":"sp/docs/webs/","text":"@pnp/sp/webs \u00b6 Webs are one of the fundamental entry points when working with SharePoint. Webs serve as a container for lists, features, sub-webs, and all of the entity types. Add a Web \u00b6 Using the library you can add a web to another web's collection of subwebs. The basic usage requires only a title and url. This will result in a team site with all of the default settings. import { sp , WebAddResult } from \"@pnp/sp\" ; sp . web . webs . add ( \"title\" , \"subweb1\" ). then (( w : WebAddResult ) => { // show the response from the server when adding the web console . log ( w . data ); w . web . select ( \"Title\" ). get (). then ( w => { // show our title console . log ( w . Title ); }); }); You can also provide other settings such as description, template, language, and inherit permissions. import { sp , WebAddResult } from \"@pnp/sp\" ; // create a German language wiki site with title, url, description, which inherits permissions sp . web . webs . add ( \"wiki\" , \"subweb2\" , \"a wiki web\" , \"WIKI#0\" , 1031 , true ). then (( w : WebAddResult ) => { // show the response from the server when adding the web console . log ( w . data ); w . web . select ( \"Title\" ). get (). then ( w => { // show our title console . log ( w . Title ); }); }); Create Default Associated Groups \u00b6 If you create a web that doesn't inherit permissions from the parent web, you can create its default associated groups (Members, Owners, Visitors) with the default role assigments (Contribute, Full Control, Read) import { sp , WebAddResult } from \"@pnp/sp\" ; sp . web . webs . add ( \"title\" , \"subweb1\" , \"a wiki web\" , \"WIKI#0\" , 1031 , false ). then (( w : WebAddResult ) => { w . web . createDefaultAssociatedGroups (). then (() => { // ... }); }); Get A Web's properties \u00b6 import { sp } from \"@pnp/sp\" ; // basic get of the webs properties sp . web . get (). then ( w => { console . log ( w . Title ); }); // use odata operators to get specific fields sp . web . select ( \"Title\" ). get (). then ( w => { console . log ( w . Title ); }); // use with get to give the result a type sp . web . select ( \"Title\" ). get < { Title : string } > (). then ( w => { console . log ( w . Title ); }); Get Complex Properties \u00b6 Some properties, such as AllProperties, are not returned by default. You can still access them using the expand operator. import { sp } from \"@pnp/sp\" ; sp . web . select ( \"AllProperties\" ). expand ( \"AllProperties\" ). get (). then ( w => { console . log ( w . AllProperties ); }); Get a Web Directly \u00b6 You can also use the Web object directly to get any web, though of course the current user must have the necessary permissions. This is done by importing the web object. import { Web } from \"@pnp/sp\" ; let web = new Web ( \"https://my-tenant.sharepoint.com/sites/mysite\" ); web . get (). then ( w => { console . log ( w ); }); Open Web By Id \u00b6 Because this method is a POST request you can chain off it directly. You will get back the full web properties in the data property of the return object. You can also chain directly off the returned Web instance on the web property. sp . site . openWebById ( \"111ca453-90f5-482e-a381-cee1ff383c9e\" ). then ( w => { //we got all the data from the web as well console . log ( w . data ); // we can chain w . web . select ( \"Title\" ). get (). then ( w2 => { // ... }); }); Update Web Properties \u00b6 You can update web properties using the update method. The properties available for update are listed in this table . Updating is a simple as passing a plain object with the properties you want to update. import { Web } from \"@pnp/sp\" ; let web = new Web ( \"https://my-tenant.sharepoint.com/sites/mysite\" ); web . update ({ Title : \"New Title\" , CustomMasterUrl : \"{path to masterpage}\" , Description : \"My new description\" , }). then ( w => { console . log ( w ); }); Delete a Web \u00b6 import { Web } from \"@pnp/sp\" ; let web = new Web ( \"https://my-tenant.sharepoint.com/sites/mysite\" ); web . delete (). then ( w => { console . log ( w ); });","title":"Webs"},{"location":"sp/docs/webs/#pnpspwebs","text":"Webs are one of the fundamental entry points when working with SharePoint. Webs serve as a container for lists, features, sub-webs, and all of the entity types.","title":"@pnp/sp/webs"},{"location":"sp/docs/webs/#add-a-web","text":"Using the library you can add a web to another web's collection of subwebs. The basic usage requires only a title and url. This will result in a team site with all of the default settings. import { sp , WebAddResult } from \"@pnp/sp\" ; sp . web . webs . add ( \"title\" , \"subweb1\" ). then (( w : WebAddResult ) => { // show the response from the server when adding the web console . log ( w . data ); w . web . select ( \"Title\" ). get (). then ( w => { // show our title console . log ( w . Title ); }); }); You can also provide other settings such as description, template, language, and inherit permissions. import { sp , WebAddResult } from \"@pnp/sp\" ; // create a German language wiki site with title, url, description, which inherits permissions sp . web . webs . add ( \"wiki\" , \"subweb2\" , \"a wiki web\" , \"WIKI#0\" , 1031 , true ). then (( w : WebAddResult ) => { // show the response from the server when adding the web console . log ( w . data ); w . web . select ( \"Title\" ). get (). then ( w => { // show our title console . log ( w . Title ); }); });","title":"Add a Web"},{"location":"sp/docs/webs/#create-default-associated-groups","text":"If you create a web that doesn't inherit permissions from the parent web, you can create its default associated groups (Members, Owners, Visitors) with the default role assigments (Contribute, Full Control, Read) import { sp , WebAddResult } from \"@pnp/sp\" ; sp . web . webs . add ( \"title\" , \"subweb1\" , \"a wiki web\" , \"WIKI#0\" , 1031 , false ). then (( w : WebAddResult ) => { w . web . createDefaultAssociatedGroups (). then (() => { // ... }); });","title":"Create Default Associated Groups"},{"location":"sp/docs/webs/#get-a-webs-properties","text":"import { sp } from \"@pnp/sp\" ; // basic get of the webs properties sp . web . get (). then ( w => { console . log ( w . Title ); }); // use odata operators to get specific fields sp . web . select ( \"Title\" ). get (). then ( w => { console . log ( w . Title ); }); // use with get to give the result a type sp . web . select ( \"Title\" ). get < { Title : string } > (). then ( w => { console . log ( w . Title ); });","title":"Get A Web's properties"},{"location":"sp/docs/webs/#get-complex-properties","text":"Some properties, such as AllProperties, are not returned by default. You can still access them using the expand operator. import { sp } from \"@pnp/sp\" ; sp . web . select ( \"AllProperties\" ). expand ( \"AllProperties\" ). get (). then ( w => { console . log ( w . AllProperties ); });","title":"Get Complex Properties"},{"location":"sp/docs/webs/#get-a-web-directly","text":"You can also use the Web object directly to get any web, though of course the current user must have the necessary permissions. This is done by importing the web object. import { Web } from \"@pnp/sp\" ; let web = new Web ( \"https://my-tenant.sharepoint.com/sites/mysite\" ); web . get (). then ( w => { console . log ( w ); });","title":"Get a Web Directly"},{"location":"sp/docs/webs/#open-web-by-id","text":"Because this method is a POST request you can chain off it directly. You will get back the full web properties in the data property of the return object. You can also chain directly off the returned Web instance on the web property. sp . site . openWebById ( \"111ca453-90f5-482e-a381-cee1ff383c9e\" ). then ( w => { //we got all the data from the web as well console . log ( w . data ); // we can chain w . web . select ( \"Title\" ). get (). then ( w2 => { // ... }); });","title":"Open Web By Id"},{"location":"sp/docs/webs/#update-web-properties","text":"You can update web properties using the update method. The properties available for update are listed in this table . Updating is a simple as passing a plain object with the properties you want to update. import { Web } from \"@pnp/sp\" ; let web = new Web ( \"https://my-tenant.sharepoint.com/sites/mysite\" ); web . update ({ Title : \"New Title\" , CustomMasterUrl : \"{path to masterpage}\" , Description : \"My new description\" , }). then ( w => { console . log ( w ); });","title":"Update Web Properties"},{"location":"sp/docs/webs/#delete-a-web","text":"import { Web } from \"@pnp/sp\" ; let web = new Web ( \"https://my-tenant.sharepoint.com/sites/mysite\" ); web . delete (). then ( w => { console . log ( w ); });","title":"Delete a Web"},{"location":"sp-addinhelpers/docs/","text":"@pnp/sp-addinhelpers \u00b6 This module contains classes to allow use of the libraries within a SharePoint add-in. Getting Started \u00b6 Install the library and all dependencies, npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/sp-addinhelpers --save Now you can make requests to the host web from your add-in using the crossDomainWeb method. // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp , SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\" ; // this only needs to be done once within your application sp . setup ({ sp : { fetchClientFactory : () => { return new SPRequestExecutorClient (); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\" ; const hostWebUrl = \"{The host web url, likely from the query string}\" ; // make requests into the host web via the SP.RequestExecutor sp . crossDomainWeb ( addInWenUrl , hostWebUrl ). get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); }); Libary Topics \u00b6 SPRequestExecutorClient SPRestAddIn UML \u00b6 Graphical UML diagram of @pnp/sp-addinhelpers. Right-click the diagram and open in new tab if it is too small.","title":"sp-addinhelpers"},{"location":"sp-addinhelpers/docs/#pnpsp-addinhelpers","text":"This module contains classes to allow use of the libraries within a SharePoint add-in.","title":"@pnp/sp-addinhelpers"},{"location":"sp-addinhelpers/docs/#getting-started","text":"Install the library and all dependencies, npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/sp-addinhelpers --save Now you can make requests to the host web from your add-in using the crossDomainWeb method. // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp , SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\" ; // this only needs to be done once within your application sp . setup ({ sp : { fetchClientFactory : () => { return new SPRequestExecutorClient (); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\" ; const hostWebUrl = \"{The host web url, likely from the query string}\" ; // make requests into the host web via the SP.RequestExecutor sp . crossDomainWeb ( addInWenUrl , hostWebUrl ). get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); });","title":"Getting Started"},{"location":"sp-addinhelpers/docs/#libary-topics","text":"SPRequestExecutorClient SPRestAddIn","title":"Libary Topics"},{"location":"sp-addinhelpers/docs/#uml","text":"Graphical UML diagram of @pnp/sp-addinhelpers. Right-click the diagram and open in new tab if it is too small.","title":"UML"},{"location":"sp-addinhelpers/docs/sp-request-executor-client/","text":"@pnp/sp-addinhelpers/sprequestexecutorclient \u00b6 The SPRequestExecutorClient is an implementation of the HttpClientImpl interface that facilitates requests to SharePoint from an add-in. It relies on the SharePoint SP product libraries being present to allow use of the SP.RequestExecutor to make the request. Setup \u00b6 To use the client you need to set it using the fetch client factory using the setup method as shown below. This is only required when working within a SharePoint add-in web. // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp , SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\" ; // this only needs to be done once within your application sp . setup ({ sp : { fetchClientFactory : () => { return new SPRequestExecutorClient (); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\" ; const hostWebUrl = \"{The host web url, likely from the query string}\" ; // make requests into the host web via the SP.RequestExecutor sp . crossDomainWeb ( addInWenUrl , hostWebUrl ). get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); });","title":"SPRequestExecutorClient"},{"location":"sp-addinhelpers/docs/sp-request-executor-client/#pnpsp-addinhelperssprequestexecutorclient","text":"The SPRequestExecutorClient is an implementation of the HttpClientImpl interface that facilitates requests to SharePoint from an add-in. It relies on the SharePoint SP product libraries being present to allow use of the SP.RequestExecutor to make the request.","title":"@pnp/sp-addinhelpers/sprequestexecutorclient"},{"location":"sp-addinhelpers/docs/sp-request-executor-client/#setup","text":"To use the client you need to set it using the fetch client factory using the setup method as shown below. This is only required when working within a SharePoint add-in web. // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp , SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\" ; // this only needs to be done once within your application sp . setup ({ sp : { fetchClientFactory : () => { return new SPRequestExecutorClient (); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\" ; const hostWebUrl = \"{The host web url, likely from the query string}\" ; // make requests into the host web via the SP.RequestExecutor sp . crossDomainWeb ( addInWenUrl , hostWebUrl ). get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); });","title":"Setup"},{"location":"sp-addinhelpers/docs/sp-rest-addin/","text":"@pnp/sp-addinhelpers/sprestaddin \u00b6 This class extends the sp export from @pnp/sp and adds in the methods required to make cross domain calls // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp , SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\" ; // this only needs to be done once within your application sp . setup ({ sp : { fetchClientFactory : () => { return new SPRequestExecutorClient (); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\" ; const hostWebUrl = \"{The host web url, likely from the query string}\" ; // make requests into the host web via the SP.RequestExecutor sp . crossDomainWeb ( addInWenUrl , hostWebUrl ). get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); });","title":"SPRestAddIn"},{"location":"sp-addinhelpers/docs/sp-rest-addin/#pnpsp-addinhelperssprestaddin","text":"This class extends the sp export from @pnp/sp and adds in the methods required to make cross domain calls // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp , SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\" ; // this only needs to be done once within your application sp . setup ({ sp : { fetchClientFactory : () => { return new SPRequestExecutorClient (); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\" ; const hostWebUrl = \"{The host web url, likely from the query string}\" ; // make requests into the host web via the SP.RequestExecutor sp . crossDomainWeb ( addInWenUrl , hostWebUrl ). get (). then ( w => { console . log ( JSON . stringify ( w , null , 4 )); });","title":"@pnp/sp-addinhelpers/sprestaddin"},{"location":"sp-addinhelpers/node_modules/@types/microsoft-ajax/","text":"Installation \u00b6 npm install --save @types/microsoft-ajax Summary \u00b6 This package contains type definitions for Microsoft ASP.NET Ajax client side library (http://msdn.microsoft.com/en-us/library/ee341002(v=vs.100).aspx). Details \u00b6 Files were exported from https://www.github.com/DefinitelyTyped/DefinitelyTyped/tree/main/types/microsoft-ajax Additional Details * Last updated: Mon, 21 Aug 2017 21:55:03 GMT * Dependencies: none * Global values: $addHandler, $addHandlers, $clearHandlers, $create, $find, $get, $removeHandler, Sys, Type Credits \u00b6 These definitions were written by Patrick Magee https://github.com/pjmagee .","title":"Installation"},{"location":"sp-addinhelpers/node_modules/@types/microsoft-ajax/#installation","text":"npm install --save @types/microsoft-ajax","title":"Installation"},{"location":"sp-addinhelpers/node_modules/@types/microsoft-ajax/#summary","text":"This package contains type definitions for Microsoft ASP.NET Ajax client side library (http://msdn.microsoft.com/en-us/library/ee341002(v=vs.100).aspx).","title":"Summary"},{"location":"sp-addinhelpers/node_modules/@types/microsoft-ajax/#details","text":"Files were exported from https://www.github.com/DefinitelyTyped/DefinitelyTyped/tree/main/types/microsoft-ajax Additional Details * Last updated: Mon, 21 Aug 2017 21:55:03 GMT * Dependencies: none * Global values: $addHandler, $addHandlers, $clearHandlers, $create, $find, $get, $removeHandler, Sys, Type","title":"Details"},{"location":"sp-addinhelpers/node_modules/@types/microsoft-ajax/#credits","text":"These definitions were written by Patrick Magee https://github.com/pjmagee .","title":"Credits"},{"location":"sp-addinhelpers/node_modules/@types/sharepoint/","text":"Installation \u00b6 npm install --save @types/sharepoint Summary \u00b6 This package contains type definitions for Microsoft SharePoint: (https://msdn.microsoft.com/en-us/library/office/jj193034.aspx). Details \u00b6 Files were exported from https://www.github.com/DefinitelyTyped/DefinitelyTyped/tree/main/types/sharepoint Additional Details * Last updated: Thu, 15 Jun 2017 22:02:56 GMT * Dependencies: microsoft-ajax * Global values: $addRenderContextCallback, $contentLineText, $findResultObjectFromDOM, $getCachedItemValue, $getClientControl, $getItemValue, $getResultItem, $getResultObject, $htmlEncode, $imgSrcUrl, $includeCSS, $includeLanguageScript, $includeScript, $isEmptyArray, $isEmptyString, $isInArray, $isNull, $registerResourceDictionary, $resource, $scriptEncode, $setItemWrapperCallback, $setResultItem, $setResultObject, $urlHtmlEncode, $urlKeyValueEncode, $urlPathEncode, AddEvtHandler, AjaxNavigate, BrowserDetection, BrowserStorage, Browseris, CSSUtil, Callout, CalloutAction, CalloutActionMenu, CalloutActionMenuEntry, CalloutActionOptions, CalloutManager, CalloutOpenOptions, CalloutOptions, CoreRender, DOM, Define, Encoding, ExecuteOrDelayUntilBodyLoaded, ExecuteOrDelayUntilEventNotified, ExecuteOrDelayUntilScriptLoaded, GenerateIID, GenerateIIDForListItem, GetCurrentCtx, GetUrlKeyValue, IE8Support, JSRequest, ListModule, Microsoft, Nav, RefreshCommandUI, RegisterModuleInit, SP, SPAnimation, SPAnimationUtility, SPClientAutoFill, SPClientForms, SPClientPeoplePicker, SPClientPeoplePickerCSRTemplate, SPClientPeoplePickerMRU, SPClientPeoplePickerProcessedUser, SPClientTemplates, SPFieldAttachments_Default, SPFieldBoolean_Edit, SPFieldChoice_Dropdown_Edit, SPFieldChoice_Edit, SPFieldChoice_Radio_Edit, SPFieldDateTime_Display, SPFieldDateTime_Edit, SPFieldFile_Display, SPFieldFile_Edit, SPFieldLookupMulti_Edit, SPFieldLookup_Display, SPFieldLookup_Edit, SPFieldMultiChoice_Edit, SPFieldNote_Display, SPFieldNote_Edit, SPFieldNumber_Edit, SPFieldText_Edit, SPFieldUrl_Display, SPFieldUrl_Edit, SPFieldUserMulti_Display, SPFieldUser_Display, SPField_FormDisplay_Default, SPField_FormDisplay_DefaultNoEncode, SPField_FormDisplay_Empty, SPFormControl_AppendValidationErrorMessage, SPMgr, SPNotifications, SPStatusNotificationData, SPThemeUtils, STSHtmlDecode, STSHtmlEncode, SetFullScreenMode, Srch, StringUtil, Strings, TypeUtil, URI_Encoding, Verify, _spBodyOnLoadCalled, _spBodyOnLoadFunctionNames, _spBodyOnLoadFunctions, _spFriendlyUrlPageContextInfo, _spPageContextInfo, ajaxNavigate, browseris, m$, spMgr Credits \u00b6 These definitions were written by Stanislav Vyshchepan http:// blog.gandjustas.ru , Andrey Markeev http:// markeev.com , Vincent Biret https://github.com/baywet .","title":"Installation"},{"location":"sp-addinhelpers/node_modules/@types/sharepoint/#installation","text":"npm install --save @types/sharepoint","title":"Installation"},{"location":"sp-addinhelpers/node_modules/@types/sharepoint/#summary","text":"This package contains type definitions for Microsoft SharePoint: (https://msdn.microsoft.com/en-us/library/office/jj193034.aspx).","title":"Summary"},{"location":"sp-addinhelpers/node_modules/@types/sharepoint/#details","text":"Files were exported from https://www.github.com/DefinitelyTyped/DefinitelyTyped/tree/main/types/sharepoint Additional Details * Last updated: Thu, 15 Jun 2017 22:02:56 GMT * Dependencies: microsoft-ajax * Global values: $addRenderContextCallback, $contentLineText, $findResultObjectFromDOM, $getCachedItemValue, $getClientControl, $getItemValue, $getResultItem, $getResultObject, $htmlEncode, $imgSrcUrl, $includeCSS, $includeLanguageScript, $includeScript, $isEmptyArray, $isEmptyString, $isInArray, $isNull, $registerResourceDictionary, $resource, $scriptEncode, $setItemWrapperCallback, $setResultItem, $setResultObject, $urlHtmlEncode, $urlKeyValueEncode, $urlPathEncode, AddEvtHandler, AjaxNavigate, BrowserDetection, BrowserStorage, Browseris, CSSUtil, Callout, CalloutAction, CalloutActionMenu, CalloutActionMenuEntry, CalloutActionOptions, CalloutManager, CalloutOpenOptions, CalloutOptions, CoreRender, DOM, Define, Encoding, ExecuteOrDelayUntilBodyLoaded, ExecuteOrDelayUntilEventNotified, ExecuteOrDelayUntilScriptLoaded, GenerateIID, GenerateIIDForListItem, GetCurrentCtx, GetUrlKeyValue, IE8Support, JSRequest, ListModule, Microsoft, Nav, RefreshCommandUI, RegisterModuleInit, SP, SPAnimation, SPAnimationUtility, SPClientAutoFill, SPClientForms, SPClientPeoplePicker, SPClientPeoplePickerCSRTemplate, SPClientPeoplePickerMRU, SPClientPeoplePickerProcessedUser, SPClientTemplates, SPFieldAttachments_Default, SPFieldBoolean_Edit, SPFieldChoice_Dropdown_Edit, SPFieldChoice_Edit, SPFieldChoice_Radio_Edit, SPFieldDateTime_Display, SPFieldDateTime_Edit, SPFieldFile_Display, SPFieldFile_Edit, SPFieldLookupMulti_Edit, SPFieldLookup_Display, SPFieldLookup_Edit, SPFieldMultiChoice_Edit, SPFieldNote_Display, SPFieldNote_Edit, SPFieldNumber_Edit, SPFieldText_Edit, SPFieldUrl_Display, SPFieldUrl_Edit, SPFieldUserMulti_Display, SPFieldUser_Display, SPField_FormDisplay_Default, SPField_FormDisplay_DefaultNoEncode, SPField_FormDisplay_Empty, SPFormControl_AppendValidationErrorMessage, SPMgr, SPNotifications, SPStatusNotificationData, SPThemeUtils, STSHtmlDecode, STSHtmlEncode, SetFullScreenMode, Srch, StringUtil, Strings, TypeUtil, URI_Encoding, Verify, _spBodyOnLoadCalled, _spBodyOnLoadFunctionNames, _spBodyOnLoadFunctions, _spFriendlyUrlPageContextInfo, _spPageContextInfo, ajaxNavigate, browseris, m$, spMgr","title":"Details"},{"location":"sp-addinhelpers/node_modules/@types/sharepoint/#credits","text":"These definitions were written by Stanislav Vyshchepan http:// blog.gandjustas.ru , Andrey Markeev http:// markeev.com , Vincent Biret https://github.com/baywet .","title":"Credits"},{"location":"sp-addinhelpers/node_modules/tslib/","text":"tslib \u00b6 This is a runtime library for TypeScript that contains all of the TypeScript helper functions. This library is primarily used by the --importHelpers flag in TypeScript. When using --importHelpers , a module that uses helper functions like __extends and __assign in the following emitted file: var __assign = ( this && this . __assign ) || Object . assign || function ( t ) { for ( var s , i = 1 , n = arguments . length ; i < n ; i ++ ) { s = arguments [ i ]; for ( var p in s ) if ( Object . prototype . hasOwnProperty . call ( s , p )) t [ p ] = s [ p ]; } return t ; }; exports . x = {}; exports . y = __assign ({}, exports . x ); will instead be emitted as something like the following: var tslib_1 = require ( \"tslib\" ); exports . x = {}; exports . y = tslib_1 . __assign ({}, exports . x ); Because this can avoid duplicate declarations of things like __extends , __assign , etc., this means delivering users smaller files on average, as well as less runtime overhead. For optimized bundles with TypeScript, you should absolutely consider using tslib and --importHelpers . Installing \u00b6 For the latest stable version, run: npm \u00b6 # TypeScript 2.3.3 or later npm install --save tslib # TypeScript 2.3.2 or earlier npm install --save tslib@1.6.1 bower \u00b6 # TypeScript 2.3.3 or later bower install tslib # TypeScript 2.3.2 or earlier bower install tslib@1.6.1 JSPM \u00b6 # TypeScript 2.3.3 or later jspm install tslib # TypeScript 2.3.2 or earlier jspm install tslib@1.6.1 Usage \u00b6 Set the importHelpers compiler option on the command line: tsc --importHelpers file.ts or in your tsconfig.json: { \"compilerOptions\" : { \"importHelpers\" : true } } For bower and JSPM users \u00b6 You will need to add a paths mapping for tslib , e.g. For Bower users: { \"compilerOptions\" : { \"module\" : \"amd\" , \"importHelpers\" : true , \"baseUrl\" : \"./\" , \"paths\" : { \"tslib\" : [ \"bower_components/tslib/tslib.d.ts\" ] } } } For JSPM users: { \"compilerOptions\" : { \"module\" : \"system\" , \"importHelpers\" : true , \"baseUrl\" : \"./\" , \"paths\" : { \"tslib\" : [ \"jspm_packages/npm/tslib@1.9.3/tslib.d.ts\" ] } } } Contribute \u00b6 There are many ways to contribute to TypeScript. Submit bugs and help us verify fixes as they are checked in. Review the source code changes . Engage with other TypeScript users and developers on StackOverflow . Join the #typescript discussion on Twitter. Contribute bug fixes . Read the language specification ( docx , pdf ). Documentation \u00b6 Quick tutorial Programming handbook Language specification Homepage","title":"tslib"},{"location":"sp-addinhelpers/node_modules/tslib/#tslib","text":"This is a runtime library for TypeScript that contains all of the TypeScript helper functions. This library is primarily used by the --importHelpers flag in TypeScript. When using --importHelpers , a module that uses helper functions like __extends and __assign in the following emitted file: var __assign = ( this && this . __assign ) || Object . assign || function ( t ) { for ( var s , i = 1 , n = arguments . length ; i < n ; i ++ ) { s = arguments [ i ]; for ( var p in s ) if ( Object . prototype . hasOwnProperty . call ( s , p )) t [ p ] = s [ p ]; } return t ; }; exports . x = {}; exports . y = __assign ({}, exports . x ); will instead be emitted as something like the following: var tslib_1 = require ( \"tslib\" ); exports . x = {}; exports . y = tslib_1 . __assign ({}, exports . x ); Because this can avoid duplicate declarations of things like __extends , __assign , etc., this means delivering users smaller files on average, as well as less runtime overhead. For optimized bundles with TypeScript, you should absolutely consider using tslib and --importHelpers .","title":"tslib"},{"location":"sp-addinhelpers/node_modules/tslib/#installing","text":"For the latest stable version, run:","title":"Installing"},{"location":"sp-addinhelpers/node_modules/tslib/#npm","text":"# TypeScript 2.3.3 or later npm install --save tslib # TypeScript 2.3.2 or earlier npm install --save tslib@1.6.1","title":"npm"},{"location":"sp-addinhelpers/node_modules/tslib/#bower","text":"# TypeScript 2.3.3 or later bower install tslib # TypeScript 2.3.2 or earlier bower install tslib@1.6.1","title":"bower"},{"location":"sp-addinhelpers/node_modules/tslib/#jspm","text":"# TypeScript 2.3.3 or later jspm install tslib # TypeScript 2.3.2 or earlier jspm install tslib@1.6.1","title":"JSPM"},{"location":"sp-addinhelpers/node_modules/tslib/#usage","text":"Set the importHelpers compiler option on the command line: tsc --importHelpers file.ts or in your tsconfig.json: { \"compilerOptions\" : { \"importHelpers\" : true } }","title":"Usage"},{"location":"sp-addinhelpers/node_modules/tslib/#for-bower-and-jspm-users","text":"You will need to add a paths mapping for tslib , e.g. For Bower users: { \"compilerOptions\" : { \"module\" : \"amd\" , \"importHelpers\" : true , \"baseUrl\" : \"./\" , \"paths\" : { \"tslib\" : [ \"bower_components/tslib/tslib.d.ts\" ] } } } For JSPM users: { \"compilerOptions\" : { \"module\" : \"system\" , \"importHelpers\" : true , \"baseUrl\" : \"./\" , \"paths\" : { \"tslib\" : [ \"jspm_packages/npm/tslib@1.9.3/tslib.d.ts\" ] } } }","title":"For bower and JSPM users"},{"location":"sp-addinhelpers/node_modules/tslib/#contribute","text":"There are many ways to contribute to TypeScript. Submit bugs and help us verify fixes as they are checked in. Review the source code changes . Engage with other TypeScript users and developers on StackOverflow . Join the #typescript discussion on Twitter. Contribute bug fixes . Read the language specification ( docx , pdf ).","title":"Contribute"},{"location":"sp-addinhelpers/node_modules/tslib/#documentation","text":"Quick tutorial Programming handbook Language specification Homepage","title":"Documentation"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/","text":"The __generator helper \u00b6 The __generator helper is a function designed to support TypeScript's down-level emit for async functions when targeting ES5 and earlier. But how, exactly, does it work? Here's the body of the __generator helper: __generator = function ( thisArg , body ) { var _ = { label : 0 , sent : function () { if ( t [ 0 ] & 1 ) throw t [ 1 ]; return t [ 1 ]; }, trys : [], ops : [] }, f , y , t ; return { next : verb ( 0 ), \"throw\" : verb ( 1 ), \"return\" : verb ( 2 ) }; function verb ( n ) { return function ( v ) { return step ([ n , v ]); }; } function step ( op ) { if ( f ) throw new TypeError ( \"Generator is already executing.\" ); while ( _ ) try { if ( f = 1 , y && ( t = y [ op [ 0 ] & 2 ? \"return\" : op [ 0 ] ? \"throw\" : \"next\" ]) && ! ( t = t . call ( y , op [ 1 ])). done ) return t ; if ( y = 0 , t ) op = [ 0 , t . value ]; switch ( op [ 0 ]) { case 0 : case 1 : t = op ; break ; case 4 : _ . label ++ ; return { value : op [ 1 ], done : false }; case 5 : _ . label ++ ; y = op [ 1 ]; op = [ 0 ]; continue ; case 7 : op = _ . ops . pop (); _ . trys . pop (); continue ; default : if ( ! ( t = _ . trys , t = t . length > 0 && t [ t . length - 1 ]) && ( op [ 0 ] === 6 || op [ 0 ] === 2 )) { _ = 0 ; continue ; } if ( op [ 0 ] === 3 && ( ! t || ( op [ 1 ] > t [ 0 ] && op [ 1 ] < t [ 3 ]))) { _ . label = op [ 1 ]; break ; } if ( op [ 0 ] === 6 && _ . label < t [ 1 ]) { _ . label = t [ 1 ]; t = op ; break ; } if ( t && _ . label < t [ 2 ]) { _ . label = t [ 2 ]; _ . ops . push ( op ); break ; } if ( t [ 2 ]) _ . ops . pop (); _ . trys . pop (); continue ; } op = body . call ( thisArg , _ ); } catch ( e ) { op = [ 6 , e ]; y = 0 ; } finally { f = t = 0 ; } if ( op [ 0 ] & 5 ) throw op [ 1 ]; return { value : op [ 0 ] ? op [ 1 ] : void 0 , done : true }; } }; And here's an example of it in use: // source async function func ( x ) { try { await x ; } catch ( e ) { console . error ( e ); } finally { console . log ( \"finally\" ); } } // generated function func ( x ) { return __awaiter ( this , void 0 , void 0 , function () { var e_1 ; return __generator ( this , function ( _a ) { switch ( _a . label ) { case 0 : _a.trys.push ([ 0 , 1 , 3 , 4 ]); return [ 4 /*yield*/ , x ]; case 1 : _a.sent (); return [ 3 /*break*/ , 4 ]; case 2 : e_1 = _a . sent (); console . error ( e_1 ); return [ 3 /*break*/ , 4 ]; case 3 : console.log ( \"finally\" ); return [ 7 /*endfinally*/ ]; case 4 : return [ 2 /*return*/ ]; } }); }); } There is a lot going on in this function, so the following will break down what each part of the __generator helper does and how it works. Opcodes \u00b6 The __generator helper uses opcodes which represent various operations that are interpreted by the helper to affect its internal state. The following table lists the various opcodes, their arguments, and their purpose: Opcode Arguments Purpose 0 (next) value Starts the generator, or resumes the generator with value as the result of the AwaitExpression where execution was paused. 1 (throw) value Resumes the generator, throwing value at AwaitExpression where execution was paused. 2 (return) value Exits the generator, executing any finally blocks starting at the AwaitExpression where execution was paused. 3 (break) label Performs an unconditional jump to the specified label, executing any finally between the current instruction and the label. 4 (yield) value Suspends the generator, setting the resume point at the next label and yielding the value. 5 (yieldstar) value Suspends the generator, setting the resume point at the next label and delegating operations to the supplied value. 6 (catch) error An internal instruction used to indicate an exception that was thrown from the body of the generator. 7 (endfinally) Exits a finally block, resuming any previous operation (such as a break, return, throw, etc.) State \u00b6 The _ , f , y , and t variables make up the persistent state of the __generator function. Each variable has a specific purpose, as described in the following sections: The _ variable \u00b6 The __generator helper must share state between its internal step orchestration function and the body function passed to the helper. var _ = { label : 0 , sent : function () { if ( t [ 0 ] & 1 ) // NOTE: true for `throw`, but not `next` or `catch` throw t [ 1 ]; return sent [ 1 ]; }, trys : [], ops : [] }; The following table describes the members of the _ state object and their purpose: Name Description label Specifies the next switch case to execute in the body function. sent Handles the completion result passed to the generator. trys A stack of Protected Regions , which are 4-tuples that describe the labels that make up a try..catch..finally block. ops A stack of pending operations used for try..finally blocks. The __generator helper passes this state object to the body function for use with switching between switch cases in the body, handling completions from AwaitExpression , etc. The f variable \u00b6 The f variable indicates whether the generator is currently executing, to prevent re-entry of the same generator during its execution. The y variable \u00b6 The y variable stores the iterator passed to a yieldstar instruction to which operations should be delegated. The t variable \u00b6 The t variable is a temporary variable that stores one of the following values: The completion value when resuming from a yield or yield* . The error value for a catch block. The current Protected Region . The verb ( next , throw , or return method) to delegate to the expression of a yield* . The result of evaluating the verb delegated to the expression of a yield* . NOTE: None of the above cases overlap. Protected Regions \u00b6 A Protected Region is a region within the body function that indicates a try..catch..finally statement. It consists of a 4-tuple that contains 4 labels: Offset Description 0 Required The label that indicates the beginning of a try..catch..finally statement. 1 Optional The label that indicates the beginning of a catch clause. 2 Optional The label that indicates the beginning of a finally clause. 3 Required The label that indicates the end of the try..catch..finally statement. The generator object \u00b6 The final step of the __generator helper is the allocation of an object that implements the Generator protocol, to be used by the __awaiter helper: return { next : verb ( 0 ), \"throw\" : verb ( 1 ), \"return\" : verb ( 2 ) }; function verb ( n ) { return function ( v ) { return step ([ n , v ]); }; } This object translates calls to next , throw , and return to the appropriate Opcodes and invokes the step orchestration function to continue execution. The throw and return method names are quoted to better support ES3. Orchestration \u00b6 The step function is the main orechestration mechanism for the __generator helper. It interprets opcodes, handles protected regions , and communicates results back to the caller. Here's a closer look at the step function: function step ( op ) { if ( f ) throw new TypeError ( \"Generator is already executing.\" ); while ( _ ) try { if ( f = 1 , y && ( t = y [ op [ 0 ] & 2 ? \"return\" : op [ 0 ] ? \"throw\" : \"next\" ]) && ! ( t = t . call ( y , op [ 1 ])). done ) return t ; if ( y = 0 , t ) op = [ 0 , t . value ]; switch ( op [ 0 ]) { case 0 : case 1 : t = op ; break ; case 4 : _.label ++ ; return { value : op [ 1 ], done : false }; case 5 : _.label ++ ; y = op [ 1 ]; op = [ 0 ]; continue ; case 7 : op = _ . ops . pop (); _ . trys . pop (); continue ; default : if ( ! ( t = _ . trys , t = t . length > 0 && t [ t . length - 1 ]) && ( op [ 0 ] === 6 || op [ 0 ] === 2 )) { _ = 0 ; continue ; } if ( op [ 0 ] === 3 && ( ! t || ( op [ 1 ] > t [ 0 ] && op [ 1 ] < t [ 3 ]))) { _ . label = op [ 1 ]; break ; } if ( op [ 0 ] === 6 && _ . label < t [ 1 ]) { _ . label = t [ 1 ]; t = op ; break ; } if ( t && _ . label < t [ 2 ]) { _ . label = t [ 2 ]; _ . ops . push ( op ); break ; } if ( t [ 2 ]) _ . ops . pop (); _ . trys . pop (); continue ; } op = body . call ( thisArg , _ ); } catch ( e ) { op = [ 6 , e ]; y = 0 ; } finally { f = t = 0 ; } if ( op [ 0 ] & 5 ) throw op [ 1 ]; return { value : op [ 0 ] ? op [ 1 ] : void 0 , done : true }; } The main body of step exists in a while loop. This allows us to continually interpret operations until we have reached some completion value, be it a return , await , or throw . Preventing re-entry \u00b6 The first part of the step function is used as a check to prevent re-entry into a currently executing generator: if ( f ) throw new TypeError ( \"Generator is already executing.\" ); Running the generator \u00b6 The main body of the step function consists of a while loop which continues to evaluate instructions until the generator exits or is suspended: while ( _ ) try ... When the generator has run to completion, the _ state variable will be cleared, forcing the loop to exit. Evaluating the generator body. \u00b6 try { ... op = body . call ( thisArg , _ ); } catch ( e ) { op = [ 6 , e ]; y = 0 ; } finally { f = t = 0 ; } Depending on the current operation, we re-enter the generator body to start or continue execution. Here we invoke body with thisArg as the this binding and the _ state object as the only argument. The result is a tuple that contains the next Opcode and argument. If evaluation of the body resulted in an exception, we convert this into an Opcode 6 (\"catch\") operation to be handled in the next spin of the while loop. We also clear the y variable in case it is set to ensure we are no longer delegating operations as the exception occurred in user code outside of, or at the function boundary of, the delegated iterator (otherwise the iterator would have handled the exception itself). After executing user code, we clear the f flag that indicates we are executing the generator, as well as the t temporary value so that we don't hold onto values sent to the generator for longer than necessary. Inside of the try..finally statement are a series of statements that are used to evaluate the operations of the transformed generator body. The first thing we do is mark the generator as executing: if ( f = 1 , ...) Despite the fact this expression is part of the head of an if statement, the comma operator causes it to be evaluated and the result thrown out. This is a minification added purely to reduce the overall footprint of the helper. Delegating yield* \u00b6 The first two statements of the try..finally statement handle delegation for yield* : if ( f = 1 , y && ( t = y [ op [ 0 ] & 2 ? \"return\" : op [ 0 ] ? \"throw\" : \"next\" ]) && ! ( t = t . call ( y , op [ 1 ])). done ) return t ; if ( y = 0 , t ) op = [ 0 , t . value ]; If the y variable is set, and y has a next , throw , or return method (depending on the current operation), we invoke this method and store the return value (an IteratorResult) in t . If t indicates it is a yielded value (e.g. t.done === false ), we return t to the caller. If t indicates it is a returned value (e.g. t.done === true ), we mark the operation with the next Opcode, and the returned value. If y did not have the appropriate method, or t was a returned value, we reset y to a falsey value and continue processing the operation. Handling operations \u00b6 The various Opcodes are handled in the following switch statement: switch ( op [ 0 ]) { case 0 : case 1 : t = op ; break ; case 4 : _.label ++ ; return { value : op [ 1 ], done : false }; case 5 : _.label ++ ; y = op [ 1 ]; op = [ 0 ]; continue ; case 7 : op = _ . ops . pop (); _ . trys . pop (); continue ; default : if ( ! ( t = _ . trys , t = t . length > 0 && t [ t . length - 1 ]) && ( op [ 0 ] === 6 || op [ 0 ] === 2 )) { _ = 0 ; continue ; } if ( op [ 0 ] === 3 && ( ! t || ( op [ 1 ] > t [ 0 ] && op [ 1 ] < t [ 3 ]))) { _ . label = op [ 1 ]; break ; } if ( op [ 0 ] === 6 && _ . label < t [ 1 ]) { _ . label = t [ 1 ]; t = op ; break ; } if ( t && _ . label < t [ 2 ]) { _ . label = t [ 2 ]; _ . ops . push ( op ); break ; } if ( t [ 2 ]) _ . ops . pop (); _ . trys . pop (); continue ; } The following sections describe the various Opcodes: Opcode 0 (\"next\") and Opcode 1 (\"throw\") \u00b6 case 0 : // next case 1 : // throw t = op ; break ; Both Opcode 0 (\"next\") and Opcode 1 (\"throw\") have the same behavior. The current operation is stored in the t variable and the body function is invoked. The body function should call _.sent() which will evaluate the appropriate completion result. Opcode 4 (\"yield\") \u00b6 case 4 : // yield _ . label ++ ; return { value : op [ 1 ], done : false }; When we encounter Opcode 4 (\"yield\"), we increment the label by one to indicate the point at which the generator will resume execution. We then return an IteratorResult whose value is the yielded value, and done is false . Opcode 5 (\"yieldstar\") \u00b6 case 5 : // yieldstar _ . label ++ ; y = op [ 1 ]; op = [ 0 ]; continue ; When we receive Opcode 5 (\"yieldstar\"), we increment the label by one to indicate the point at which the generator will resume execution. We then store the iterator in op[1] in the y variable, and set the operation to delegate to Opcode 0 (\"next\") with no value. Finally, we continue execution at the top of the loop to start delegation. Opcode 7 (\"endfinally\") \u00b6 case 7 : op = _ . ops . pop (); _ . trys . pop (); continue ; Opcode 7 (\"endfinally\") indicates that we have hit the end of a finally clause, and that the last operation recorded before entering the finally block should be evaluated. Opcode 2 (\"return\"), Opcode 3 (\"break\"), and Opcode 6 (\"catch\") \u00b6 default : if ( ! ( t = _ . trys , t = t . length > 0 && t [ t . length - 1 ]) && ( op [ 0 ] === 6 || op [ 0 ] === 2 )) { _ = 0 ; continue ; } if ( op [ 0 ] === 3 && ( ! t || ( op [ 1 ] > t [ 0 ] && op [ 1 ] < t [ 3 ]))) { _ . label = op [ 1 ]; break ; } if ( op [ 0 ] === 6 && _ . label < t [ 1 ]) { _ . label = t [ 1 ]; t = op ; break ; } if ( t && _ . label < t [ 2 ]) { _ . label = t [ 2 ]; _ . ops . push ( op ); break ; } if ( t [ 2 ]) _ . ops . pop (); _ . trys . pop (); continue ; } The handling for Opcode 2 (\"return\"), Opcode 3 (\"break\") and Opcode 6 (\"catch\") is more complicated, as we must obey the specified runtime semantics of generators. The first line in this clause gets the current Protected Region if found and stores it in the t temp variable: if ( ! ( t = _ . trys , t = t . length > 0 && t [ t . length - 1 ]) && ...) ... The remainder of this statement, as well as the following by several if statements test for more complex conditions. The first of these is the following: if ( ! ( t = ...) && ( op [ 0 ] === 6 || op [ 0 ] === 2 )) { _ = 0 ; continue ; } If we encounter an Opcode 6 (\"catch\") or Opcode 2 (\"return\"), and we are not in a protected region, then this operation completes the generator by setting the _ variable to a falsey value. The continue statement resumes execution at the top of the while statement, which will exit the loop so that we continue execution at the statement following the loop. if ( op [ 0 ] === 3 && ( ! t || ( op [ 1 ] > t [ 0 ] && op [ 1 ] < t [ 3 ]))) { _ . label = op [ 1 ]; break ; } The if statement above handles Opcode 3 (\"break\") when we are either not in a protected region , or are performing an unconditional jump to a label inside of the current protected region . In this case we can unconditionally jump to the specified label. if ( op [ 0 ] === 6 && _ . label < t [ 1 ]) { _ . label = t [ 1 ]; t = op ; break ; } The if statement above handles Opcode 6 (\"catch\") when inside the try block of a protected region . In this case we jump to the catch block, if present. We replace the value of t with the operation so that the exception can be read as the first statement of the transformed catch clause of the transformed generator body. if ( t && _ . label < t [ 2 ]) { _ . label = t [ 2 ]; _ . ops . push ( op ); break ; } This if statement handles all Opcodes when in a protected region with a finally clause. As long as we are not already inside the finally clause, we jump to the finally clause and push the pending operation onto the _.ops stack. This allows us to resume execution of the pending operation once we have completed execution of the finally clause, as long as it does not supersede this operation with its own completion value. if ( t [ 2 ]) _ . ops . pop (); Any other completion value inside of a finally clause will supersede the pending completion value from the try or catch clauses. The above if statement pops the pending completion from the stack. _ . trys . pop (); continue ; The remaining statements handle the point at which we exit a protected region . Here we pop the current protected region from the stack and spin the while statement to evaluate the current operation again in the next protected region or at the function boundary. Handling a completed generator \u00b6 Once the generator has completed, the _ state variable will be falsey. As a result, the while loop will terminate and hand control off to the final statement of the orchestration function, which deals with how a completed generator is evaluated: if ( op [ 0 ] & 5 ) throw op [ 1 ]; return { value : op [ 0 ] ? op [ 1 ] : void 0 , done : true }; If the caller calls throw on the generator it will send Opcode 1 (\"throw\"). If an exception is uncaught within the body of the generator, it will send Opcode 6 (\"catch\"). As the generator has completed, it throws the exception. Both of these cases are caught by the bitmask 5 , which does not collide with the only two other valid completion Opcodes. If the caller calls next on the generator, it will send Opcode 0 (\"next\"). As the generator has completed, it returns an IteratorResult where value is undefined and done is true. If the caller calls return on the generator, it will send Opcode 2 (\"return\"). As the generator has completed, it returns an IteratorResult where value is the value provided to return , and done is true.","title":"The `__generator` helper"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#the-__generator-helper","text":"The __generator helper is a function designed to support TypeScript's down-level emit for async functions when targeting ES5 and earlier. But how, exactly, does it work? Here's the body of the __generator helper: __generator = function ( thisArg , body ) { var _ = { label : 0 , sent : function () { if ( t [ 0 ] & 1 ) throw t [ 1 ]; return t [ 1 ]; }, trys : [], ops : [] }, f , y , t ; return { next : verb ( 0 ), \"throw\" : verb ( 1 ), \"return\" : verb ( 2 ) }; function verb ( n ) { return function ( v ) { return step ([ n , v ]); }; } function step ( op ) { if ( f ) throw new TypeError ( \"Generator is already executing.\" ); while ( _ ) try { if ( f = 1 , y && ( t = y [ op [ 0 ] & 2 ? \"return\" : op [ 0 ] ? \"throw\" : \"next\" ]) && ! ( t = t . call ( y , op [ 1 ])). done ) return t ; if ( y = 0 , t ) op = [ 0 , t . value ]; switch ( op [ 0 ]) { case 0 : case 1 : t = op ; break ; case 4 : _ . label ++ ; return { value : op [ 1 ], done : false }; case 5 : _ . label ++ ; y = op [ 1 ]; op = [ 0 ]; continue ; case 7 : op = _ . ops . pop (); _ . trys . pop (); continue ; default : if ( ! ( t = _ . trys , t = t . length > 0 && t [ t . length - 1 ]) && ( op [ 0 ] === 6 || op [ 0 ] === 2 )) { _ = 0 ; continue ; } if ( op [ 0 ] === 3 && ( ! t || ( op [ 1 ] > t [ 0 ] && op [ 1 ] < t [ 3 ]))) { _ . label = op [ 1 ]; break ; } if ( op [ 0 ] === 6 && _ . label < t [ 1 ]) { _ . label = t [ 1 ]; t = op ; break ; } if ( t && _ . label < t [ 2 ]) { _ . label = t [ 2 ]; _ . ops . push ( op ); break ; } if ( t [ 2 ]) _ . ops . pop (); _ . trys . pop (); continue ; } op = body . call ( thisArg , _ ); } catch ( e ) { op = [ 6 , e ]; y = 0 ; } finally { f = t = 0 ; } if ( op [ 0 ] & 5 ) throw op [ 1 ]; return { value : op [ 0 ] ? op [ 1 ] : void 0 , done : true }; } }; And here's an example of it in use: // source async function func ( x ) { try { await x ; } catch ( e ) { console . error ( e ); } finally { console . log ( \"finally\" ); } } // generated function func ( x ) { return __awaiter ( this , void 0 , void 0 , function () { var e_1 ; return __generator ( this , function ( _a ) { switch ( _a . label ) { case 0 : _a.trys.push ([ 0 , 1 , 3 , 4 ]); return [ 4 /*yield*/ , x ]; case 1 : _a.sent (); return [ 3 /*break*/ , 4 ]; case 2 : e_1 = _a . sent (); console . error ( e_1 ); return [ 3 /*break*/ , 4 ]; case 3 : console.log ( \"finally\" ); return [ 7 /*endfinally*/ ]; case 4 : return [ 2 /*return*/ ]; } }); }); } There is a lot going on in this function, so the following will break down what each part of the __generator helper does and how it works.","title":"The __generator helper"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#opcodes","text":"The __generator helper uses opcodes which represent various operations that are interpreted by the helper to affect its internal state. The following table lists the various opcodes, their arguments, and their purpose: Opcode Arguments Purpose 0 (next) value Starts the generator, or resumes the generator with value as the result of the AwaitExpression where execution was paused. 1 (throw) value Resumes the generator, throwing value at AwaitExpression where execution was paused. 2 (return) value Exits the generator, executing any finally blocks starting at the AwaitExpression where execution was paused. 3 (break) label Performs an unconditional jump to the specified label, executing any finally between the current instruction and the label. 4 (yield) value Suspends the generator, setting the resume point at the next label and yielding the value. 5 (yieldstar) value Suspends the generator, setting the resume point at the next label and delegating operations to the supplied value. 6 (catch) error An internal instruction used to indicate an exception that was thrown from the body of the generator. 7 (endfinally) Exits a finally block, resuming any previous operation (such as a break, return, throw, etc.)","title":"Opcodes"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#state","text":"The _ , f , y , and t variables make up the persistent state of the __generator function. Each variable has a specific purpose, as described in the following sections:","title":"State"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#the-_-variable","text":"The __generator helper must share state between its internal step orchestration function and the body function passed to the helper. var _ = { label : 0 , sent : function () { if ( t [ 0 ] & 1 ) // NOTE: true for `throw`, but not `next` or `catch` throw t [ 1 ]; return sent [ 1 ]; }, trys : [], ops : [] }; The following table describes the members of the _ state object and their purpose: Name Description label Specifies the next switch case to execute in the body function. sent Handles the completion result passed to the generator. trys A stack of Protected Regions , which are 4-tuples that describe the labels that make up a try..catch..finally block. ops A stack of pending operations used for try..finally blocks. The __generator helper passes this state object to the body function for use with switching between switch cases in the body, handling completions from AwaitExpression , etc.","title":"The _ variable"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#the-f-variable","text":"The f variable indicates whether the generator is currently executing, to prevent re-entry of the same generator during its execution.","title":"The f variable"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#the-y-variable","text":"The y variable stores the iterator passed to a yieldstar instruction to which operations should be delegated.","title":"The y variable"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#the-t-variable","text":"The t variable is a temporary variable that stores one of the following values: The completion value when resuming from a yield or yield* . The error value for a catch block. The current Protected Region . The verb ( next , throw , or return method) to delegate to the expression of a yield* . The result of evaluating the verb delegated to the expression of a yield* . NOTE: None of the above cases overlap.","title":"The t variable"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#protected-regions","text":"A Protected Region is a region within the body function that indicates a try..catch..finally statement. It consists of a 4-tuple that contains 4 labels: Offset Description 0 Required The label that indicates the beginning of a try..catch..finally statement. 1 Optional The label that indicates the beginning of a catch clause. 2 Optional The label that indicates the beginning of a finally clause. 3 Required The label that indicates the end of the try..catch..finally statement.","title":"Protected Regions"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#the-generator-object","text":"The final step of the __generator helper is the allocation of an object that implements the Generator protocol, to be used by the __awaiter helper: return { next : verb ( 0 ), \"throw\" : verb ( 1 ), \"return\" : verb ( 2 ) }; function verb ( n ) { return function ( v ) { return step ([ n , v ]); }; } This object translates calls to next , throw , and return to the appropriate Opcodes and invokes the step orchestration function to continue execution. The throw and return method names are quoted to better support ES3.","title":"The generator object"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#orchestration","text":"The step function is the main orechestration mechanism for the __generator helper. It interprets opcodes, handles protected regions , and communicates results back to the caller. Here's a closer look at the step function: function step ( op ) { if ( f ) throw new TypeError ( \"Generator is already executing.\" ); while ( _ ) try { if ( f = 1 , y && ( t = y [ op [ 0 ] & 2 ? \"return\" : op [ 0 ] ? \"throw\" : \"next\" ]) && ! ( t = t . call ( y , op [ 1 ])). done ) return t ; if ( y = 0 , t ) op = [ 0 , t . value ]; switch ( op [ 0 ]) { case 0 : case 1 : t = op ; break ; case 4 : _.label ++ ; return { value : op [ 1 ], done : false }; case 5 : _.label ++ ; y = op [ 1 ]; op = [ 0 ]; continue ; case 7 : op = _ . ops . pop (); _ . trys . pop (); continue ; default : if ( ! ( t = _ . trys , t = t . length > 0 && t [ t . length - 1 ]) && ( op [ 0 ] === 6 || op [ 0 ] === 2 )) { _ = 0 ; continue ; } if ( op [ 0 ] === 3 && ( ! t || ( op [ 1 ] > t [ 0 ] && op [ 1 ] < t [ 3 ]))) { _ . label = op [ 1 ]; break ; } if ( op [ 0 ] === 6 && _ . label < t [ 1 ]) { _ . label = t [ 1 ]; t = op ; break ; } if ( t && _ . label < t [ 2 ]) { _ . label = t [ 2 ]; _ . ops . push ( op ); break ; } if ( t [ 2 ]) _ . ops . pop (); _ . trys . pop (); continue ; } op = body . call ( thisArg , _ ); } catch ( e ) { op = [ 6 , e ]; y = 0 ; } finally { f = t = 0 ; } if ( op [ 0 ] & 5 ) throw op [ 1 ]; return { value : op [ 0 ] ? op [ 1 ] : void 0 , done : true }; } The main body of step exists in a while loop. This allows us to continually interpret operations until we have reached some completion value, be it a return , await , or throw .","title":"Orchestration"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#preventing-re-entry","text":"The first part of the step function is used as a check to prevent re-entry into a currently executing generator: if ( f ) throw new TypeError ( \"Generator is already executing.\" );","title":"Preventing re-entry"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#running-the-generator","text":"The main body of the step function consists of a while loop which continues to evaluate instructions until the generator exits or is suspended: while ( _ ) try ... When the generator has run to completion, the _ state variable will be cleared, forcing the loop to exit.","title":"Running the generator"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#evaluating-the-generator-body","text":"try { ... op = body . call ( thisArg , _ ); } catch ( e ) { op = [ 6 , e ]; y = 0 ; } finally { f = t = 0 ; } Depending on the current operation, we re-enter the generator body to start or continue execution. Here we invoke body with thisArg as the this binding and the _ state object as the only argument. The result is a tuple that contains the next Opcode and argument. If evaluation of the body resulted in an exception, we convert this into an Opcode 6 (\"catch\") operation to be handled in the next spin of the while loop. We also clear the y variable in case it is set to ensure we are no longer delegating operations as the exception occurred in user code outside of, or at the function boundary of, the delegated iterator (otherwise the iterator would have handled the exception itself). After executing user code, we clear the f flag that indicates we are executing the generator, as well as the t temporary value so that we don't hold onto values sent to the generator for longer than necessary. Inside of the try..finally statement are a series of statements that are used to evaluate the operations of the transformed generator body. The first thing we do is mark the generator as executing: if ( f = 1 , ...) Despite the fact this expression is part of the head of an if statement, the comma operator causes it to be evaluated and the result thrown out. This is a minification added purely to reduce the overall footprint of the helper.","title":"Evaluating the generator body."},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#delegating-yield","text":"The first two statements of the try..finally statement handle delegation for yield* : if ( f = 1 , y && ( t = y [ op [ 0 ] & 2 ? \"return\" : op [ 0 ] ? \"throw\" : \"next\" ]) && ! ( t = t . call ( y , op [ 1 ])). done ) return t ; if ( y = 0 , t ) op = [ 0 , t . value ]; If the y variable is set, and y has a next , throw , or return method (depending on the current operation), we invoke this method and store the return value (an IteratorResult) in t . If t indicates it is a yielded value (e.g. t.done === false ), we return t to the caller. If t indicates it is a returned value (e.g. t.done === true ), we mark the operation with the next Opcode, and the returned value. If y did not have the appropriate method, or t was a returned value, we reset y to a falsey value and continue processing the operation.","title":"Delegating yield*"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#handling-operations","text":"The various Opcodes are handled in the following switch statement: switch ( op [ 0 ]) { case 0 : case 1 : t = op ; break ; case 4 : _.label ++ ; return { value : op [ 1 ], done : false }; case 5 : _.label ++ ; y = op [ 1 ]; op = [ 0 ]; continue ; case 7 : op = _ . ops . pop (); _ . trys . pop (); continue ; default : if ( ! ( t = _ . trys , t = t . length > 0 && t [ t . length - 1 ]) && ( op [ 0 ] === 6 || op [ 0 ] === 2 )) { _ = 0 ; continue ; } if ( op [ 0 ] === 3 && ( ! t || ( op [ 1 ] > t [ 0 ] && op [ 1 ] < t [ 3 ]))) { _ . label = op [ 1 ]; break ; } if ( op [ 0 ] === 6 && _ . label < t [ 1 ]) { _ . label = t [ 1 ]; t = op ; break ; } if ( t && _ . label < t [ 2 ]) { _ . label = t [ 2 ]; _ . ops . push ( op ); break ; } if ( t [ 2 ]) _ . ops . pop (); _ . trys . pop (); continue ; } The following sections describe the various Opcodes:","title":"Handling operations"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#opcode-0-next-and-opcode-1-throw","text":"case 0 : // next case 1 : // throw t = op ; break ; Both Opcode 0 (\"next\") and Opcode 1 (\"throw\") have the same behavior. The current operation is stored in the t variable and the body function is invoked. The body function should call _.sent() which will evaluate the appropriate completion result.","title":"Opcode 0 (\"next\") and Opcode 1 (\"throw\")"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#opcode-4-yield","text":"case 4 : // yield _ . label ++ ; return { value : op [ 1 ], done : false }; When we encounter Opcode 4 (\"yield\"), we increment the label by one to indicate the point at which the generator will resume execution. We then return an IteratorResult whose value is the yielded value, and done is false .","title":"Opcode 4 (\"yield\")"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#opcode-5-yieldstar","text":"case 5 : // yieldstar _ . label ++ ; y = op [ 1 ]; op = [ 0 ]; continue ; When we receive Opcode 5 (\"yieldstar\"), we increment the label by one to indicate the point at which the generator will resume execution. We then store the iterator in op[1] in the y variable, and set the operation to delegate to Opcode 0 (\"next\") with no value. Finally, we continue execution at the top of the loop to start delegation.","title":"Opcode 5 (\"yieldstar\")"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#opcode-7-endfinally","text":"case 7 : op = _ . ops . pop (); _ . trys . pop (); continue ; Opcode 7 (\"endfinally\") indicates that we have hit the end of a finally clause, and that the last operation recorded before entering the finally block should be evaluated.","title":"Opcode 7 (\"endfinally\")"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#opcode-2-return-opcode-3-break-and-opcode-6-catch","text":"default : if ( ! ( t = _ . trys , t = t . length > 0 && t [ t . length - 1 ]) && ( op [ 0 ] === 6 || op [ 0 ] === 2 )) { _ = 0 ; continue ; } if ( op [ 0 ] === 3 && ( ! t || ( op [ 1 ] > t [ 0 ] && op [ 1 ] < t [ 3 ]))) { _ . label = op [ 1 ]; break ; } if ( op [ 0 ] === 6 && _ . label < t [ 1 ]) { _ . label = t [ 1 ]; t = op ; break ; } if ( t && _ . label < t [ 2 ]) { _ . label = t [ 2 ]; _ . ops . push ( op ); break ; } if ( t [ 2 ]) _ . ops . pop (); _ . trys . pop (); continue ; } The handling for Opcode 2 (\"return\"), Opcode 3 (\"break\") and Opcode 6 (\"catch\") is more complicated, as we must obey the specified runtime semantics of generators. The first line in this clause gets the current Protected Region if found and stores it in the t temp variable: if ( ! ( t = _ . trys , t = t . length > 0 && t [ t . length - 1 ]) && ...) ... The remainder of this statement, as well as the following by several if statements test for more complex conditions. The first of these is the following: if ( ! ( t = ...) && ( op [ 0 ] === 6 || op [ 0 ] === 2 )) { _ = 0 ; continue ; } If we encounter an Opcode 6 (\"catch\") or Opcode 2 (\"return\"), and we are not in a protected region, then this operation completes the generator by setting the _ variable to a falsey value. The continue statement resumes execution at the top of the while statement, which will exit the loop so that we continue execution at the statement following the loop. if ( op [ 0 ] === 3 && ( ! t || ( op [ 1 ] > t [ 0 ] && op [ 1 ] < t [ 3 ]))) { _ . label = op [ 1 ]; break ; } The if statement above handles Opcode 3 (\"break\") when we are either not in a protected region , or are performing an unconditional jump to a label inside of the current protected region . In this case we can unconditionally jump to the specified label. if ( op [ 0 ] === 6 && _ . label < t [ 1 ]) { _ . label = t [ 1 ]; t = op ; break ; } The if statement above handles Opcode 6 (\"catch\") when inside the try block of a protected region . In this case we jump to the catch block, if present. We replace the value of t with the operation so that the exception can be read as the first statement of the transformed catch clause of the transformed generator body. if ( t && _ . label < t [ 2 ]) { _ . label = t [ 2 ]; _ . ops . push ( op ); break ; } This if statement handles all Opcodes when in a protected region with a finally clause. As long as we are not already inside the finally clause, we jump to the finally clause and push the pending operation onto the _.ops stack. This allows us to resume execution of the pending operation once we have completed execution of the finally clause, as long as it does not supersede this operation with its own completion value. if ( t [ 2 ]) _ . ops . pop (); Any other completion value inside of a finally clause will supersede the pending completion value from the try or catch clauses. The above if statement pops the pending completion from the stack. _ . trys . pop (); continue ; The remaining statements handle the point at which we exit a protected region . Here we pop the current protected region from the stack and spin the while statement to evaluate the current operation again in the next protected region or at the function boundary.","title":"Opcode 2 (\"return\"), Opcode 3 (\"break\"), and Opcode 6 (\"catch\")"},{"location":"sp-addinhelpers/node_modules/tslib/docs/generator/#handling-a-completed-generator","text":"Once the generator has completed, the _ state variable will be falsey. As a result, the while loop will terminate and hand control off to the final statement of the orchestration function, which deals with how a completed generator is evaluated: if ( op [ 0 ] & 5 ) throw op [ 1 ]; return { value : op [ 0 ] ? op [ 1 ] : void 0 , done : true }; If the caller calls throw on the generator it will send Opcode 1 (\"throw\"). If an exception is uncaught within the body of the generator, it will send Opcode 6 (\"catch\"). As the generator has completed, it throws the exception. Both of these cases are caught by the bitmask 5 , which does not collide with the only two other valid completion Opcodes. If the caller calls next on the generator, it will send Opcode 0 (\"next\"). As the generator has completed, it returns an IteratorResult where value is undefined and done is true. If the caller calls return on the generator, it will send Opcode 2 (\"return\"). As the generator has completed, it returns an IteratorResult where value is the value provided to return , and done is true.","title":"Handling a completed generator"},{"location":"sp-clientsvc/docs/","text":"@pnp/sp-clientsvc \u00b6 This library provides base classes for working with the legacy SharePoint client.svc/ProcessQuery endpoint. The base classes support most of the possibilities for types of query calls, as well as supporting fluent batching and caching. They are based on the same @pnp/queryable foundation as the other libraries so should feel familiar when extending. You can see @pnp/sp-taxonomy for an example showing how to extend these base classes into a functional fluent model. UML \u00b6 Graphical UML diagram of @pnp/sp-clientsvc. Right-click the diagram and open in new tab if it is too small.","title":"sp-clientsvc"},{"location":"sp-clientsvc/docs/#pnpsp-clientsvc","text":"This library provides base classes for working with the legacy SharePoint client.svc/ProcessQuery endpoint. The base classes support most of the possibilities for types of query calls, as well as supporting fluent batching and caching. They are based on the same @pnp/queryable foundation as the other libraries so should feel familiar when extending. You can see @pnp/sp-taxonomy for an example showing how to extend these base classes into a functional fluent model.","title":"@pnp/sp-clientsvc"},{"location":"sp-clientsvc/docs/#uml","text":"Graphical UML diagram of @pnp/sp-clientsvc. Right-click the diagram and open in new tab if it is too small.","title":"UML"},{"location":"sp-taxonomy/docs/","text":"@pnp/sp-taxonomy \u00b6 This module provides a fluent interface for working with the SharePoint term store. It does not rely on SP.taxonomy.js or other dependencies outside the @pnp scope. It is designed to function in a similar manner and present a similar feel to the other data retrieval libraries. It works by calling the \"/_vti_bin/client.svc/ProcessQuery\" endpoint. Getting Started \u00b6 You will need to install the @pnp/sp-taxonomy package as well as the packages it requires to run. npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/sp-taxonomy @pnp/sp-clientsvc --save Root Object \u00b6 All fluent taxonomy operations originate from the Taxonomy object. You can access it in several ways. Import existing instance \u00b6 This method will grab an existing instance of the Taxonomy class and allow you to immediately chain additional methods. import { taxonomy } from \"@pnp/sp-taxonomy\" ; await taxonomy . termStores . get (); Import class and create instance \u00b6 You can also import the Taxonomy class and create a new instance. This useful in those cases where you want to work with taxonomy in another web than the current web. import { Session } from \"@pnp/sp-taxonomy\" ; const taxonomy = new Session ( \"https://mytenant.sharepoint.com/sites/dev\" ); await taxonomy . termStores . get (); Setup \u00b6 Because the sp-taxonomy library uses the same @pnp/queryable request pipeline as the other libraries you can call the setup method with the same options used for the @pnp/sp library. The setup method is provided as shorthand and avoids the need to import anything from @pnp/sp if you do not need to. A call to this setup method is equivilent to calling the sp.setup method and the configuration is shared between the libraries within your application. In the below example all requests for the @pnp/sp-taxonomy library and the @pnp/sp library will be routed through the specified SPFetchClient. Sharing the configuration like this handles the most common scenario of working on the same web easily. You can set other values here as well such as baseUrl and they will be respected by both libraries. import { taxonomy } from \"@pnp/sp-taxonomy\" ; import { SPFetchClient } from \"@pnp/nodejs\" ; // example for setting up the node client using setup method // we also set a custom header, as an example taxonomy . setup ({ sp : { fetchClientFactory : () => { return new SPFetchClient ( \"{url}\" , \"{client id}\" , \"{client secret}\" ); }, headers : { \"X-Custom-Header\" : \"A Great Value\" , }, }, }); Library Topics \u00b6 Term Stores Term Groups Term Sets Terms Labels UML \u00b6 Graphical UML diagram of @pnp/sp-taxonomy. Right-click the diagram and open in new tab if it is too small.","title":"sp-taxonomy"},{"location":"sp-taxonomy/docs/#pnpsp-taxonomy","text":"This module provides a fluent interface for working with the SharePoint term store. It does not rely on SP.taxonomy.js or other dependencies outside the @pnp scope. It is designed to function in a similar manner and present a similar feel to the other data retrieval libraries. It works by calling the \"/_vti_bin/client.svc/ProcessQuery\" endpoint.","title":"@pnp/sp-taxonomy"},{"location":"sp-taxonomy/docs/#getting-started","text":"You will need to install the @pnp/sp-taxonomy package as well as the packages it requires to run. npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/sp-taxonomy @pnp/sp-clientsvc --save","title":"Getting Started"},{"location":"sp-taxonomy/docs/#root-object","text":"All fluent taxonomy operations originate from the Taxonomy object. You can access it in several ways.","title":"Root Object"},{"location":"sp-taxonomy/docs/#import-existing-instance","text":"This method will grab an existing instance of the Taxonomy class and allow you to immediately chain additional methods. import { taxonomy } from \"@pnp/sp-taxonomy\" ; await taxonomy . termStores . get ();","title":"Import existing instance"},{"location":"sp-taxonomy/docs/#import-class-and-create-instance","text":"You can also import the Taxonomy class and create a new instance. This useful in those cases where you want to work with taxonomy in another web than the current web. import { Session } from \"@pnp/sp-taxonomy\" ; const taxonomy = new Session ( \"https://mytenant.sharepoint.com/sites/dev\" ); await taxonomy . termStores . get ();","title":"Import class and create instance"},{"location":"sp-taxonomy/docs/#setup","text":"Because the sp-taxonomy library uses the same @pnp/queryable request pipeline as the other libraries you can call the setup method with the same options used for the @pnp/sp library. The setup method is provided as shorthand and avoids the need to import anything from @pnp/sp if you do not need to. A call to this setup method is equivilent to calling the sp.setup method and the configuration is shared between the libraries within your application. In the below example all requests for the @pnp/sp-taxonomy library and the @pnp/sp library will be routed through the specified SPFetchClient. Sharing the configuration like this handles the most common scenario of working on the same web easily. You can set other values here as well such as baseUrl and they will be respected by both libraries. import { taxonomy } from \"@pnp/sp-taxonomy\" ; import { SPFetchClient } from \"@pnp/nodejs\" ; // example for setting up the node client using setup method // we also set a custom header, as an example taxonomy . setup ({ sp : { fetchClientFactory : () => { return new SPFetchClient ( \"{url}\" , \"{client id}\" , \"{client secret}\" ); }, headers : { \"X-Custom-Header\" : \"A Great Value\" , }, }, });","title":"Setup"},{"location":"sp-taxonomy/docs/#library-topics","text":"Term Stores Term Groups Term Sets Terms Labels","title":"Library Topics"},{"location":"sp-taxonomy/docs/#uml","text":"Graphical UML diagram of @pnp/sp-taxonomy. Right-click the diagram and open in new tab if it is too small.","title":"UML"},{"location":"sp-taxonomy/docs/labels/","text":"@pnp/sp-taxonomy/labels \u00b6 Load labels \u00b6 You can load labels by accessing the labels property of a term . import { ILabel , ILabelData , ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < see terms article for loading term > // load the terms merged with data const labelsWithData : ( ILabel & ILabelData )[] = await term . labels . get (); // get a label by value const label : ILabel = term . labels . getByValue ( \"term value\" ); // get a label merged with data const label2 : ILabel & ILabelData = term . labels . getByValue ( \"term value\" ). get (); Label Properties and Methods \u00b6 setAsDefaultForLanguage \u00b6 Sets this labels as the default for the language import { ILabel , ILabelData , ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < see terms article for loading term > // get a label by value await term . labels . getByValue ( \"term value\" ). setAsDefaultForLanguage (); delete \u00b6 Deletes this label import { ILabel , ILabelData , ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < see terms article for loading term > // get a label by value await term . labels . getByValue ( \"term value\" ). delete ();","title":"Labels"},{"location":"sp-taxonomy/docs/labels/#pnpsp-taxonomylabels","text":"","title":"@pnp/sp-taxonomy/labels"},{"location":"sp-taxonomy/docs/labels/#load-labels","text":"You can load labels by accessing the labels property of a term . import { ILabel , ILabelData , ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < see terms article for loading term > // load the terms merged with data const labelsWithData : ( ILabel & ILabelData )[] = await term . labels . get (); // get a label by value const label : ILabel = term . labels . getByValue ( \"term value\" ); // get a label merged with data const label2 : ILabel & ILabelData = term . labels . getByValue ( \"term value\" ). get ();","title":"Load labels"},{"location":"sp-taxonomy/docs/labels/#label-properties-and-methods","text":"","title":"Label Properties and Methods"},{"location":"sp-taxonomy/docs/labels/#setasdefaultforlanguage","text":"Sets this labels as the default for the language import { ILabel , ILabelData , ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < see terms article for loading term > // get a label by value await term . labels . getByValue ( \"term value\" ). setAsDefaultForLanguage ();","title":"setAsDefaultForLanguage"},{"location":"sp-taxonomy/docs/labels/#delete","text":"Deletes this label import { ILabel , ILabelData , ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < see terms article for loading term > // get a label by value await term . labels . getByValue ( \"term value\" ). delete ();","title":"delete"},{"location":"sp-taxonomy/docs/term-groups/","text":"@pnp/sp-taxonomy/termgroups \u00b6 Term groups are used as a container for terms within a term store. Load a term group \u00b6 Term groups are loaded from a term store import { taxonomy , ITermStore , ITermGroup } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup = store . getTermGroupById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); Term Group methods and properties \u00b6 addContributor \u00b6 Adds a contributor to the Group import { taxonomy , ITermStore , ITermGroup } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup = store . getTermGroupById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); await group . addContributor ( \"i:0#.f|membership|person@tenant.com\" ); addGroupManager \u00b6 Adds a group manager to the Group import { taxonomy , ITermStore , ITermGroup } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup = store . getTermGroupById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); await group . addGroupManager ( \"i:0#.f|membership|person@tenant.com\" ); createTermSet \u00b6 Creates a new term set import { taxonomy , ITermStore , ITermGroup , ITermSet , ITermSetData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup = store . getTermGroupById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const set : ITermSet & ITermSetData = await group . createTermSet ( \"name\" , 1031 ); // you can optionally supply the term set id, if you do not we create a new id for you const set2 : ITermSet & ITermSetData = await group . createTermSet ( \"name\" , 1031 , \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); get \u00b6 Gets this term group's data import { taxonomy , ITermStore , ITermGroupData , ITermGroup } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup & ITermGroupData = store . getTermGroupById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ). get ();","title":"Term Groups"},{"location":"sp-taxonomy/docs/term-groups/#pnpsp-taxonomytermgroups","text":"Term groups are used as a container for terms within a term store.","title":"@pnp/sp-taxonomy/termgroups"},{"location":"sp-taxonomy/docs/term-groups/#load-a-term-group","text":"Term groups are loaded from a term store import { taxonomy , ITermStore , ITermGroup } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup = store . getTermGroupById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" );","title":"Load a term group"},{"location":"sp-taxonomy/docs/term-groups/#term-group-methods-and-properties","text":"","title":"Term Group methods and properties"},{"location":"sp-taxonomy/docs/term-groups/#addcontributor","text":"Adds a contributor to the Group import { taxonomy , ITermStore , ITermGroup } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup = store . getTermGroupById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); await group . addContributor ( \"i:0#.f|membership|person@tenant.com\" );","title":"addContributor"},{"location":"sp-taxonomy/docs/term-groups/#addgroupmanager","text":"Adds a group manager to the Group import { taxonomy , ITermStore , ITermGroup } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup = store . getTermGroupById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); await group . addGroupManager ( \"i:0#.f|membership|person@tenant.com\" );","title":"addGroupManager"},{"location":"sp-taxonomy/docs/term-groups/#createtermset","text":"Creates a new term set import { taxonomy , ITermStore , ITermGroup , ITermSet , ITermSetData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup = store . getTermGroupById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const set : ITermSet & ITermSetData = await group . createTermSet ( \"name\" , 1031 ); // you can optionally supply the term set id, if you do not we create a new id for you const set2 : ITermSet & ITermSetData = await group . createTermSet ( \"name\" , 1031 , \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" );","title":"createTermSet"},{"location":"sp-taxonomy/docs/term-groups/#get","text":"Gets this term group's data import { taxonomy , ITermStore , ITermGroupData , ITermGroup } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup & ITermGroupData = store . getTermGroupById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ). get ();","title":"get"},{"location":"sp-taxonomy/docs/term-sets/","text":"@pnp/sp-taxonomy/termsets \u00b6 Term sets contain terms within the taxonomy heirarchy. Load a term set \u00b6 You load a term set directly from a term store. import { taxonomy , ITermStore , ITermSet } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); Or you can load a term set from a collection - though if you know the id it is more efficient to get the term set directly. import { taxonomy , ITermStore , ITermSet } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set = store . getTermSetsByName ( \"my set\" , 1031 ). getById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const setWithData = store . getTermSetsByName ( \"my set\" , 1031 ). getByName ( \"my set\" ). get (); Term set methods and properties \u00b6 addStakeholder \u00b6 Adds a stakeholder to the TermSet import { taxonomy , ITermStore , ITermSet } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); await set . addStakeholder ( \"i:0#.f|membership|person@tenant.com\" ); deleteStakeholder \u00b6 Deletes a stakeholder to the TermSet import { taxonomy , ITermStore , ITermSet } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); await set . deleteStakeholder ( \"i:0#.f|membership|person@tenant.com\" ); get \u00b6 Gets the data for this TermSet import { taxonomy , ITermStore , ITermSet , ITermSetData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const setWithData : ITermSet & ITermSetData = await set . get (); terms \u00b6 Provides access to the terms collection for this termset import { taxonomy , ITermStore , ITermSet , ITerms , ITermData , ITerm } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const terms : ITerms = set . terms ; // load the data into the terms instances const termsWithData : ( ITermData & ITerm )[] = set . terms . get (); getTermById \u00b6 Gets a term by id from this set import { taxonomy , ITermStore , ITermSet , ITermData , ITerm } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const term : ITerm = set . getTermById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); // load the data into the term instances const termWithData : ITermData & ITerm = term . get (); addTerm \u00b6 Adds a term to a term set import { taxonomy , ITermStore , ITermSet , ITermData , ITerm } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const term : ITerm & ITermData = await set . addTerm ( \"name\" , 1031 , true ); // you can optionally set the id when you create the term const term2 : ITerm & ITermData = await set . addTerm ( \"name\" , 1031 , true , \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" );","title":"Term Sets"},{"location":"sp-taxonomy/docs/term-sets/#pnpsp-taxonomytermsets","text":"Term sets contain terms within the taxonomy heirarchy.","title":"@pnp/sp-taxonomy/termsets"},{"location":"sp-taxonomy/docs/term-sets/#load-a-term-set","text":"You load a term set directly from a term store. import { taxonomy , ITermStore , ITermSet } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); Or you can load a term set from a collection - though if you know the id it is more efficient to get the term set directly. import { taxonomy , ITermStore , ITermSet } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set = store . getTermSetsByName ( \"my set\" , 1031 ). getById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const setWithData = store . getTermSetsByName ( \"my set\" , 1031 ). getByName ( \"my set\" ). get ();","title":"Load a term set"},{"location":"sp-taxonomy/docs/term-sets/#term-set-methods-and-properties","text":"","title":"Term set methods and properties"},{"location":"sp-taxonomy/docs/term-sets/#addstakeholder","text":"Adds a stakeholder to the TermSet import { taxonomy , ITermStore , ITermSet } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); await set . addStakeholder ( \"i:0#.f|membership|person@tenant.com\" );","title":"addStakeholder"},{"location":"sp-taxonomy/docs/term-sets/#deletestakeholder","text":"Deletes a stakeholder to the TermSet import { taxonomy , ITermStore , ITermSet } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); await set . deleteStakeholder ( \"i:0#.f|membership|person@tenant.com\" );","title":"deleteStakeholder"},{"location":"sp-taxonomy/docs/term-sets/#get","text":"Gets the data for this TermSet import { taxonomy , ITermStore , ITermSet , ITermSetData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const setWithData : ITermSet & ITermSetData = await set . get ();","title":"get"},{"location":"sp-taxonomy/docs/term-sets/#terms","text":"Provides access to the terms collection for this termset import { taxonomy , ITermStore , ITermSet , ITerms , ITermData , ITerm } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const terms : ITerms = set . terms ; // load the data into the terms instances const termsWithData : ( ITermData & ITerm )[] = set . terms . get ();","title":"terms"},{"location":"sp-taxonomy/docs/term-sets/#gettermbyid","text":"Gets a term by id from this set import { taxonomy , ITermStore , ITermSet , ITermData , ITerm } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const term : ITerm = set . getTermById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); // load the data into the term instances const termWithData : ITermData & ITerm = term . get ();","title":"getTermById"},{"location":"sp-taxonomy/docs/term-sets/#addterm","text":"Adds a term to a term set import { taxonomy , ITermStore , ITermSet , ITermData , ITerm } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const term : ITerm & ITermData = await set . addTerm ( \"name\" , 1031 , true ); // you can optionally set the id when you create the term const term2 : ITerm & ITermData = await set . addTerm ( \"name\" , 1031 , true , \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" );","title":"addTerm"},{"location":"sp-taxonomy/docs/term-stores/","text":"@pnp/sp-taxonomy/termstores \u00b6 Term stores contain term groups, term sets, and terms. This article describes how to work find, load, and use a term store to access the terms inside. List term stores \u00b6 You can access a list of all term stores via the termstores property of the Taxonomy class. // get a list of term stores and return all properties const stores = await taxonomy . termStores . get (); // you can also select the fields to return for the term stores using the select operator. const stores2 = await taxonomy . termStores . select ( \"Name\" ). get (); Load a term store \u00b6 To load a specific term store you can use the getByName or getById methods. Using the get method executes the request to the server. const store = await taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ). get (); const store2 = await taxonomy . termStores . getById ( \"f6112509-fba7-4544-b2ed-ce6c9396b646\" ). get (); // you can use select as well with either method to choose the fields to return const store3 = await taxonomy . termStores . getById ( \"f6112509-fba7-4544-b2ed-ce6c9396b646\" ). select ( \"Name\" ). get (); For term stores and all other objects data is returned as a merger of the data and a new instance of the representative class. Allowing you to immediately begin acting on the object. IF you do not need the data, skip the get call until you do. // no data loaded yet, store is an instance of TermStore class const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); // I can call subsequent methods on the same object and will now have an object with data // I could have called get above as well - this is just an example const store2 : ITermStore & ITermStoreData = await store . get (); // log the Name property console . log ( store2 . Name ); // call another TermStore method on the same object await store2 . addLanguage ( 1031 ); Term store methods and properties \u00b6 get \u00b6 Loads the data for this term store import { taxonomy , ITermStore } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = await taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ). get (); getTermSetsByName \u00b6 Gets the collection of term sets with a matching name import { taxonomy , ITermSets } from \"@pnp/sp-taxonomy\" ; const sets : ITermSets = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ). getTermSetsByName ( \"My Set\" , 1033 ); getTermSetById \u00b6 Gets the term set with a matching id import { taxonomy , ITermStore , ITermSet } from \"@pnp/sp-taxonomy\" ; // note that you can also use instances if you wanted to conduct multiple operations on a single store const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); // we will handle normalizing guids for you as well :) const set2 : ITermSet = store . getTermSetById ( \"{a63aefc9-359d-42b7-a0d2-cb1809acd260}\" ); getTermById \u00b6 Gets a term by id import { taxonomy , ITermStore , ITerm , ITermData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const term : ITerm = store . getTermById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const termWithData : ITerm & ITermData = await term . get (); getTermsById \u00b6 Added in 1.2.6 import { taxonomy , ITermStore , ITerms , ITermData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const terms : ITerms = store . getTermsById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" , \"0ba6845c-1468-4ec5-a5a8-718f1fb05432\" ); const termWithData : ( ITerm & ITermData )[] = await term . get (); getTermGroupById \u00b6 Gets a term group by id import { taxonomy , ITermStore , ITermGroup } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup = store . getTermGroupById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); getTerms \u00b6 Gets terms that match the provided criteria. Please see this article for details on valid querys. import { taxonomy , ITermStore , ILabelMatchInfo , ITerm , ITermData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const terms : ITerms = store . getTerms ({ TermLabel : \"test label\" , TrimUnavailable : true , }); // load the data based on the above query const termsWithData : ( ITerm & ITermData )[] = terms . get (); // select works here too :) const termsWithData2 : ( ITerm & ITermData )[] = terms . select ( \"Name\" ). get (); addLanguage \u00b6 Adds a language to the term store by LCID import { taxonomy , ITermStore } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); await store . addLanguage ( 1031 ); addGroup \u00b6 Adds a term group to the term store import { taxonomy , ITermStore } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup & ITermGroupData = await store . addGroup ( \"My Group Name\" ); // you can optionally specify the guid of the group, if you don't we just create a new guid for you const groups : ITermGroup & ITermGroupData = await store . addGroup ( \"My Group Name\" , \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); commitAll \u00b6 Commits all updates to the database that have occurred since the last commit or rollback. import { taxonomy , ITermStore } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); await store . commitAll (); deleteLanguage \u00b6 Delete a working language from the TermStore import { taxonomy , ITermStore } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); await store . deleteLanguage ( 1031 ); rollbackAll \u00b6 Discards all updates that have occurred since the last commit or rollback. It is unlikely you will need to call this method through this library due to how things are structured. import { taxonomy , ITermStore } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); await store . rollbackAll ();","title":"Term Stores"},{"location":"sp-taxonomy/docs/term-stores/#pnpsp-taxonomytermstores","text":"Term stores contain term groups, term sets, and terms. This article describes how to work find, load, and use a term store to access the terms inside.","title":"@pnp/sp-taxonomy/termstores"},{"location":"sp-taxonomy/docs/term-stores/#list-term-stores","text":"You can access a list of all term stores via the termstores property of the Taxonomy class. // get a list of term stores and return all properties const stores = await taxonomy . termStores . get (); // you can also select the fields to return for the term stores using the select operator. const stores2 = await taxonomy . termStores . select ( \"Name\" ). get ();","title":"List term stores"},{"location":"sp-taxonomy/docs/term-stores/#load-a-term-store","text":"To load a specific term store you can use the getByName or getById methods. Using the get method executes the request to the server. const store = await taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ). get (); const store2 = await taxonomy . termStores . getById ( \"f6112509-fba7-4544-b2ed-ce6c9396b646\" ). get (); // you can use select as well with either method to choose the fields to return const store3 = await taxonomy . termStores . getById ( \"f6112509-fba7-4544-b2ed-ce6c9396b646\" ). select ( \"Name\" ). get (); For term stores and all other objects data is returned as a merger of the data and a new instance of the representative class. Allowing you to immediately begin acting on the object. IF you do not need the data, skip the get call until you do. // no data loaded yet, store is an instance of TermStore class const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); // I can call subsequent methods on the same object and will now have an object with data // I could have called get above as well - this is just an example const store2 : ITermStore & ITermStoreData = await store . get (); // log the Name property console . log ( store2 . Name ); // call another TermStore method on the same object await store2 . addLanguage ( 1031 );","title":"Load a term store"},{"location":"sp-taxonomy/docs/term-stores/#term-store-methods-and-properties","text":"","title":"Term store methods and properties"},{"location":"sp-taxonomy/docs/term-stores/#get","text":"Loads the data for this term store import { taxonomy , ITermStore } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = await taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ). get ();","title":"get"},{"location":"sp-taxonomy/docs/term-stores/#gettermsetsbyname","text":"Gets the collection of term sets with a matching name import { taxonomy , ITermSets } from \"@pnp/sp-taxonomy\" ; const sets : ITermSets = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ). getTermSetsByName ( \"My Set\" , 1033 );","title":"getTermSetsByName"},{"location":"sp-taxonomy/docs/term-stores/#gettermsetbyid","text":"Gets the term set with a matching id import { taxonomy , ITermStore , ITermSet } from \"@pnp/sp-taxonomy\" ; // note that you can also use instances if you wanted to conduct multiple operations on a single store const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const set : ITermSet = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); // we will handle normalizing guids for you as well :) const set2 : ITermSet = store . getTermSetById ( \"{a63aefc9-359d-42b7-a0d2-cb1809acd260}\" );","title":"getTermSetById"},{"location":"sp-taxonomy/docs/term-stores/#gettermbyid","text":"Gets a term by id import { taxonomy , ITermStore , ITerm , ITermData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const term : ITerm = store . getTermById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); const termWithData : ITerm & ITermData = await term . get ();","title":"getTermById"},{"location":"sp-taxonomy/docs/term-stores/#gettermsbyid","text":"Added in 1.2.6 import { taxonomy , ITermStore , ITerms , ITermData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const terms : ITerms = store . getTermsById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" , \"0ba6845c-1468-4ec5-a5a8-718f1fb05432\" ); const termWithData : ( ITerm & ITermData )[] = await term . get ();","title":"getTermsById"},{"location":"sp-taxonomy/docs/term-stores/#gettermgroupbyid","text":"Gets a term group by id import { taxonomy , ITermStore , ITermGroup } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup = store . getTermGroupById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" );","title":"getTermGroupById"},{"location":"sp-taxonomy/docs/term-stores/#getterms","text":"Gets terms that match the provided criteria. Please see this article for details on valid querys. import { taxonomy , ITermStore , ILabelMatchInfo , ITerm , ITermData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const terms : ITerms = store . getTerms ({ TermLabel : \"test label\" , TrimUnavailable : true , }); // load the data based on the above query const termsWithData : ( ITerm & ITermData )[] = terms . get (); // select works here too :) const termsWithData2 : ( ITerm & ITermData )[] = terms . select ( \"Name\" ). get ();","title":"getTerms"},{"location":"sp-taxonomy/docs/term-stores/#addlanguage","text":"Adds a language to the term store by LCID import { taxonomy , ITermStore } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); await store . addLanguage ( 1031 );","title":"addLanguage"},{"location":"sp-taxonomy/docs/term-stores/#addgroup","text":"Adds a term group to the term store import { taxonomy , ITermStore } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const group : ITermGroup & ITermGroupData = await store . addGroup ( \"My Group Name\" ); // you can optionally specify the guid of the group, if you don't we just create a new guid for you const groups : ITermGroup & ITermGroupData = await store . addGroup ( \"My Group Name\" , \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" );","title":"addGroup"},{"location":"sp-taxonomy/docs/term-stores/#commitall","text":"Commits all updates to the database that have occurred since the last commit or rollback. import { taxonomy , ITermStore } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); await store . commitAll ();","title":"commitAll"},{"location":"sp-taxonomy/docs/term-stores/#deletelanguage","text":"Delete a working language from the TermStore import { taxonomy , ITermStore } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); await store . deleteLanguage ( 1031 );","title":"deleteLanguage"},{"location":"sp-taxonomy/docs/term-stores/#rollbackall","text":"Discards all updates that have occurred since the last commit or rollback. It is unlikely you will need to call this method through this library due to how things are structured. import { taxonomy , ITermStore } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); await store . rollbackAll ();","title":"rollbackAll"},{"location":"sp-taxonomy/docs/terms/","text":"@pnp/sp-taxonomy/terms \u00b6 Terms are the individual entries with a term set. Load Terms \u00b6 You can load a collection of terms through a term set or term store . import { taxonomy , ITermStore , ITerms , ILabelMatchInfo , ITerm , ITermData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = await taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const labelMatchInfo : ILabelMatchInfo = { TermLabel : \"My Label\" , TrimUnavailable : true , }; const terms : ITerms = store . getTerms ( labelMatchInfo ); // get term instances merged with data const terms2 : ( ITermData & ITerm )[] = await store . getTerms ( labelMatchInfo ). get (); const terms3 : ITerms = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ). terms ; // get terms merged with data from a term set const terms4 : ( ITerm & ITermData )[] = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ). terms . get (); Load Single Term \u00b6 You can get a single term a variety of ways as shown below. The \"best\" way will be determined by what information is available to do the lookup but ultimately will result in the same end product. import { taxonomy , ITermStore , ITerms , ILabelMatchInfo , ITerm , ITermData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = await taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); // get a single term by id const term : ITerm = store . getTermById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); // get single get merged with data const term2 : ITerm = store . getTermById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ). get (); // use select to choose which fields to return const term3 : ITerm = store . getTermById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ). select ( \"Name\" ). get (); // get a term from a term set const term4 : ITerm = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ). getTermById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); Term methods and properties \u00b6 labels \u00b6 Accesses the labels collection for this term import { taxonomy , ITermStore , ITerm , ILabels } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; const labels : ILabels = term . labels ; // labels merged with data const labelsWithData = term . labels . get (); createLabel \u00b6 Creates a new label for this Term import { taxonomy , ITermStore , ITerm , ILabelData , ILabel } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; const label : ILabelData & ILabel = term . createLabel ( \"label text\" , 1031 ); // optionally specify this is the default label const label2 : ILabelData & ILabel = term . createLabel ( \"label text\" , 1031 , true ); deprecate \u00b6 Sets the deprecation flag on a term import { ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; await term . deprecate ( true ); get \u00b6 Loads the term data import { ITerm , ITermData } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; // load term instance merged with data const term2 : ITerm & ITermData = await term . get (); getDescription \u00b6 Sets the description import { ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; // load term instance merged with data const description = await term . getDescription ( 1031 ); setDescription \u00b6 Sets the description import { ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; // load term instance merged with data await term . setDescription ( \"the description\" , 1031 ); setLocalCustomProperty \u00b6 Sets a custom property on this term import { ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; // load term instance merged with data await term . setLocalCustomProperty ( \"name\" , \"value\" ); addTerm \u00b6 Added in 1.2.8 Adds a child term to an existing term instance. import { ITerm } from \"@pnp/sp-taxonomy\" ; const parentTerm : ITerm = < from one of the above methods > ; await parentTerm . addTerm ( \"child 1\" , 1033 ); await parentTerm . addTerm ( \"child 2\" , 1033 );","title":"Terms"},{"location":"sp-taxonomy/docs/terms/#pnpsp-taxonomyterms","text":"Terms are the individual entries with a term set.","title":"@pnp/sp-taxonomy/terms"},{"location":"sp-taxonomy/docs/terms/#load-terms","text":"You can load a collection of terms through a term set or term store . import { taxonomy , ITermStore , ITerms , ILabelMatchInfo , ITerm , ITermData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = await taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); const labelMatchInfo : ILabelMatchInfo = { TermLabel : \"My Label\" , TrimUnavailable : true , }; const terms : ITerms = store . getTerms ( labelMatchInfo ); // get term instances merged with data const terms2 : ( ITermData & ITerm )[] = await store . getTerms ( labelMatchInfo ). get (); const terms3 : ITerms = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ). terms ; // get terms merged with data from a term set const terms4 : ( ITerm & ITermData )[] = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ). terms . get ();","title":"Load Terms"},{"location":"sp-taxonomy/docs/terms/#load-single-term","text":"You can get a single term a variety of ways as shown below. The \"best\" way will be determined by what information is available to do the lookup but ultimately will result in the same end product. import { taxonomy , ITermStore , ITerms , ILabelMatchInfo , ITerm , ITermData } from \"@pnp/sp-taxonomy\" ; const store : ITermStore = await taxonomy . termStores . getByName ( \"Taxonomy_v5o/SbcTE2cegwO2dtAN9l==\" ); // get a single term by id const term : ITerm = store . getTermById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ); // get single get merged with data const term2 : ITerm = store . getTermById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ). get (); // use select to choose which fields to return const term3 : ITerm = store . getTermById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ). select ( \"Name\" ). get (); // get a term from a term set const term4 : ITerm = store . getTermSetById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" ). getTermById ( \"0ba6845c-1468-4ec5-a5a8-718f1fb05431\" );","title":"Load Single Term"},{"location":"sp-taxonomy/docs/terms/#term-methods-and-properties","text":"","title":"Term methods and properties"},{"location":"sp-taxonomy/docs/terms/#labels","text":"Accesses the labels collection for this term import { taxonomy , ITermStore , ITerm , ILabels } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; const labels : ILabels = term . labels ; // labels merged with data const labelsWithData = term . labels . get ();","title":"labels"},{"location":"sp-taxonomy/docs/terms/#createlabel","text":"Creates a new label for this Term import { taxonomy , ITermStore , ITerm , ILabelData , ILabel } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; const label : ILabelData & ILabel = term . createLabel ( \"label text\" , 1031 ); // optionally specify this is the default label const label2 : ILabelData & ILabel = term . createLabel ( \"label text\" , 1031 , true );","title":"createLabel"},{"location":"sp-taxonomy/docs/terms/#deprecate","text":"Sets the deprecation flag on a term import { ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; await term . deprecate ( true );","title":"deprecate"},{"location":"sp-taxonomy/docs/terms/#get","text":"Loads the term data import { ITerm , ITermData } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; // load term instance merged with data const term2 : ITerm & ITermData = await term . get ();","title":"get"},{"location":"sp-taxonomy/docs/terms/#getdescription","text":"Sets the description import { ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; // load term instance merged with data const description = await term . getDescription ( 1031 );","title":"getDescription"},{"location":"sp-taxonomy/docs/terms/#setdescription","text":"Sets the description import { ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; // load term instance merged with data await term . setDescription ( \"the description\" , 1031 );","title":"setDescription"},{"location":"sp-taxonomy/docs/terms/#setlocalcustomproperty","text":"Sets a custom property on this term import { ITerm } from \"@pnp/sp-taxonomy\" ; const term : ITerm = < from one of the above methods > ; // load term instance merged with data await term . setLocalCustomProperty ( \"name\" , \"value\" );","title":"setLocalCustomProperty"},{"location":"sp-taxonomy/docs/terms/#addterm","text":"Added in 1.2.8 Adds a child term to an existing term instance. import { ITerm } from \"@pnp/sp-taxonomy\" ; const parentTerm : ITerm = < from one of the above methods > ; await parentTerm . addTerm ( \"child 1\" , 1033 ); await parentTerm . addTerm ( \"child 2\" , 1033 );","title":"addTerm"},{"location":"sp-taxonomy/docs/utilities/","text":"@pnp/sp-taxonomy/utilities \u00b6 These are a collection of helper methods you may find useful. setItemMetaDataField \u00b6 Allows you to easily set the value of a metadata field in a list item. import { sp } from \"@pnp/sp\" ; import { taxonomy , setItemMetaDataField } from \"@pnp/sp-taxonomy\" ; // create a new item, or load an existing const itemResult = await sp . web . lists . getByTitle ( \"TaxonomyList\" ). items . add ({ Title : \"My Title\" , }); // get a term const term = await taxonomy . getDefaultSiteCollectionTermStore () . getTermById ( \"99992696-1111-1111-1111-15e65b221111\" ). get (); setItemMetaDataField ( itemResult . item , \"MetaDataFieldName\" , term ); setItemMetaDataMultiField \u00b6 Allows you to easily set the value of a multi-value metadata field in a list item. import { sp } from \"@pnp/sp\" ; import { taxonomy , setItemMetaDataMultiField } from \"@pnp/sp-taxonomy\" ; // create a new item, or load an existing const itemResult = await sp . web . lists . getByTitle ( \"TaxonomyList\" ). items . add ({ Title : \"My Title\" , }); // get a term const term = await taxonomy . getDefaultSiteCollectionTermStore () . getTermById ( \"99992696-1111-1111-1111-15e65b221111\" ). get (); // get another term const term2 = await taxonomy . getDefaultSiteCollectionTermStore () . getTermById ( \"99992696-1111-1111-1111-15e65b221112\" ). get (); // get yet another term const term3 = await taxonomy . getDefaultSiteCollectionTermStore () . getTermById ( \"99992696-1111-1111-1111-15e65b221113\" ). get (); setItemMetaDataMultiField ( itemResult . item , \"MultiValueMetaDataFieldName\" , term , term2 , term3 );","title":"Utilities"},{"location":"sp-taxonomy/docs/utilities/#pnpsp-taxonomyutilities","text":"These are a collection of helper methods you may find useful.","title":"@pnp/sp-taxonomy/utilities"},{"location":"sp-taxonomy/docs/utilities/#setitemmetadatafield","text":"Allows you to easily set the value of a metadata field in a list item. import { sp } from \"@pnp/sp\" ; import { taxonomy , setItemMetaDataField } from \"@pnp/sp-taxonomy\" ; // create a new item, or load an existing const itemResult = await sp . web . lists . getByTitle ( \"TaxonomyList\" ). items . add ({ Title : \"My Title\" , }); // get a term const term = await taxonomy . getDefaultSiteCollectionTermStore () . getTermById ( \"99992696-1111-1111-1111-15e65b221111\" ). get (); setItemMetaDataField ( itemResult . item , \"MetaDataFieldName\" , term );","title":"setItemMetaDataField"},{"location":"sp-taxonomy/docs/utilities/#setitemmetadatamultifield","text":"Allows you to easily set the value of a multi-value metadata field in a list item. import { sp } from \"@pnp/sp\" ; import { taxonomy , setItemMetaDataMultiField } from \"@pnp/sp-taxonomy\" ; // create a new item, or load an existing const itemResult = await sp . web . lists . getByTitle ( \"TaxonomyList\" ). items . add ({ Title : \"My Title\" , }); // get a term const term = await taxonomy . getDefaultSiteCollectionTermStore () . getTermById ( \"99992696-1111-1111-1111-15e65b221111\" ). get (); // get another term const term2 = await taxonomy . getDefaultSiteCollectionTermStore () . getTermById ( \"99992696-1111-1111-1111-15e65b221112\" ). get (); // get yet another term const term3 = await taxonomy . getDefaultSiteCollectionTermStore () . getTermById ( \"99992696-1111-1111-1111-15e65b221113\" ). get (); setItemMetaDataMultiField ( itemResult . item , \"MultiValueMetaDataFieldName\" , term , term2 , term3 );","title":"setItemMetaDataMultiField"}]} \ No newline at end of file diff --git a/docs/v3/v1/sitemap.xml b/docs/v3/v1/sitemap.xml deleted file mode 100644 index 8d198947b..000000000 --- a/docs/v3/v1/sitemap.xml +++ /dev/null @@ -1,428 +0,0 @@ - - - - https://pnp.github.io/pnpjs/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/getting-started/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/transition-guide/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/getting-started/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/getting-started-dev/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/gulp-commands/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/debugging/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/documentation/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/deployment/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/beta-versions/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/polyfill/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/package-structure/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/SPFx-On-Premesis-2016/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/transition-guide/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/documentation/packages/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/common/docs/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/common/docs/adalclient/ - 2019-12-20 - daily - - - - - daily - - - https://pnp.github.io/pnpjs/common/docs/collections/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/common/docs/custom-httpclientimpl/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/common/docs/libconfig/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/common/docs/netutil/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/common/docs/storage/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/common/docs/util/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/config-store/docs/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/config-store/docs/configuration/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/config-store/docs/providers/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/graph/docs/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/graph/docs/contacts/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/graph/docs/directoryobjects/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/graph/docs/invitations/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/graph/docs/onedrive/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/graph/docs/planner/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/graph/docs/subscriptions/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/graph/docs/teams/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/logging/docs/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/nodejs/docs/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/nodejs/docs/adal-fetch-client/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/nodejs/docs/sp-fetch-client/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/nodejs/docs/bearer-token-fetch-client/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/nodejs/docs/provider-hosted-app/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/queryable/docs/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/queryable/docs/caching/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/queryable/docs/core/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/queryable/docs/odata-batch/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/queryable/docs/parsers/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/queryable/docs/pipeline/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/queryable/docs/queryable/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/pnpjs/docs/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/alias-parameters/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/alm/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/attachments/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/client-side-pages/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/content-types/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/entity-merging/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/features/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/fields/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/files/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/items/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/navigation-service/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/permissions/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/profiles/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/related-items/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/search/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/sharing/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/sites/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/sitedesigns/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/social/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/sp-utilities-utility/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/tenant-properties/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/views/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/webs/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp/docs/comments-likes/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp-addinhelpers/docs/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp-addinhelpers/docs/sp-request-executor-client/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp-addinhelpers/docs/sp-rest-addin/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp-clientsvc/docs/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp-taxonomy/docs/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp-taxonomy/docs/term-stores/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp-taxonomy/docs/term-groups/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp-taxonomy/docs/term-sets/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp-taxonomy/docs/terms/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp-taxonomy/docs/labels/ - 2019-12-20 - daily - - - https://pnp.github.io/pnpjs/sp-taxonomy/docs/utilities/ - 2019-12-20 - daily - - \ No newline at end of file diff --git a/docs/v3/v1/sitemap.xml.gz b/docs/v3/v1/sitemap.xml.gz deleted file mode 100644 index e47e640804b1f8d6bf96eae87b4f71310bf0807f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 878 zcmV-!1Cjh6iwFpW_xxP~|8r?{Wo=<_E_iKh0PUJhkJB&^hVT4}s&_ZLdq9A?<;E2x z;0H|XY2sbSHufal{Cn)CTgsON5+5%Tz0`57cN7qi(Ls z&!@iA5Z#mSc%S&g&X9HUwz=q8p5B}v|1%&a=y6~#Bt@L2W09hY%0fxlpkhiQU-U0c z)s5Ehe>s08rZPOiqK+;q9fpCtfn7z0+p+74qOgFUUBRjtU6|`NDq+h)>a?%GPR1DA zytmH5@{zu$j*Ih2qn0tjI`g-0UOlMSUcE^?aJIRiTtEH4L;e!$1FfFAxhyUc6p9Qv z?d%`-iNy2ea@6Q_)aJ3nrYgscdT@&7r+x zR7mS5yM$_(e8ICxc4A1l=*W9ckZgHuvrabRnpT(wATk%0k`9)$^TF_vRDnX(Y(?!^ zbg%wK((T+NtLi@vYPQ2Bd5V;umk=xTneBwF0rGU@`290ToP%d9O2B> zQ-a(I>dTH96U%V69pdWDL0fWwTv652LMxW@sWszKmcWfl=vG`@alsW84{@@50K__Q z3mJhS!c*-t?G2&~k diff --git a/docs/v3/v1/sp-addinhelpers/docs/index.html b/docs/v3/v1/sp-addinhelpers/docs/index.html deleted file mode 100644 index b100c6d53..000000000 --- a/docs/v3/v1/sp-addinhelpers/docs/index.html +++ /dev/null @@ -1,1771 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sp-addinhelpers - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp-addinhelpers

    -

    npm version

    -

    This module contains classes to allow use of the libraries within a SharePoint add-in.

    -

    Getting Started

    -

    Install the library and all dependencies,

    -

    npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/sp-addinhelpers --save

    -

    Now you can make requests to the host web from your add-in using the crossDomainWeb method.

    -
    // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods
    -import { sp, SPRequestExecutorClient } from "@pnp/sp-addinhelpers";
    -
    -// this only needs to be done once within your application
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -            return new SPRequestExecutorClient();
    -        }
    -    }
    -});
    -
    -// now we need to use the crossDomainWeb method to make our requests to the host web
    -const addInWenUrl = "{The add-in web url, likely from the query string}";
    -const hostWebUrl = "{The host web url, likely from the query string}";
    -
    -// make requests into the host web via the SP.RequestExecutor
    -sp.crossDomainWeb(addInWenUrl, hostWebUrl).get().then(w => {
    -    console.log(JSON.stringify(w, null, 4));
    -});
    -
    - - -

    Libary Topics

    - -

    UML

    -

    Graphical UML diagram

    -

    Graphical UML diagram of @pnp/sp-addinhelpers. Right-click the diagram and open in new tab if it is too small.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp-addinhelpers/docs/sp-request-executor-client/index.html b/docs/v3/v1/sp-addinhelpers/docs/sp-request-executor-client/index.html deleted file mode 100644 index 084fdfd78..000000000 --- a/docs/v3/v1/sp-addinhelpers/docs/sp-request-executor-client/index.html +++ /dev/null @@ -1,1760 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SPRequestExecutorClient - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp-addinhelpers/sprequestexecutorclient

    -

    The SPRequestExecutorClient is an implementation of the HttpClientImpl interface that facilitates requests to SharePoint from an add-in. It relies on -the SharePoint SP product libraries being present to allow use of the SP.RequestExecutor to make the request.

    -

    Setup

    -

    To use the client you need to set it using the fetch client factory using the setup method as shown below. This is only required when working within a -SharePoint add-in web.

    -
    // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods
    -import { sp, SPRequestExecutorClient } from "@pnp/sp-addinhelpers";
    -
    -// this only needs to be done once within your application
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -            return new SPRequestExecutorClient();
    -        }
    -    }
    -});
    -
    -// now we need to use the crossDomainWeb method to make our requests to the host web
    -const addInWenUrl = "{The add-in web url, likely from the query string}";
    -const hostWebUrl = "{The host web url, likely from the query string}";
    -
    -// make requests into the host web via the SP.RequestExecutor
    -sp.crossDomainWeb(addInWenUrl, hostWebUrl).get().then(w => {
    -    console.log(JSON.stringify(w, null, 4));
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp-addinhelpers/docs/sp-rest-addin/index.html b/docs/v3/v1/sp-addinhelpers/docs/sp-rest-addin/index.html deleted file mode 100644 index 72c4fff46..000000000 --- a/docs/v3/v1/sp-addinhelpers/docs/sp-rest-addin/index.html +++ /dev/null @@ -1,1711 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SPRestAddIn - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp-addinhelpers/sprestaddin

    -

    This class extends the sp export from @pnp/sp and adds in the methods required to make cross domain calls

    -
    // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods
    -import { sp, SPRequestExecutorClient } from "@pnp/sp-addinhelpers";
    -
    -// this only needs to be done once within your application
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -            return new SPRequestExecutorClient();
    -        }
    -    }
    -});
    -
    -// now we need to use the crossDomainWeb method to make our requests to the host web
    -const addInWenUrl = "{The add-in web url, likely from the query string}";
    -const hostWebUrl = "{The host web url, likely from the query string}";
    -
    -// make requests into the host web via the SP.RequestExecutor
    -sp.crossDomainWeb(addInWenUrl, hostWebUrl).get().then(w => {
    -    console.log(JSON.stringify(w, null, 4));
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp-clientsvc/docs/index.html b/docs/v3/v1/sp-clientsvc/docs/index.html deleted file mode 100644 index a1607fbe7..000000000 --- a/docs/v3/v1/sp-clientsvc/docs/index.html +++ /dev/null @@ -1,1736 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sp-clientsvc - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp-clientsvc

    -

    This library provides base classes for working with the legacy SharePoint client.svc/ProcessQuery endpoint. The base classes support most of the possibilities for types of query calls, as well as supporting fluent batching and caching. They are based on the same @pnp/queryable foundation as the other libraries so should feel familiar when extending. You can see @pnp/sp-taxonomy for an example showing how to extend these base classes into a functional fluent model.

    -

    UML

    -

    Graphical UML diagram

    -

    Graphical UML diagram of @pnp/sp-clientsvc. Right-click the diagram and open in new tab if it is too small.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp-taxonomy/docs/index.html b/docs/v3/v1/sp-taxonomy/docs/index.html deleted file mode 100644 index d6553072b..000000000 --- a/docs/v3/v1/sp-taxonomy/docs/index.html +++ /dev/null @@ -1,1887 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sp-taxonomy - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp-taxonomy

    -

    npm version

    -

    This module provides a fluent interface for working with the SharePoint term store. It does not rely on SP.taxonomy.js or other dependencies outside the @pnp scope. It is designed to function in a similar manner and present a similar feel to the other data retrieval libraries. It works by calling the "/_vti_bin/client.svc/ProcessQuery" endpoint.

    -

    Getting Started

    -

    You will need to install the @pnp/sp-taxonomy package as well as the packages it requires to run.

    -

    npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/sp-taxonomy @pnp/sp-clientsvc --save

    -

    Root Object

    -

    All fluent taxonomy operations originate from the Taxonomy object. You can access it in several ways.

    -

    Import existing instance

    -

    This method will grab an existing instance of the Taxonomy class and allow you to immediately chain additional methods.

    -
    import { taxonomy } from "@pnp/sp-taxonomy";
    -
    -await taxonomy.termStores.get();
    -
    - - -

    Import class and create instance

    -

    You can also import the Taxonomy class and create a new instance. This useful in those cases where you want to work with taxonomy in another web than the current web.

    -
    import { Session } from "@pnp/sp-taxonomy";
    -
    -const taxonomy = new Session("https://mytenant.sharepoint.com/sites/dev");
    -
    -await taxonomy.termStores.get();
    -
    - - -

    Setup

    -

    Because the sp-taxonomy library uses the same @pnp/queryable request pipeline as the other libraries you can call the setup method with the same options used for the @pnp/sp library. The setup method is provided as shorthand and avoids the need to import anything from @pnp/sp if you do not need to. A call to this setup method is equivilent to calling the sp.setup method and the configuration is shared between the libraries within your application.

    -

    In the below example all requests for the @pnp/sp-taxonomy library and the @pnp/sp library will be routed through the specified SPFetchClient. Sharing the configuration like this handles the most common scenario of working on the same web easily. You can set other values here as well such as baseUrl and they will be respected by both libraries.

    -
    import { taxonomy } from "@pnp/sp-taxonomy";
    -import { SPFetchClient } from "@pnp/nodejs";
    -
    -// example for setting up the node client using setup method
    -// we also set a custom header, as an example
    -taxonomy.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -            return new SPFetchClient("{url}", "{client id}", "{client secret}");
    -        },
    -        headers: {
    -            "X-Custom-Header": "A Great Value",
    -        },
    -    },
    -});
    -
    - - -

    Library Topics

    - -

    UML

    -

    Graphical UML diagram

    -

    Graphical UML diagram of @pnp/sp-taxonomy. Right-click the diagram and open in new tab if it is too small.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp-taxonomy/docs/labels/index.html b/docs/v3/v1/sp-taxonomy/docs/labels/index.html deleted file mode 100644 index ec247fd16..000000000 --- a/docs/v3/v1/sp-taxonomy/docs/labels/index.html +++ /dev/null @@ -1,1827 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Labels - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp-taxonomy/labels

    -

    Load labels

    -

    You can load labels by accessing the labels property of a term.

    -
    import { ILabel, ILabelData, ITerm } from "@pnp/sp-taxonomy";
    -
    -const term: ITerm = <see terms article for loading term>
    -
    -// load the terms merged with data
    -const labelsWithData: (ILabel & ILabelData)[] = await term.labels.get();
    -
    -
    -// get a label by value
    -const label: ILabel = term.labels.getByValue("term value");
    -
    -// get a label merged with data
    -const label2: ILabel & ILabelData = term.labels.getByValue("term value").get();
    -
    - - -

    Label Properties and Methods

    -

    setAsDefaultForLanguage

    -

    Sets this labels as the default for the language

    -
    import { ILabel, ILabelData, ITerm } from "@pnp/sp-taxonomy";
    -
    -const term: ITerm = <see terms article for loading term>
    -
    -// get a label by value
    -await term.labels.getByValue("term value").setAsDefaultForLanguage();
    -
    - - -

    delete

    -

    Deletes this label

    -
    import { ILabel, ILabelData, ITerm } from "@pnp/sp-taxonomy";
    -
    -const term: ITerm = <see terms article for loading term>
    -
    -// get a label by value
    -await term.labels.getByValue("term value").delete();
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp-taxonomy/docs/term-groups/index.html b/docs/v3/v1/sp-taxonomy/docs/term-groups/index.html deleted file mode 100644 index 6b2f4834e..000000000 --- a/docs/v3/v1/sp-taxonomy/docs/term-groups/index.html +++ /dev/null @@ -1,1875 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Term Groups - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp-taxonomy/termgroups

    -

    Term groups are used as a container for terms within a term store.

    -

    Load a term group

    -

    Term groups are loaded from a term store

    -
    import { taxonomy, ITermStore, ITermGroup } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const group: ITermGroup = store.getTermGroupById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    - - -

    Term Group methods and properties

    -

    addContributor

    -

    Adds a contributor to the Group

    -
    import { taxonomy, ITermStore, ITermGroup } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const group: ITermGroup = store.getTermGroupById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    -await group.addContributor("i:0#.f|membership|person@tenant.com");
    -
    - - -

    addGroupManager

    -

    Adds a group manager to the Group

    -
    import { taxonomy, ITermStore, ITermGroup } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const group: ITermGroup = store.getTermGroupById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    -await group.addGroupManager("i:0#.f|membership|person@tenant.com");
    -
    - - -

    createTermSet

    -

    Creates a new term set

    -
    import { taxonomy, ITermStore, ITermGroup, ITermSet, ITermSetData } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const group: ITermGroup = store.getTermGroupById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    -const set: ITermSet & ITermSetData = await group.createTermSet("name", 1031);
    -
    -// you can optionally supply the term set id, if you do not we create a new id for you
    -const set2: ITermSet & ITermSetData = await group.createTermSet("name", 1031, "0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    - - -

    get

    -

    Gets this term group's data

    -
    import { taxonomy, ITermStore, ITermGroupData, ITermGroup } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const group: ITermGroup & ITermGroupData = store.getTermGroupById("0ba6845c-1468-4ec5-a5a8-718f1fb05431").get();
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp-taxonomy/docs/term-sets/index.html b/docs/v3/v1/sp-taxonomy/docs/term-sets/index.html deleted file mode 100644 index 9a0b56511..000000000 --- a/docs/v3/v1/sp-taxonomy/docs/term-sets/index.html +++ /dev/null @@ -1,1946 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Term Sets - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp-taxonomy/termsets

    -

    Term sets contain terms within the taxonomy heirarchy.

    -

    Load a term set

    -

    You load a term set directly from a term store.

    -
    import { taxonomy, ITermStore, ITermSet } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const set: ITermSet = store.getTermSetById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    - - -

    Or you can load a term set from a collection - though if you know the id it is more efficient to get the term set directly.

    -
    import { taxonomy, ITermStore, ITermSet } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const set = store.getTermSetsByName("my set", 1031).getById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    -const setWithData = await store.getTermSetsByName("my set", 1031).getByName("my set").get();
    -
    - - -

    Term set methods and properties

    -

    addStakeholder

    -

    Adds a stakeholder to the TermSet

    -
    import { taxonomy, ITermStore, ITermSet } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const set: ITermSet = store.getTermSetById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    -await set.addStakeholder("i:0#.f|membership|person@tenant.com");
    -
    - - -

    deleteStakeholder

    -

    Deletes a stakeholder to the TermSet

    -
    import { taxonomy, ITermStore, ITermSet } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const set: ITermSet = store.getTermSetById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    -await set.deleteStakeholder("i:0#.f|membership|person@tenant.com");
    -
    - - -

    get

    -

    Gets the data for this TermSet

    -
    import { taxonomy, ITermStore, ITermSet, ITermSetData } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const set: ITermSet = store.getTermSetById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    -const setWithData: ITermSet & ITermSetData = await set.get();
    -
    - - -

    terms

    -

    Provides access to the terms collection for this termset

    -
    import { taxonomy, ITermStore, ITermSet, ITerms, ITermData, ITerm } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const set: ITermSet = store.getTermSetById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    -const terms: ITerms = set.terms;
    -
    -// load the data into the terms instances
    -const termsWithData: (ITermData & ITerm)[] = set.terms.get();
    -
    - - -

    getTermById

    -

    Gets a term by id from this set

    -
    import { taxonomy, ITermStore, ITermSet, ITermData, ITerm } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const set: ITermSet = store.getTermSetById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    -const term: ITerm = set.getTermById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    -// load the data into the term instances
    -const termWithData: ITermData & ITerm = term.get();
    -
    - - -

    addTerm

    -

    Adds a term to a term set

    -
    import { taxonomy, ITermStore, ITermSet, ITermData, ITerm } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const set: ITermSet = store.getTermSetById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    -const term: ITerm & ITermData = await set.addTerm("name", 1031, true);
    -
    -// you can optionally set the id when you create the term
    -const term2: ITerm & ITermData = await set.addTerm("name", 1031, true, "0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - diff --git a/docs/v3/v1/sp-taxonomy/docs/term-stores/index.html b/docs/v3/v1/sp-taxonomy/docs/term-stores/index.html deleted file mode 100644 index e68a993f7..000000000 --- a/docs/v3/v1/sp-taxonomy/docs/term-stores/index.html +++ /dev/null @@ -1,2111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Term Stores - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp-taxonomy/termstores

    -

    Term stores contain term groups, term sets, and terms. This article describes how to work find, load, and use a term store to access the terms inside.

    -

    List term stores

    -

    You can access a list of all term stores via the termstores property of the Taxonomy class.

    -
    // get a list of term stores and return all properties
    -const stores = await taxonomy.termStores.get();
    -
    -// you can also select the fields to return for the term stores using the select operator.
    -const stores2 = await taxonomy.termStores.select("Name").get();
    -
    - - -

    Load a term store

    -

    To load a specific term store you can use the getByName or getById methods. Using the get method executes the request to the server.

    -
    const store = await taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==").get();
    -
    -const store2 = await taxonomy.termStores.getById("f6112509-fba7-4544-b2ed-ce6c9396b646").get();
    -
    -// you can use select as well with either method to choose the fields to return
    -const store3 = await taxonomy.termStores.getById("f6112509-fba7-4544-b2ed-ce6c9396b646").select("Name").get();
    -
    - - -

    For term stores and all other objects data is returned as a merger of the data and a new instance of the representative class. Allowing you to immediately begin acting on the object. IF you do not need the data, skip the get call until you do.

    -
    // no data loaded yet, store is an instance of TermStore class
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -// I can call subsequent methods on the same object and will now have an object with data
    -// I could have called get above as well - this is just an example
    -const store2: ITermStore & ITermStoreData = await store.get();
    -
    -// log the Name property
    -console.log(store2.Name);
    -
    -// call another TermStore method on the same object
    -await store2.addLanguage(1031);
    -
    - - -

    Term store methods and properties

    -

    get

    -

    Loads the data for this term store

    -
    import { taxonomy, ITermStore } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = await taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==").get();
    -
    - - -

    getTermSetsByName

    -

    Gets the collection of term sets with a matching name

    -
    import { taxonomy, ITermSets } from "@pnp/sp-taxonomy";
    -
    -const sets: ITermSets = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==").getTermSetsByName("My Set", 1033);
    -
    - - -

    getTermSetById

    -

    Gets the term set with a matching id

    -
    import { taxonomy, ITermStore, ITermSet } from "@pnp/sp-taxonomy";
    -
    -// note that you can also use instances if you wanted to conduct multiple operations on a single store
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -const set: ITermSet = store.getTermSetById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -// we will handle normalizing guids for you as well :)
    -const set2: ITermSet = store.getTermSetById("{a63aefc9-359d-42b7-a0d2-cb1809acd260}");
    -
    - - -

    getTermById

    -

    Gets a term by id

    -
    import { taxonomy, ITermStore, ITerm, ITermData } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const term: ITerm = store.getTermById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -const termWithData: ITerm & ITermData = await term.get();
    -
    - - -

    getTermsById

    -

    Added in 1.2.6

    -
    import { taxonomy, ITermStore, ITerms, ITermData } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const terms: ITerms = store.getTermsById("0ba6845c-1468-4ec5-a5a8-718f1fb05431", "0ba6845c-1468-4ec5-a5a8-718f1fb05432");
    -const termWithData: (ITerm & ITermData)[] = await term.get();
    -
    - - -

    getTermGroupById

    -

    Gets a term group by id

    -
    import { taxonomy, ITermStore, ITermGroup } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const group: ITermGroup = store.getTermGroupById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    - - -

    getTerms

    -

    Gets terms that match the provided criteria. Please see this article for details on valid querys.

    -
    import { taxonomy, ITermStore, ILabelMatchInfo, ITerm, ITermData } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const terms: ITerms = store.getTerms({
    -                                TermLabel: "test label",
    -                                TrimUnavailable: true,
    -                            });
    -
    -// load the data based on the above query
    -const termsWithData: (ITerm & ITermData)[] = terms.get();
    -
    -// select works here too :)
    -const termsWithData2: (ITerm & ITermData)[] = terms.select("Name").get();
    -
    - - -

    addLanguage

    -

    Adds a language to the term store by LCID

    -
    import { taxonomy, ITermStore } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -await store.addLanguage(1031);
    -
    - - -

    addGroup

    -

    Adds a term group to the term store

    -
    import { taxonomy, ITermStore } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const group: ITermGroup & ITermGroupData = await store.addGroup("My Group Name");
    -
    -// you can optionally specify the guid of the group, if you don't we just create a new guid for you
    -const groups: ITermGroup & ITermGroupData = await store.addGroup("My Group Name", "0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    - - -

    commitAll

    -

    Commits all updates to the database that have occurred since the last commit or rollback.

    -
    import { taxonomy, ITermStore } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -await store.commitAll();
    -
    - - -

    deleteLanguage

    -

    Delete a working language from the TermStore

    -
    import { taxonomy, ITermStore } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -await store.deleteLanguage(1031);
    -
    - - -

    rollbackAll

    -

    Discards all updates that have occurred since the last commit or rollback. It is unlikely you will need to call this method through this library due to how things are structured.

    -
    import { taxonomy, ITermStore } from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -await store.rollbackAll();
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp-taxonomy/docs/terms/index.html b/docs/v3/v1/sp-taxonomy/docs/terms/index.html deleted file mode 100644 index 8c079d639..000000000 --- a/docs/v3/v1/sp-taxonomy/docs/terms/index.html +++ /dev/null @@ -1,2036 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Terms - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp-taxonomy/terms

    -

    Terms are the individual entries with a term set.

    -

    Load Terms

    -

    You can load a collection of terms through a term set or term store.

    -
    import {
    -    taxonomy,
    -    ITermStore,
    -    ITerms,
    -    ILabelMatchInfo,
    -    ITerm,
    -    ITermData
    -} from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = await taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -const labelMatchInfo: ILabelMatchInfo = {
    -    TermLabel: "My Label",
    -    TrimUnavailable: true,
    -};
    -
    -const terms: ITerms = store.getTerms(labelMatchInfo);
    -
    -// get term instances merged with data
    -const terms2: (ITermData & ITerm)[] = await store.getTerms(labelMatchInfo).get();
    -
    -const terms3: ITerms = store.getTermSetById("0ba6845c-1468-4ec5-a5a8-718f1fb05431").terms;
    -
    -// get terms merged with data from a term set
    -const terms4: (ITerm & ITermData)[] = store.getTermSetById("0ba6845c-1468-4ec5-a5a8-718f1fb05431").terms.get();
    -
    - - -

    Load Single Term

    -

    You can get a single term a variety of ways as shown below. The "best" way will be determined by what information is available to do the lookup but ultimately will result in the same end product.

    -
    import {
    -    taxonomy,
    -    ITermStore,
    -    ITerms,
    -    ILabelMatchInfo,
    -    ITerm,
    -    ITermData
    -} from "@pnp/sp-taxonomy";
    -
    -const store: ITermStore = await taxonomy.termStores.getByName("Taxonomy_v5o/SbcTE2cegwO2dtAN9l==");
    -
    -// get a single term by id
    -const term: ITerm = store.getTermById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    -// get single get merged with data
    -const term2: ITerm = store.getTermById("0ba6845c-1468-4ec5-a5a8-718f1fb05431").get();
    -
    -// use select to choose which fields to return
    -const term3: ITerm = store.getTermById("0ba6845c-1468-4ec5-a5a8-718f1fb05431").select("Name").get();
    -
    -// get a term from a term set
    -const term4: ITerm = store.getTermSetById("0ba6845c-1468-4ec5-a5a8-718f1fb05431").getTermById("0ba6845c-1468-4ec5-a5a8-718f1fb05431");
    -
    - - -

    Term methods and properties

    -

    labels

    -

    Accesses the labels collection for this term

    -
    import { taxonomy, ITermStore, ITerm, ILabels } from "@pnp/sp-taxonomy";
    -
    -const term: ITerm = <from one of the above methods>;
    -
    -const labels: ILabels = term.labels;
    -
    -// labels merged with data
    -const labelsWithData = term.labels.get();
    -
    - - -

    createLabel

    -

    Creates a new label for this Term

    -
    import { taxonomy, ITermStore, ITerm, ILabelData, ILabel } from "@pnp/sp-taxonomy";
    -
    -const term: ITerm = <from one of the above methods>;
    -
    -const label: ILabelData & ILabel = term.createLabel("label text", 1031);
    -
    -// optionally specify this is the default label
    -const label2: ILabelData & ILabel = term.createLabel("label text", 1031, true);
    -
    - - -

    deprecate

    -

    Sets the deprecation flag on a term

    -
    import { ITerm } from "@pnp/sp-taxonomy";
    -
    -const term: ITerm = <from one of the above methods>;
    -
    -await term.deprecate(true);
    -
    - - -

    get

    -

    Loads the term data

    -
    import { ITerm, ITermData } from "@pnp/sp-taxonomy";
    -
    -const term: ITerm = <from one of the above methods>;
    -
    -// load term instance merged with data
    -const term2: ITerm & ITermData = await term.get();
    -
    - - -

    getDescription

    -

    Sets the description

    -
    import { ITerm } from "@pnp/sp-taxonomy";
    -
    -const term: ITerm = <from one of the above methods>;
    -
    -// load term instance merged with data
    -const description = await term.getDescription(1031);
    -
    - - -

    setDescription

    -

    Sets the description

    -
    import { ITerm } from "@pnp/sp-taxonomy";
    -
    -const term: ITerm = <from one of the above methods>;
    -
    -// load term instance merged with data
    -await term.setDescription("the description", 1031);
    -
    - - -

    setLocalCustomProperty

    -

    Sets a custom property on this term

    -
    import { ITerm } from "@pnp/sp-taxonomy";
    -
    -const term: ITerm = <from one of the above methods>;
    -
    -// load term instance merged with data
    -await term.setLocalCustomProperty("name", "value");
    -
    - - -

    addTerm

    -

    Added in 1.2.8

    -

    Adds a child term to an existing term instance.

    -
    import { ITerm } from "@pnp/sp-taxonomy";
    -
    -const parentTerm: ITerm = <from one of the above methods>;
    -
    -await parentTerm.addTerm("child 1", 1033);
    -
    -await parentTerm.addTerm("child 2", 1033);
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp-taxonomy/docs/utilities/index.html b/docs/v3/v1/sp-taxonomy/docs/utilities/index.html deleted file mode 100644 index fdf0ad1e7..000000000 --- a/docs/v3/v1/sp-taxonomy/docs/utilities/index.html +++ /dev/null @@ -1,1783 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Utilities - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp-taxonomy/utilities

    -

    These are a collection of helper methods you may find useful.

    -

    setItemMetaDataField

    -

    Allows you to easily set the value of a metadata field in a list item.

    -
    import { sp } from "@pnp/sp";
    -import { taxonomy, setItemMetaDataField } from "@pnp/sp-taxonomy";
    -
    -// create a new item, or load an existing
    -const itemResult = await sp.web.lists.getByTitle("TaxonomyList").items.add({
    -    Title: "My Title",
    -});
    -
    -// get a term
    -const term = await taxonomy.getDefaultSiteCollectionTermStore()
    -    .getTermById("99992696-1111-1111-1111-15e65b221111").get();
    -
    -setItemMetaDataField(itemResult.item, "MetaDataFieldName", term);
    -
    - - -

    setItemMetaDataMultiField

    -

    Allows you to easily set the value of a multi-value metadata field in a list item.

    -
    import { sp } from "@pnp/sp";
    -import { taxonomy, setItemMetaDataMultiField } from "@pnp/sp-taxonomy";
    -
    -// create a new item, or load an existing
    -const itemResult = await sp.web.lists.getByTitle("TaxonomyList").items.add({
    -    Title: "My Title",
    -});
    -
    -// get a term
    -const term = await taxonomy.getDefaultSiteCollectionTermStore()
    -    .getTermById("99992696-1111-1111-1111-15e65b221111").get();
    -
    -// get another term
    -const term2 = await taxonomy.getDefaultSiteCollectionTermStore()
    -    .getTermById("99992696-1111-1111-1111-15e65b221112").get();
    -
    -// get yet another term
    -const term3 = await taxonomy.getDefaultSiteCollectionTermStore()
    -    .getTermById("99992696-1111-1111-1111-15e65b221113").get();
    -
    -setItemMetaDataMultiField(
    -    itemResult.item,
    -    "MultiValueMetaDataFieldName",
    -    term,
    -    term2,
    -    term3
    -);
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/alias-parameters/index.html b/docs/v3/v1/sp/docs/alias-parameters/index.html deleted file mode 100644 index 068fd6be1..000000000 --- a/docs/v3/v1/sp/docs/alias-parameters/index.html +++ /dev/null @@ -1,1830 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Alias Parameters - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp - Aliased Parameters

    -

    Within the @pnp/sp api you can alias any of the parameters so they will be written into the querystring. This is most helpful if you are hitting up against the -url length limits when working with files and folders.

    -

    To alias a parameter you include the label name, a separator ("::") and the value in the string. You also need to prepend a "!" to the string to trigger the replacement. You can see this below, as well as the string that will be generated. Labels must start with a "@" followed by a letter. It is also your responsibility to ensure that the aliases you supply do not conflict, for example if you use "@p1" you should use "@p2" for a second parameter alias in the same query.

    -

    Construct a parameter alias

    -

    Pattern: !@{label name}::{value}

    -

    Example: "!@p1::\sites\dev" or "!@p2::\text.txt"

    -

    Example without aliasing

    -
    import { sp } from "@pnp/sp";
    -// still works as expected, no aliasing
    -const query = sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/").files.select("Title").top(3);
    -
    -console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files
    -console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files?$select=Title&$top=3
    -
    -query.get().then(r => {
    -
    -    console.log(r);
    -});
    -
    - - -

    Example with aliasing

    -
    import { sp } from "@pnp/sp";
    -// same query with aliasing
    -const query = sp.web.getFolderByServerRelativeUrl("!@p1::/sites/dev/Shared Documents/").files.select("Title").top(3);
    -
    -console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files
    -console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3
    -
    -query.get().then(r => {
    -
    -    console.log(r);
    -});
    -
    - - -

    Example with aliasing and batching

    -

    Aliasing is supported with batching as well:

    -
    import { sp } from "@pnp/sp";
    -// same query with aliasing and batching
    -const batch = sp.web.createBatch();
    -
    -const query = sp.web.getFolderByServerRelativeUrl("!@p1::/sites/dev/Shared Documents/").files.select("Title").top(3);
    -
    -console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files
    -console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3
    -
    -query.inBatch(batch).get().then(r => {
    -
    -    console.log(r);
    -});
    -
    -batch.execute();
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/alm/index.html b/docs/v3/v1/sp/docs/alm/index.html deleted file mode 100644 index d5e73d7ef..000000000 --- a/docs/v3/v1/sp/docs/alm/index.html +++ /dev/null @@ -1,1920 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ALM api - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/appcatalog

    -

    The ALM api allows you to manage app installations both in the tenant app catalog and individual site app catalogs. Some of the methods are still in beta and as such may change in the future. This article outlines how to call this api using @pnp/sp. Remember all these actions are bound by permissions so it is likely most users will not have the rights to perform these ALM actions.

    -

    Understanding the App Catalog Heirarchy

    -

    Before you begin provisioning applications it is important to understand the relationship between a local web catalog and the tenant app catalog. Some of the methods described below only work within the context of the tenant app catalog web, such as adding an app to the catalog and the app actions retract, remove, and deploy. You can install, uninstall, and upgrade an app in any web. Read more in the official documentation.

    -

    Reference an App Catalog

    -

    There are several ways using @pnp/sp to get a reference to an app catalog. These methods are to provide you the greatest amount of flexibility in gaining access to the app catalog. Ultimately each method produces an AppCatalog instance differentiated only by the web to which it points.

    -
    import { sp } from "@pnp/sp";
    -// get the curren't context web's app catalog
    -const catalog = sp.web.getAppCatalog();
    -
    -// you can also chain off the app catalog
    -pnp.sp.web.getAppCatalog().get().then(console.log);
    -
    - - -
    import { sp } from "@pnp/sp";
    -// you can get the tenant app catalog (or any app catalog) by passing in a url
    -
    -// get the tenant app catalog
    -const tenantCatalog = sp.web.getAppCatalog("https://mytenant.sharepoint.com/sites/appcatalog");
    -
    -// get a different app catalog
    -const catalog = sp.web.getAppCatalog("https://mytenant.sharepoint.com/sites/anothersite");
    -
    - - -
    // alternatively you can create a new app catalog instance directly by importing the AppCatalog class
    -import { AppCatalog } from "@pnp/sp";
    -
    -const catalog = new AppCatalog("https://mytenant.sharepoint.com/sites/dev");
    -
    - - -
    // and finally you can combine use of the Web and AppCatalog classes to create an AppCatalog instance from an existing Web
    -import { Web, AppCatalog } from "@pnp/sp";
    -
    -const web = new Web("https://mytenant.sharepoint.com/sites/dev");
    -const catalog = new AppCatalog(web);
    -
    - - -

    The following examples make use of a variable "catalog" which is assumed to represent an AppCatalog instance obtained using one of the above methods, supporting code is omitted for brevity.

    -

    List Available Apps

    -

    The AppCatalog is itself a queryable collection so you can query this object directly to get a list of available apps. Also, the odata operators work on the catalog to sort, filter, and select.

    -
    // get available apps
    -catalog.get().then(console.log);
    -
    -// get available apps selecting two fields
    -catalog.select("Title", "Deployed").get().then(console.log);
    -
    - - -

    Add an App

    -

    This action must be performed in the context of the tenant app catalog

    -
    // this represents the file bytes of the app package file
    -const blob = new Blob();
    -
    -// there is an optional third argument to control overwriting existing files
    -catalog.add("myapp.app", blob).then(r => {
    -
    -    // this is at its core a file add operation so you have access to the response data as well
    -    // as a File isntance representing the created file
    -
    -    console.log(JSON.stringify(r.data, null, 4));
    -
    -    // all file operations are available
    -    r.file.select("Name").get().then(console.log);
    -});
    -
    - - -

    Get an App

    -

    You can get the details of a single app by GUID id. This is also the branch point to perform specific app actions

    -
    catalog.getAppById("5137dff1-0b79-4ebc-8af4-ca01f7bd393c").get().then(console.log);
    -
    - - -

    Perform app actions

    -

    Remember: retract, deploy, and remove only work in the context of the tenant app catalog web. All of these methods return void and you can monitor success using then and catch.

    -
    // deploy
    -catalog.getAppById("5137dff1-0b79-4ebc-8af4-ca01f7bd393c").deploy().then(console.log).catch(console.error);
    -
    -// retract
    -catalog.getAppById("5137dff1-0b79-4ebc-8af4-ca01f7bd393c").retract().then(console.log).catch(console.error);
    -
    -// install
    -catalog.getAppById("5137dff1-0b79-4ebc-8af4-ca01f7bd393c").install().then(console.log).catch(console.error);
    -
    -// uninstall
    -catalog.getAppById("5137dff1-0b79-4ebc-8af4-ca01f7bd393c").uninstall().then(console.log).catch(console.error);
    -
    -// upgrade
    -catalog.getAppById("5137dff1-0b79-4ebc-8af4-ca01f7bd393c").upgrade().then(console.log).catch(console.error);
    -
    -// remove
    -catalog.getAppById("5137dff1-0b79-4ebc-8af4-ca01f7bd393c").remove().then(console.log).catch(console.error);
    -
    - - -

    Notes

    -
      -
    • The app catalog is just a document library under the hood, so you can also perform non-ALM actions on the library if needed. But you should be aware of possible side-effects to the ALM life-cycle when doing so.
    • -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/attachments/index.html b/docs/v3/v1/sp/docs/attachments/index.html deleted file mode 100644 index 5067d7c72..000000000 --- a/docs/v3/v1/sp/docs/attachments/index.html +++ /dev/null @@ -1,1998 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Attachments - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - -

    @pnp/sp/attachments

    -

    The ability to attach file to list items allows users to track documents outside of a document library. You can use the PnP JS Core library to work with attachments as outlined below.

    -

    Get attachments

    -
    import { sp } from "@pnp/sp";
    -
    -let item = sp.web.lists.getByTitle("MyList").items.getById(1);
    -
    -// get all the attachments
    -item.attachmentFiles.get().then(v => {
    -
    -    console.log(v);
    -});
    -
    -// get a single file by file name
    -item.attachmentFiles.getByName("file.txt").get().then(v => {
    -
    -    console.log(v);
    -});
    -
    -// select specific properties using odata operators
    -item.attachmentFiles.select("ServerRelativeUrl").get().then(v => {
    -
    -    console.log(v);
    -});
    -
    - - -

    Add an Attachment

    -

    You can add an attachment to a list item using the add method. This method takes either a string, Blob, or ArrayBuffer.

    -
    import { sp } from "@pnp/sp";
    -
    -let item = sp.web.lists.getByTitle("MyList").items.getById(1);
    -
    -item.attachmentFiles.add("file2.txt", "Here is my content").then(v => {
    -
    -    console.log(v);
    -});
    -
    - - -

    Add Multiple

    -

    This method allows you to pass an array of AttachmentFileInfo plain objects that will be added one at a time as attachments. Essentially automating the promise chaining.

    -
    const list = sp.web.lists.getByTitle("MyList");
    -
    -var fileInfos: AttachmentFileInfo[] = [];
    -
    -fileInfos.push({
    -    name: "My file name 1",
    -    content: "string, blob, or array"
    -});
    -
    -fileInfos.push({
    -    name: "My file name 2",
    -    content: "string, blob, or array"
    -});
    -
    -list.items.getById(2).attachmentFiles.addMultiple(fileInfos).then(r => {
    -
    -    console.log(r);
    -});
    -
    - - -

    Delete Multiple

    -
    const list = sp.web.lists.getByTitle("MyList");
    -
    -list.items.getById(2).attachmentFiles.deleteMultiple("1.txt","2.txt").then(r => {
    -    console.log(r);
    -});
    -
    - - -

    Read Attachment Content

    -

    You can read the content of an attachment as a string, Blob, ArrayBuffer, or json using the methods supplied.

    -
    import { sp } from "@pnp/sp";
    -
    -let item = sp.web.lists.getByTitle("MyList").items.getById(1);
    -
    -item.attachmentFiles.getByName("file.txt").getText().then(v => {
    -
    -    console.log(v);
    -});
    -
    -// use this in the browser, does not work in nodejs
    -item.attachmentFiles.getByName("file.mp4").getBlob().then(v => {
    -
    -    console.log(v);
    -});
    -
    -// use this in nodejs
    -item.attachmentFiles.getByName("file.mp4").getBuffer().then(v => {
    -
    -    console.log(v);
    -});
    -
    -// file must be valid json
    -item.attachmentFiles.getByName("file.json").getJSON().then(v => {
    -
    -    console.log(v);
    -});
    -
    - - -

    Update Attachment Content

    -

    You can also update the content of an attachment. This API is limited compared to the full file API - so if you need to upload large files consider using a document library.

    -
    import { sp } from "@pnp/sp";
    -
    -let item = sp.web.lists.getByTitle("MyList").items.getById(1);
    -
    -item.attachmentFiles.getByName("file2.txt").setContent("My new content!!!").then(v => {
    -
    -    console.log(v);
    -});
    -
    - - -

    Delete Attachment

    -
    import { sp } from "@pnp/sp";
    -
    -let item = sp.web.lists.getByTitle("MyList").items.getById(1);
    -
    -item.attachmentFiles.getByName("file2.txt").delete().then(v => {
    -
    -    console.log(v);
    -});
    -
    - - -

    Recycle Attachment

    -

    Added in 1.2.4

    -

    Delete the attachment and send it to recycle bin

    -
    import { sp } from "@pnp/sp";
    -
    -let item = sp.web.lists.getByTitle("MyList").items.getById(1);
    -
    -item.attachmentFiles.getByName("file2.txt").recycle().then(v => {
    -
    -    console.log(v);
    -});
    -
    - - -

    Recycle Multiple Attachments

    -

    Added in 1.2.4

    -

    Delete multiple attachments and send them to recycle bin

    -
    import { sp } from "@pnp/sp";
    -
    -const list = sp.web.lists.getByTitle("MyList");
    -
    -list.items.getById(2).attachmentFiles.recycleMultiple("1.txt","2.txt").then(r => {
    -    console.log(r);
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/client-side-pages/index.html b/docs/v3/v1/sp/docs/client-side-pages/index.html deleted file mode 100644 index 31cb7faa8..000000000 --- a/docs/v3/v1/sp/docs/client-side-pages/index.html +++ /dev/null @@ -1,2031 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Client-side Pages - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - -

    @pnp/sp/clientsidepages

    -

    The ability to manage client-side pages is a capability introduced in version 1.0.2 of @pnp/sp. Through the methods described -you can add and edit "modern" pages in SharePoint sites.

    -

    Add Client-side page

    -

    Using the addClientSidePage you can add a new client side page to a site, specifying the filename.

    -
    import { sp } from "@pnp/sp";
    -
    -const page = await sp.web.addClientSidePage(`file-name`);
    -
    -// OR
    -
    -const page = await sp.web.addClientSidePage(`file-name`, `Page Display Title`);
    -
    - - -

    Added in 1.0.5 you can also add a client side page using the list path. This gets around potential language issues with list title. You must specify the list path when calling this method in addition to the new page's filename.

    -
    import { sp } from "@pnp/sp";
    -
    -const page = await sp.web.addClientSidePageByPath(`file-name`, "/sites/dev/SitePages");
    -
    - - -

    Load Client-side page

    -

    You can also load an existing page based on the file representing that page. Note that the static fromFile returns a promise which -resolves so the loaded page. Here we are showing use of the getFileByServerRelativeUrl method to get the File instance, but any of the ways -of getting a File instance will work. Also note we are passing the File instance, not the file content.

    -
    import { 
    -    sp,
    -    ClientSidePage,
    -} from "@pnp/sp";
    -
    -const page = await ClientSidePage.fromFile(sp.web.getFileByServerRelativeUrl("/sites/dev/SitePages/ExistingFile.aspx"));
    -
    - - -

    The remaining examples below reference a variable "page" which is assumed to be a ClientSidePage instance loaded through one of the above means.

    -

    Add Controls

    -

    A client-side page is made up of sections, which have columns, which contain controls. A new page will have none of these and an existing page may have -any combination of these. There are a few rules to understand how sections and columns layout on a page for display. A section is a horizontal piece of -a page that extends 100% of the page width. A page with multiple sections will stack these sections based on the section's order property - a 1 based index.

    -

    Within a section you can have one or more columns. Each column is ordered left to right based on the column's order property. The width of each column is -controlled by the factor property whose value is one of 0, 2, 4, 6, 8, 10, or 12. The columns in a section should have factors that add up to 12. Meaning -if you wanted to have two equal columns you can set a factor of 6 for each. A page can have empty columns.

    -
    import { 
    -    sp, 
    -    ClientSideText, 
    -} from "@pnp/sp";
    -
    -// this code adds a section, and then adds a control to that section. The control is added to the section's defaultColumn, and if there are no columns a single
    -// column of factor 12 is created as a default. Here we add the ClientSideText part
    -page.addSection().addControl(new ClientSideText("@pnp/sp is a great library!"));
    -
    -// here we add a section, add two columns, and add a text control to the second section so it will appear on the right of the page
    -// add and get a reference to a new section
    -const section = page.addSection();
    -
    -// add a column of factor 6
    -section.addColumn(6);
    -
    -// add and get a reference to a new column of factor 6
    -const column = section.addColumn(6);
    -
    -// add a text control to the second new column
    -column.addControl(new ClientSideText("Be sure to check out the @pnp docs at https://pnp.github.io/pnpjs/"));
    -
    -// we need to save our content changes
    -await page.save();
    -
    - - -

    Add Client-side Web Parts

    -

    Beyond the text control above you can also add any of the available client-side web parts in a given site. To find out what web parts are available you -first call the web's getClientSideWebParts method. Once you have a list of parts you need to find the defintion you want to use, here we get the Embed web part -whose's id is "490d7c76-1824-45b2-9de3-676421c997fa" (at least in one farm, your mmv).

    -
    import {
    -    sp,
    -    ClientSideWebpart,
    -    ClientSideWebpartPropertyTypes,
    -} from "@pnp/sp";
    -
    -// this will be a ClientSidePageComponent array
    -// this can be cached on the client in production scenarios
    -const partDefs = await sp.web.getClientSideWebParts();
    -
    -// find the definition we want, here by id
    -const partDef = partDefs.filter(c => c.Id === "490d7c76-1824-45b2-9de3-676421c997fa");
    -
    -// optionally ensure you found the def
    -if (partDef.length < 1) {
    -    // we didn't find it so we throw an error
    -    throw new Error("Could not find the web part");
    -}
    -
    -// create a ClientWebPart instance from the definition
    -const part = ClientSideWebpart.fromComponentDef(partDef[0]);
    -
    -// set the properties on the web part. Here we have imported the ClientSideWebpartPropertyTypes module and can use that to type
    -// the available settings object. You can use your own types or help us out and add some typings to the module :).
    -// here for the embed web part we only have to supply an embedCode - in this case a youtube video.
    -part.setProperties<ClientSideWebpartPropertyTypes.Embed>({
    -    embedCode: "https://www.youtube.com/watch?v=IWQFZ7Lx-rg",
    -});
    -
    -// we add that part to a new section
    -page.addSection().addControl(part);
    -
    -// save our content changes back to the server
    -await page.save();
    -
    - - -

    Find Controls

    -

    Added in 1.0.3

    -

    You can use the either of the two available method to locate controls within a page. These method search through all sections, columns, and controls returning the first instance that meets the supplied criteria.

    -
    import { ClientSideWebPart } from "@pnp/sp";
    -
    -// find a control by instance id
    -const control1 = page.findControlById("b99bfccc-164e-4d3d-9b96-da48db62eb78");
    -
    -// type the returned control
    -const control2 = page.findControlById<ClientSideWebPart>("c99bfccc-164e-4d3d-9b96-da48db62eb78");
    -const control3 = page.findControlById<ClientSideText>("a99bfccc-164e-4d3d-9b96-da48db62eb78");
    -
    -// use any predicate to find a control
    -const control4 = page2.findControl<ClientSideWebpart>((c: CanvasControl) => {
    -
    -    // any logic you wish can be used on the control here
    -    // return true to return that control
    -    return c.order > 3;
    -});
    -
    - - -

    Control Comments

    -

    You can choose to enable or disable comments on a page using these methods

    -
    // indicates if comments are disabled, not valid until the page is loaded (Added in _1.0.3_)
    -page.commentsDisabled
    -
    -// enable comments
    -await page.enableComments();
    -
    -// disable comments
    -await page.disableComments();
    -
    - - -

    Like/Unlike Client-side page, get like information about page

    -

    Added in 1.2.4

    -

    You can like or unlike a modern page. You can also get information about the likes (i.e like Count and which users liked the page)

    -
    // Like a Client-side page (Added in _1.2.4_)
    -await page.like();
    -
    -// Unlike a Client-side page
    -await page.unlike();
    -
    -// Get liked by information such as like count and user's who liked the page
    -await page.getLikedByInformation();
    -
    - - -

    Sample

    -

    The below sample shows the process to add a Yammer feed webpart to the page. The properties required as well as the data version are found by adding the part using the UI and reviewing the values. Some or all of these may be discoverable using Yammer APIs. An identical process can be used to add web parts of any type by adjusting the definition, data version, and properties appropriately.

    -
    // get webpart defs
    -const defs = await sp.web.getClientSideWebParts();
    -
    -// this is the id of the definition in my farm
    -const yammerPartDef = defs.filter(d => d.Id === "31e9537e-f9dc-40a4-8834-0e3b7df418bc")[0];
    -
    -// page file
    -const file = sp.web.getFileByServerRelativePath("/sites/dev/SitePages/Testing_kVKF.aspx");
    -
    -// create page instance
    -const page = await ClientSidePage.fromFile(file);
    -
    -// create part instance from definition
    -const part = ClientSideWebpart.fromComponentDef(yammerPartDef);
    -
    -// update data version
    -part.dataVersion = "1.5";
    -
    -// set the properties required
    -part.setProperties({
    -    feedType: 0,
    -    isSuiteConnected: false,
    -    mode: 2,
    -    networkId: 9999999,
    -    yammerEmbedContainerHeight: 400,
    -    yammerFeedURL: "",
    -    yammerGroupId: -1,
    -    yammerGroupMugshotUrl: "https://mug0.assets-yammer.com/mugshot/images/{width}x{height}/all_company.png",
    -    yammerGroupName: "All Company",
    -    yammerGroupUrl: "https://www.yammer.com/{tenant}/#/threads/company?type=general",
    -});
    -
    -// add to the section/column you want
    -page.sections[0].addControl(part);
    -
    -// persist changes
    -page.save();
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/comments-likes/index.html b/docs/v3/v1/sp/docs/comments-likes/index.html deleted file mode 100644 index 9c41ad0b4..000000000 --- a/docs/v3/v1/sp/docs/comments-likes/index.html +++ /dev/null @@ -1,1958 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Comments and Likes - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/comments and likes

    -

    Likes and comments in the context of modern sites are based on list items, meaning the operations branch from the Item class. To load an item you can refer to the guidance in the items article. If you want to set the likes or comments on a modern page and don't know the item id but do know the url you can first load the file and then use the getItem method to get an item instance:

    -

    These APIs are currently in BETA and are subject to change or may not work on all tenants.

    -
    import { sp } from "@pnp/sp";
    -
    -const item = await sp.web.getFileByServerRelativeUrl("/sites/dev/SitePages/Test_8q5L.aspx").getItem();
    -
    -// as an example, or any of the below options
    -await item.like();
    -
    - - -

    The below examples use a variable named "item" which is taken to represent an instance of the Item class.

    -

    Comments

    -

    Get Comments

    -
    const comments = await item.comments.get();
    -
    - - -

    You can also get the comments merged with instances of the Comment class to immediately start accessing the properties and methods:

    -
    import { spODataEntityArray, Comment, CommentData } from "@pnp/sp";
    -
    -const comments = await item.comments.get(spODataEntityArray<Comment, CommentData>(Comment));
    -
    -// these will be Comment instances in the array
    -comments[0].replies.add({ text: "#PnPjs is pretty ok!" });
    -
    -//load the top 20 replies and comments for an item including likedBy information
    -const comments = await item.comments.expand("replies", "likedBy", "replies/likedBy").top(20).get();
    -
    - - -

    Add Comment

    -
    // you can add a comment as a string
    -item.comments.add("string comment");
    -
    -// or you can add it as an object to include mentions
    -item.comments.add({ text: "comment from object property" });
    -
    - - -

    Delete a Comment

    -
    import { spODataEntityArray, Comment, CommentData } from "@pnp/sp";
    -
    -const comments = await item.comments.get(spODataEntityArray<Comment, CommentData>(Comment));
    -
    -// these will be Comment instances in the array
    -comments[0].delete()
    -
    - - -

    Like Comment

    -
    import { spODataEntityArray, Comment, CommentData } from "@pnp/sp";
    -
    -const comments = await item.comments.get(spODataEntityArray<Comment, CommentData>(Comment));
    -
    -// these will be Comment instances in the array
    -comments[0].like()
    -
    - - -

    Unlike Comment

    -
    import { spODataEntityArray, Comment, CommentData } from "@pnp/sp";
    -
    -const comments = await item.comments.get(spODataEntityArray<Comment, CommentData>(Comment));
    -
    -comments[0].unlike()
    -
    - - -

    Reply to a Comment

    -
    import { spODataEntityArray, Comment, CommentData } from "@pnp/sp";
    -
    -const comments = await item.comments.get(spODataEntityArray<Comment, CommentData>(Comment));
    -
    -const comment: Comment & CommentData = await comments[0].replies.add({ text: "#PnPjs is pretty ok!" });
    -
    - - -

    Load Replies to a Comment

    -
    import { spODataEntityArray, Comment, CommentData } from "@pnp/sp";
    -
    -const comments = await item.comments.get(spODataEntityArray<Comment, CommentData>(Comment));
    -
    -const replies = await comments[0].replies.get();
    -
    - - -

    Like

    -

    You can like items and comments on items. See above for how to like or unlike a comment. Below you can see how to like and unlike an items, as well as get the liked by data.

    -
    import { LikeData } from "@pnp/sp";
    -
    -// like an item
    -await item.like();
    -
    -// unlike an item
    -await item.unlike();
    -
    -// get the liked by information
    -const likedByData: LikeData[] = await item.getLikedBy();
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/content-types/index.html b/docs/v3/v1/sp/docs/content-types/index.html deleted file mode 100644 index e575d1461..000000000 --- a/docs/v3/v1/sp/docs/content-types/index.html +++ /dev/null @@ -1,1758 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Content Types - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/content types

    -

    Set Folder Unique Content Type Order

    -
    interface OrderData {
    -    ContentTypeOrder: { StringValue: string }[];
    -    UniqueContentTypeOrder?: { StringValue: string }[];
    -}
    -
    -const folder = sp.web.lists.getById("{list id guid}").rootFolder;
    -
    -// here you need to see if there are unique content type orders already or just the default
    -const existingOrders = await folder.select("ContentTypeOrder", "UniqueContentTypeOrder").get<OrderData>();
    -
    -const activeOrder = existingOrders.UniqueContentTypeOrder ? existingOrders.UniqueContentTypeOrder : existingOrders.ContentTypeOrder;
    -
    -// manipulate the order here however you want (I am just reversing the array as an example)
    -const newOrder = activeOrder.reverse();
    -
    -// update the content type order thusly:
    -await folder.update({
    -    UniqueContentTypeOrder: {
    -        __metadata: { type: "Collection(SP.ContentTypeId)" },
    -        results: newOrder,
    -    },
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/entity-merging/index.html b/docs/v3/v1/sp/docs/entity-merging/index.html deleted file mode 100644 index 59e7d4ca2..000000000 --- a/docs/v3/v1/sp/docs/entity-merging/index.html +++ /dev/null @@ -1,1838 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Entity Merging - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp - entity merging

    -

    Sometimes when we make a query entity's data we would like then to immediately run other commands on the returned entity. To have data returned as its represending type we make use of the spODataEntity and spODataEntityArray parsers. The below approach works for all instance types such as List, Web, Item, or Field as examples.

    -

    Request a single entity

    -

    If we are loading a single entity we use the spODataEntity method. Here we show loading a list item using the Item class and a simple get query.

    -
    import { sp, spODataEntity, Item } from "@pnp/sp";
    -
    -// interface defining the returned properites
    -interface MyProps {
    -    Id: number;
    -}
    -
    -try {
    -
    -    // get a list item laoded with data and merged into an instance of Item
    -    const item = await sp.web.lists.getByTitle("ListTitle").items.getById(1).get(spODataEntity<Item, MyProps>(Item));
    -
    -    // log the item id, all properties specified in MyProps will be type checked
    -    Logger.write(`Item id: ${item.Id}`);
    -
    -    // now we can call update because we have an instance of the Item type to work with as well
    -    await item.update({
    -        Title: "New title.",
    -    });
    -
    -} catch (e) {
    -    Logger.error(e);
    -}
    -
    - - -

    Request a collection

    -

    The same pattern works when requesting a collection of objects with the exception of using the spODataEntityArray method.

    -
    import { sp, spODataEntityArray, Item } from "@pnp/sp";
    -
    -// interface defining the returned properites
    -interface MyProps {
    -    Id: number;
    -    Title: string;
    -}
    -
    -try {
    -
    -    // get a list item laoded with data and merged into an instance of Item
    -    const items = await sp.web.lists.getByTitle("ListTitle").items.select("Id", "Title").get(spODataEntityArray<Item, MyProps>(Item));
    -
    -    Logger.write(`Item id: ${items.length}`);
    -
    -    Logger.write(`Item id: ${items[0].Title}`);
    -
    -    // now we can call update because we have an instance of the Item type to work with as well
    -    await items[0].update({
    -        Title: "New title.",
    -    });
    -
    -} catch (e) {
    -
    -    Logger.error(e);
    -}
    -
    - - -

    Use with Item getPaged

    -

    Added in 1.3.4

    -

    Starting with 1.3.4 you can now include entity merging in the getPaged command as shown below. This approach will work with any objects matching the required factory pattern.

    -
    // create Item instances with the defined property Title
    -const items = await sp.web.lists.getByTitle("BigList").items.select("Title").getPaged(spODataEntityArray<Item, { Title: string }>(Item));
    -
    -console.log(items.results.length);
    -
    -// now invoke methods on the Item object
    -const perms = await items.results[0].getCurrentUserEffectivePermissions();
    -
    -console.log(JSON.stringify(perms, null, 2));
    -
    -// you can also type the result slightly differently if you prefer this, but the results are the same functionally.
    -const items2 = await sp.web.lists.getByTitle("BigList").items.select("Title").getPaged<(Item & { Title: string })[]>(spODataEntityArray(Item));
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/features/index.html b/docs/v3/v1/sp/docs/features/index.html deleted file mode 100644 index 2be7fc17c..000000000 --- a/docs/v3/v1/sp/docs/features/index.html +++ /dev/null @@ -1,1824 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Features - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/features

    -

    Features are used by SharePoint to package a set of functionality and either enable (activate) or disable (deactivate) that functionality based on requirements for a specific site. You can manage feature activation using the library as shown below. Note that the features collection only contains active features.

    -

    List all Features

    -
    import { sp } from "@pnp/sp";
    -
    -let web = sp.web;
    -
    -// get all the active features
    -web.features.get().then(f => {
    -
    -    console.log(f);
    -});
    -
    -// select properties using odata operators
    -web.features.select("DisplayName", "DefinitionId").get().then(f => {
    -
    -    console.log(f);
    -});
    -
    -// get a particular feature by id
    -web.features.getById("87294c72-f260-42f3-a41b-981a2ffce37a").select("DisplayName", "DefinitionId").get().then(f => {
    -
    -    console.log(f);
    -});
    -
    -// get features using odata operators
    -web.features.filter("DisplayName eq 'MDSFeature'").get().then(f => {
    -
    -    console.log(f);
    -});
    -
    - - -

    Activate a Feature

    -

    To activate a feature you must know the feature id. You can optionally force activation - if you aren't sure don't use force.

    -
    import { sp } from "@pnp/sp";
    -
    -let web = sp.web;
    -
    -// activate the minimum download strategy feature
    -web.features.add("87294c72-f260-42f3-a41b-981a2ffce37a").then(f => {
    -
    -    console.log(f);
    -});
    -
    - - -

    Deactivate a Feature

    -
    import { sp } from "@pnp/sp";
    -
    -let web = sp.web;
    -
    -web.features.remove("87294c72-f260-42f3-a41b-981a2ffce37a").then(f => {
    -
    -    console.log(f);
    -});
    -
    -// you can also deactivate a feature but going through the collection's remove method is faster
    -web.features.getById("87294c72-f260-42f3-a41b-981a2ffce37a").deactivate().then(f => {
    -
    -    console.log(f);
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/fields/index.html b/docs/v3/v1/sp/docs/fields/index.html deleted file mode 100644 index f038259e4..000000000 --- a/docs/v3/v1/sp/docs/fields/index.html +++ /dev/null @@ -1,2031 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Fields - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/fields

    -

    Fields allow you to store typed information within a SharePoint list. There are many types of fields and the library seeks to simplify working with the most common types. Fields exist in both site collections (site columns) or lists (list columns) and you can add/modify/delete them at either of these levels.

    -

    Get Fields

    -
    import { sp } from "@pnp/sp";
    -
    -let web = sp.web;
    -
    -// get all the fields in a web
    -web.fields.get().then(f => {
    -
    -    console.log(f);
    -});
    -
    -// you can use odata operators on the fields collection
    -web.fields.select("Title", "InternalName", "TypeAsString").top(10).orderBy("Id").get().then(f => {
    -
    -    console.log(f);
    -});
    -
    -// get all the available fields in a web (includes parent web's fields)
    -web.availablefields.get().then(f => {
    -
    -    console.log(f);
    -});
    -
    -// get the fields in a list
    -web.lists.getByTitle("MyList").fields.get().then(f => {
    -
    -    console.log(f);
    -});
    -
    -// you can also get individual fields using getById, getByTitle, or getByInternalNameOrTitle
    -web.fields.getById("dee9c205-2537-44d6-94e2-7c957e6ebe6e").get().then(f => {
    -
    -    console.log(f);
    -});
    -
    -web.fields.getByTitle("MyField4").get().then(f => {
    -
    -    console.log(f);
    -});
    -
    -web.fields.getByInternalNameOrTitle("MyField4").get().then(f => {
    -
    -    console.log(f);
    -});
    -
    - - -

    Filtering Fields

    -

    Sometimes you only want a subset of fields from the collection. Below are some examples of using the filter operator with the fields collection.

    -
    import { sp } from '@pnp/sp';
    -
    -const list = sp.web.lists.getByTitle('Custom');
    -
    -// Fields which can be updated
    -const filter1 = `Hidden eq false and ReadOnlyField eq false`;
    -list.fields.select('InternalName').filter(filter1).get().then(fields => {
    -    console.log(`Can be updated: ${fields.map(f => f.InternalName).join(', ')}`);
    -    // Title, ...Custom, ContentType, Attachments
    -});
    -
    -// Only custom field
    -const filter2 = `Hidden eq false and CanBeDeleted eq true`;
    -list.fields.select('InternalName').filter(filter2).get().then(fields => {
    -    console.log(`Custom fields: ${fields.map(f => f.InternalName).join(', ')}`);
    -    // ...Custom
    -});
    -
    -// Application specific fields
    -const includeFields = [ 'Title', 'Author', 'Editor', 'Modified', 'Created' ];
    -const filter3 = `Hidden eq false and (ReadOnlyField eq false or (${
    -    includeFields.map(field => `InternalName eq '${field}'`).join(' or ')
    -}))`;
    -list.fields.select('InternalName').filter(filter3).get().then(fields => {
    -    console.log(`Application specific: ${fields.map(f => f.InternalName).join(', ')}`);
    -    // Title, ...Custom, ContentType, Modified, Created, Author, Editor, Attachments
    -});
    -
    -// Fields in a view
    -list.defaultView.fields.select('Items').get().then(f => {
    -    const fields = (f as any).Items.results || (f as any).Items;
    -    console.log(`Fields in a view: ${fields.join(', ')}`);
    -});
    -
    - - -

    Add Fields

    -

    You can add fields using the add, createFieldAsXml, or one of the type specific methods. Functionally there is no difference, however one method may be easier given a certain scenario.

    -
    import { sp } from "@pnp/sp";
    -
    -let web = sp.web;
    -
    -// if you use add you _must_ include the correct FieldTypeKind in the extended properties
    -web.fields.add("MyField1", "SP.FieldText", { 
    -    Group: "~Example",
    -    FieldTypeKind: 2,
    -    Filterable: true,
    -    Hidden: false,
    -    EnforceUniqueValues: true,
    -}).then(f => {
    -
    -    console.log(f);
    -});
    -
    -// you can also use the addText or any of the other type specific methods on the collection
    -web.fields.addText("MyField2", 75, { 
    -    Group: "~Example"
    -}).then(f => {
    -
    -    console.log(f);
    -});
    -
    -// if you have the field schema (for example from an old elements file) you can use createFieldAsXml
    -let xml = `<Field DisplayName="MyField4" Type="Text" Required="FALSE" StaticName="MyField4" Name="MyField4" MaxLength="125" Group="~Example" />`;
    -
    -web.fields.createFieldAsXml(xml).then(f => {
    -
    -    console.log(f);
    -});
    -
    -// the same operations work on a list's fields collection
    -web.lists.getByTitle("MyList").fields.addText("MyField5", 100).then(f => {
    -
    -    console.log(f);
    -});
    -
    -// Create a lookup field, and a dependent lookup field
    -web.lists.getByTitle("MyList").fields.addLookup("MyLookup", "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx", "MyLookupTargetField").then(f => {
    -    console.log(f);
    -
    -    // Create the dependent lookup field
    -    return web.lists.getByTitle("MyList").fields.addDependentLookupField("MyLookup_ID", f.Id, "ID");
    -}).then(fDep => {
    -    console.log(fDep);
    -});
    -
    - - -

    Adding Multiline Text Fields with FullHtml

    -

    Because the RichTextMode property is not exposed to the clients we cannot set this value via the API directly. The work around is to use the createFieldAsXml method as shown below

    -
    import { sp } from "@pnp/sp";
    -
    -let web = sp.web;
    -
    -const fieldAddResult = await web.fields.createFieldAsXml(`<Field Type="Note" Name="Content" DisplayName="Content" Required="{TRUE|FALSE}" RichText="TRUE" RichTextMode="FullHtml" />`);
    -
    - - -

    Update a Field

    -

    You can also update the properties of a field in both webs and lists, but not all properties are able to be updated after creation. You can review this list for details.

    -
    import { sp } from "@pnp/sp";
    -
    -let web = sp.web;
    -
    -web.fields.getByTitle("MyField4").update({ 
    -    Description: "A new description",
    - }).then(f => {
    -
    -    console.log(f);
    -});
    -
    - - -

    Update a Url/Picture Field

    -

    When updating a URL or Picture field you need to include the __metadata descriptor as shown below.

    -
    import { sp } from "@pnp/sp";
    -
    -const data = {
    -    "My_Field_Name": {
    -        "__metadata": { "type": "SP.FieldUrlValue" },
    -        "Description": "A Pretty picture",
    -        "Url": "https://tenant.sharepoint.com/sites/dev/Style%20Library/DSC_0024.JPG",
    -    },
    -};
    -
    -await sp.web.lists.getByTitle("MyListTitle").items.getById(1).update(data);
    -
    - - -

    Delete a Field

    -
    import { sp } from "@pnp/sp";
    -
    -let web = sp.web;
    -
    -web.fields.getByTitle("MyField4").delete().then(f => {
    -
    -    console.log(f);
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/files/index.html b/docs/v3/v1/sp/docs/files/index.html deleted file mode 100644 index 85183647c..000000000 --- a/docs/v3/v1/sp/docs/files/index.html +++ /dev/null @@ -1,2178 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Files - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/files

    -

    One of the more challenging tasks on the client side is working with SharePoint files, especially if they are large files. We have added some methods to the library to help and their use is outlined below.

    -

    Reading Files

    -

    Reading files from the client using REST is covered in the below examples. The important thing to remember is choosing which format you want the file in so you can appropriately process it. You can retrieve a file as Blob, Buffer, JSON, or Text. If you have a special requirement you could also write your own parser.

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.getFileByServerRelativeUrl("/sites/dev/documents/file.avi").getBlob().then((blob: Blob) => {});
    -
    -sp.web.getFileByServerRelativeUrl("/sites/dev/documents/file.avi").getBuffer().then((buffer: ArrayBuffer) => {});
    -
    -sp.web.getFileByServerRelativeUrl("/sites/dev/documents/file.json").getJSON().then((json: any) => {});
    -
    -sp.web.getFileByServerRelativeUrl("/sites/dev/documents/file.txt").getText().then((text: string) => {});
    -
    -// all of these also work from a file object no matter how you access it
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/documents").files.getByName("file.txt").getText().then((text: string) => {});
    -
    - - -

    Adding Files

    -

    Likewise you can add files using one of two methods, add or addChunked. The second is appropriate for larger files, generally larger than 10 MB but this may differ based on your bandwidth/latency so you can adjust the code to use the chunked method. The below example shows getting the file object from an input and uploading it to SharePoint, choosing the upload method based on file size.

    -
    declare var require: (s: string) => any;
    -
    -import { ConsoleListener, Web, Logger, LogLevel, ODataRaw } from "@pnp/sp";
    -import { auth } from "./auth";
    -let $ = require("jquery");
    -
    -let siteUrl = "https://mytenant.sharepoint.com/sites/dev";
    -
    -// comment this out for non-node execution
    -// auth(siteUrl);
    -
    -Logger.subscribe(new ConsoleListener());
    -Logger.activeLogLevel = LogLevel.Verbose;
    -
    -let web = new Web(siteUrl);
    -
    -$(() => {
    -    $("#testingdiv").append("<button id='thebuttontodoit'>Do It</button>");
    -
    -    $("#thebuttontodoit").on('click', (e) => {
    -
    -        e.preventDefault();
    -
    -        let input = <HTMLInputElement>document.getElementById("thefileinput");
    -        let file = input.files[0];
    -
    -        // you can adjust this number to control what size files are uploaded in chunks
    -        if (file.size <= 10485760) {
    -
    -            // small upload
    -            web.getFolderByServerRelativeUrl("/sites/dev/Shared%20Documents/test/").files.add(file.name, file, true).then(_ => Logger.write("done"));
    -        } else {
    -
    -            // large upload
    -            web.getFolderByServerRelativeUrl("/sites/dev/Shared%20Documents/test/").files.addChunked(file.name, file, data => {
    -
    -                Logger.log({ data: data, level: LogLevel.Verbose, message: "progress" });
    -
    -            }, true).then(_ => Logger.write("done!"));
    -        }
    -    });
    -});
    -
    - - -

    Setting Associated Item Values

    -

    You can also update the file properties of a newly uploaded file using code similar to the below snippet:

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared%20Documents/test/").files.add(file.name, file, true).then(f => {
    -
    -    f.file.getItem().then(item => {
    -
    -        item.update({
    -            Title: "A Title",
    -            OtherField: "My Other Value"
    -        });
    -    });
    -});
    -
    - - -

    Update File Content

    -

    You can of course use similar methods to update existing files as shown below:

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.getFileByServerRelativeUrl("/sites/dev/documents/test.txt").setContent("New string content for the file.");
    -
    -sp.web.getFileByServerRelativeUrl("/sites/dev/documents/test.mp4").setContentChunked(file);
    -
    - - -

    Check in, Check out, and Approve & Deny

    -

    The library provides helper methods for checking in, checking out, and approving files. Examples of these methods are shown below.

    -

    Check In

    -

    Check in takes two optional arguments, comment and check in type.

    -
    import { sp, CheckinType } from "@pnp/sp";
    -
    -// default options with empty comment and CheckinType.Major
    -sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").checkin().then(_ => {
    -
    -    console.log("File checked in!");
    -});
    -
    -// supply a comment (< 1024 chars) and using default check in type CheckinType.Major
    -sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").checkin("A comment").then(_ => {
    -
    -    console.log("File checked in!");
    -});
    -
    -// Supply both comment and check in type
    -sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").checkin("A comment", CheckinType.Overwrite).then(_ => {
    -
    -    console.log("File checked in!");
    -});
    -
    - - -

    Check Out

    -

    Check out takes no arguments.

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").checkout().then(_ => {
    -
    -    console.log("File checked out!");
    -});
    -
    - - -

    Approve and Deny

    -

    You can also approve or deny files in libraries that use approval. Approve takes a single required argument of comment, the comment is optional for deny.

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").approve("Approval Comment").then(_ => {
    -
    -    console.log("File approved!");
    -});
    -
    -// deny with no comment
    -sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").deny().then(_ => {
    -
    -    console.log("File denied!");
    -});
    -
    -// deny with a supplied comment.
    -sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").deny("Deny comment").then(_ => {
    -
    -    console.log("File denied!");
    -});
    -
    - - -

    Publish and Unpublish

    -

    You can both publish and unpublish a file using the library. Both methods take an optional comment argument.

    -
    import { sp } from "@pnp/sp";
    -// publish with no comment
    -sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").publish().then(_ => {
    -
    -    console.log("File published!");
    -});
    -
    -// publish with a supplied comment.
    -sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").publish("Publish comment").then(_ => {
    -
    -    console.log("File published!");
    -});
    -
    -// unpublish with no comment
    -sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").unpublish().then(_ => {
    -
    -    console.log("File unpublished!");
    -});
    -
    -// unpublish with a supplied comment.
    -sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").unpublish("Unpublish comment").then(_ => {
    -
    -    console.log("File unpublished!");
    -});
    -
    - - -

    Advanced Upload Options

    -

    Both the addChunked and setContentChunked methods support options beyond just supplying the file content.

    -

    progress function

    -

    A method that is called each time a chunk is uploaded and provides enough information to report progress or update a progress bar easily. The method has the signature:

    -

    (data: ChunkedFileUploadProgressData) => void

    -

    The data interface is:

    -
    export interface ChunkedFileUploadProgressData {
    -    stage: "starting" | "continue" | "finishing";
    -    blockNumber: number;
    -    totalBlocks: number;
    -    chunkSize: number;
    -    currentPointer: number;
    -    fileSize: number;
    -}
    -
    - - -

    chunkSize

    -

    This property controls the size of the individual chunks and is defaulted to 10485760 bytes (10 MB). You can adjust this based on your bandwidth needs - especially if writing code for mobile uploads or you are seeing frequent timeouts.

    -

    getItem

    -

    This method allows you to get the item associated with this file. You can optionally specify one or more select fields. The result will be merged with a new Item instance so you will have both the returned property values and chaining ability in a single object.

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").getItem().then(item => {
    -
    -    console.log(item);
    -});
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").getItem("Title", "Modified").then(item => {
    -
    -    console.log(item);
    -});
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").getItem().then(item => {
    -
    -    // you can also chain directly off this item instance
    -    item.getCurrentUserEffectivePermissions().then(perms => {
    -
    -        console.log(perms);
    -    });
    -});
    -
    - - -

    You can also supply a generic typing parameter and the resulting type will be a union type of Item and the generic type parameter. This allows you to have proper intellisense and type checking.

    -
    import { sp } from "@pnp/sp";
    -// also supports typing the objects so your type will be a union type
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").getItem<{ Id: number, Title: string }>("Id", "Title").then(item => {
    -
    -    // You get intellisense and proper typing of the returned object
    -    console.log(`Id: ${item.Id} -- ${item.Title}`);
    -
    -    // You can also chain directly off this item instance
    -    item.getCurrentUserEffectivePermissions().then(perms => {
    -
    -        console.log(perms);
    -    });
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/index.html b/docs/v3/v1/sp/docs/index.html deleted file mode 100644 index 9be93ee65..000000000 --- a/docs/v3/v1/sp/docs/index.html +++ /dev/null @@ -1,1894 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - sp - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp

    -

    npm version

    -

    This package contains the fluent api used to call the SharePoint rest services.

    -

    Getting Started

    -

    Install the library and required dependencies

    -

    npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp --save

    -

    Import the library into your application and access the root sp object

    -
    import { sp } from "@pnp/sp";
    -
    -(function main() {
    -
    -    // here we will load the current web's title
    -    sp.web.select("Title").get().then(w => {
    -
    -        console.log(`Web Title: ${w.Title}`);
    -    });
    -})()
    -
    - - -

    Getting Started: SharePoint Framework

    -

    Install the library and required dependencies

    -

    npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp --save

    -

    Import the library into your application, update OnInit, and access the root sp object in render

    -
    import { sp } from "@pnp/sp";
    -
    -// ...
    -
    -public onInit(): Promise<void> {
    -
    -  return super.onInit().then(_ => {
    -
    -    // other init code may be present
    -
    -    sp.setup({
    -      spfxContext: this.context
    -    });
    -  });
    -}
    -
    -// ...
    -
    -public render(): void {
    -
    -    // A simple loading message
    -    this.domElement.innerHTML = `Loading...`;
    -
    -    sp.web.select("Title").get().then(w => {
    -
    -        this.domElement.innerHTML = `Web Title: ${w.Title}`;
    -    });
    -}
    -
    - - -

    Getting Started: Nodejs

    -

    Install the library and required dependencies

    -

    npm install @pnp/logging @pnp/core @pnp/queryable @pnp/sp @pnp/nodejs --save

    -

    Import the library into your application, setup the node client, make a request

    -
    import { sp } from "@pnp/sp";
    -import { SPFetchClient } from "@pnp/nodejs";
    -
    -// do this once per page load
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -            return new SPFetchClient("{your site url}", "{your client id}", "{your client secret}");
    -        },
    -    },
    -});
    -
    -// now make any calls you need using the configured client
    -sp.web.select("Title").get().then(w => {
    -
    -    console.log(`Web Title: ${w.Title}`);
    -});
    -
    - - -

    Library Topics

    - -

    UML

    -

    Graphical UML diagram

    -

    Graphical UML diagram of @pnp/sp. Right-click the diagram and open in new tab if it is too small.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/items/index.html b/docs/v3/v1/sp/docs/items/index.html deleted file mode 100644 index ad1cceaaf..000000000 --- a/docs/v3/v1/sp/docs/items/index.html +++ /dev/null @@ -1,2362 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - List Items - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - -

    @pnp/sp/items

    -

    GET

    -

    Getting items from a list is one of the basic actions that most applications require. This is made easy through the library and the following examples demonstrate these actions.

    -

    Basic Get

    -
    import { sp } from "@pnp/sp";
    -
    -// get all the items from a list
    -sp.web.lists.getByTitle("My List").items.get().then((items: any[]) => {
    -    console.log(items);
    -});
    -
    -// get a specific item by id
    -sp.web.lists.getByTitle("My List").items.getById(1).get().then((item: any) => {
    -    console.log(item);
    -});
    -
    -// use odata operators for more efficient queries
    -sp.web.lists.getByTitle("My List").items.select("Title", "Description").top(5).orderBy("Modified", true).get().then((items: any[]) => {
    -    console.log(items);
    -});
    -
    - - -

    Get Paged Items

    -

    Working with paging can be a challenge as it is based on skip tokens and item ids, something that is hard to guess at runtime. To simplify things you can use the getPaged method on the Items class to assist. Note that there isn't a way to move backwards in the collection, this is by design. The pattern you should use to support backwards navigation in the results is to cache the results into a local array and use the standard array operators to get previous pages. Alternatively you can append the results to the UI, but this can have performance impact for large result sets.

    -
    import { sp } from "@pnp/sp";
    -
    -// basic case to get paged items form a list
    -let items = await sp.web.lists.getByTitle("BigList").items.getPaged();
    -
    -// you can also provide a type for the returned values instead of any
    -let items = await sp.web.lists.getByTitle("BigList").items.getPaged<{Title: string}[]>();
    -
    -// the query also works with select to choose certain fields and top to set the page size
    -let items = await sp.web.lists.getByTitle("BigList").items.select("Title", "Description").top(50).getPaged<{Title: string}[]>();
    -
    -// the results object will have two properties and one method:
    -
    -// the results property will be an array of the items returned
    -if (items.results.length > 0) {
    -    console.log("We got results!");
    -
    -    for (let i = 0; i < items.results.length; i++) {
    -        // type checking works here if we specify the return type
    -        console.log(items.results[i].Title);
    -    }
    -}
    -
    -// the hasNext property is used with the getNext method to handle paging
    -// hasNext will be true so long as there are additional results
    -if (items.hasNext) {
    -
    -    // this will carry over the type specified in the original query for the results array
    -    items = await items.getNext();
    -    console.log(items.results.length);
    -}
    -
    - - -

    getListItemChangesSinceToken

    -

    The GetListItemChangesSinceToken method allows clients to track changes on a list. Changes, including deleted items, are returned along with a token that represents the moment in time when those changes were requested. By including this token when you call GetListItemChangesSinceToken, the server looks for only those changes that have occurred since the token was generated. Sending a GetListItemChangesSinceToken request without including a token returns the list schema, the full list contents and a token.

    -
    import { sp } from "@pnp/sp";
    -
    -// Using RowLimit. Enables paging
    -let changes = await sp.web.lists.getByTitle("BigList").getListItemChangesSinceToken({RowLimit: '5'});
    -
    -// Use QueryOptions to make a XML-style query.
    -// Because it's XML we need to escape special characters
    -// Instead of & we use &amp; in the query
    -let changes = await sp.web.lists.getByTitle("BigList").getListItemChangesSinceToken({QueryOptions: '<Paging ListItemCollectionPositionNext="Paged=TRUE&amp;p_ID=5" />'});
    -
    -// Get everything. Using null with ChangeToken gets everything
    -let changes = await sp.web.lists.getByTitle("BigList").getListItemChangesSinceToken({ChangeToken: null});
    -
    - - -

    Get All Items

    -

    Added in 1.0.2

    -

    Using the items collection's getAll method you can get all of the items in a list regardless of the size of the list. Sample usage is shown below. Only the odata operations top, select, and filter are supported. usingCaching and inBatch are ignored - you will need to handle caching the results on your own. This method will write a warning to the Logger and should not frequently be used. Instead the standard paging operations should -be used.

    -
    import { sp } from "@pnp/sp";
    -// basic usage
    -sp.web.lists.getByTitle("BigList").items.getAll().then((allItems: any[]) => {
    -
    -    // how many did we get
    -    console.log(allItems.length);
    -});
    -
    -// set page size
    -sp.web.lists.getByTitle("BigList").items.getAll(4000).then((allItems: any[]) => {
    -
    -    // how many did we get
    -    console.log(allItems.length);
    -});
    -
    -// use select and top. top will set page size and override the any value passed to getAll
    -sp.web.lists.getByTitle("BigList").items.select("Title").top(4000).getAll().then((allItems: any[]) => {
    -
    -    // how many did we get
    -    console.log(allItems.length);
    -});
    -
    -// we can also use filter as a supported odata operation, but this will likely fail on large lists
    -sp.web.lists.getByTitle("BigList").items.select("Title").filter("Title eq 'Test'").getAll().then((allItems: any[]) => {
    -
    -    // how many did we get
    -    console.log(allItems.length);
    -});
    -
    - - -

    Retrieving Lookup Fields

    -

    When working with lookup fields you need to use the expand operator along with select to get the related fields from the lookup column. This works for both the items collection and item instances.

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.lists.getByTitle("LookupList").items.select("Title", "Lookup/Title", "Lookup/ID").expand("Lookup").get().then((items: any[]) => {
    -    console.log(items);
    -});
    -
    -sp.web.lists.getByTitle("LookupList").items.getById(1).select("Title", "Lookup/Title", "Lookup/ID").expand("Lookup").get().then((item: any) => {
    -    console.log(item);
    -});
    -
    - - -

    Retrieving PublishingPageImage

    -

    The PublishingPageImage and some other publishing-related fields aren't stored in normal fields, rather in the MetaInfo field. To get these values you need to use the technique shown below, and originally outlined in this thread. Note that a lot of information can be stored in this field so will pull back potentially a significant amount of data, so limit the rows as possible to aid performance.

    -
    import { Web } from "@pnp/sp";
    -
    -const w = new Web("https://{publishing site url}");
    -
    -w.lists.getByTitle("Pages").items
    -    .select("Title", "FileRef", "FieldValuesAsText/MetaInfo")
    -    .expand("FieldValuesAsText")
    -    .get().then(r => {
    -
    -        // look through the returned items.
    -        for (var i = 0; i < r.length; i++) {
    -
    -            // the title field value
    -            console.log(r[i].Title);
    -
    -            // find the value in the MetaInfo string using regex
    -            const matches = /PublishingPageImage:SW\|(.*?)\r\n/ig.exec(r[i].FieldValuesAsText.MetaInfo);
    -            if (matches !== null && matches.length > 1) {
    -
    -                // this wil be the value of the PublishingPageImage field
    -                console.log(matches[1]);
    -            }
    -        }
    -    }).catch(e => { console.error(e); });
    -
    - - -

    Add Items

    -

    There are several ways to add items to a list. The simplest just uses the add method of the items collection passing in the properties as a plain object.

    -
    import { sp, ItemAddResult } from "@pnp/sp";
    -
    -// add an item to the list
    -sp.web.lists.getByTitle("My List").items.add({
    -    Title: "Title",
    -    Description: "Description"
    -}).then((iar: ItemAddResult) => {
    -    console.log(iar);
    -});
    -
    - - -

    Content Type

    -

    You can also set the content type id when you create an item as shown in the example below:

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.lists.getById("4D5A36EA-6E84-4160-8458-65C436DB765C").items.add({
    -    Title: "Test 1",
    -    ContentTypeId: "0x01030058FD86C279252341AB303852303E4DAF"
    -});
    -
    - - -

    User Fields

    -

    There are two types of user fields, those that allow a single value and those that allow multiple. For both types, you first need to determine the Id field name, which you can do by doing a GET REST request on an existing item. Typically the value will be the user field internal name with "Id" appended. So in our example, we have two fields User1 and User2 so the Id fields are User1Id and User2Id.

    -

    Next, you need to remember there are two types of user fields, those that take a single value and those that allow multiple - these are updated in different ways. For single value user fields you supply just the user's id. For multiple value fields, you need to supply an object with a "results" property and an array. Examples for both are shown below.

    -
    import { sp } from "@pnp/sp";
    -import { getGUID } from "@pnp/core";
    -
    -sp.web.lists.getByTitle("PeopleFields").items.add({
    -    Title: getGUID(),
    -    User1Id: 9, // allows a single user
    -    User2Id: {
    -        results: [ 16, 45 ] // allows multiple users
    -    }
    -}).then(i => {
    -    console.log(i);
    -});
    -
    - - -

    If you want to update or add user field values when using validateUpdateListItem you need to use the form shown below. You can specify multiple values in the array.

    -
    import { sp } from "@pnp/sp";
    -
    -const result = await sp.web.lists.getByTitle("UserFieldList").items.getById(1).validateUpdateListItem([{
    -    FieldName: "UserField",
    -    FieldValue: JSON.stringify([{ "Key": "i:0#.f|membership|person@tenant.com" }]),
    -},
    -{
    -    FieldName: "Title",
    -    FieldValue: "Test - Updated",
    -}]);
    -
    - - -

    Lookup Fields

    -

    What is said for User Fields is, in general, relevant to Lookup Fields: -- Lookup Field types: - - Single-valued lookup - - Multiple-valued lookup -- Id suffix should be appended to the end of lookup's EntityPropertyName in payloads -- Numeric Ids for lookups' items should be passed as values

    -
    import { sp } from "@pnp/sp";
    -import { getGUID } from "@pnp/core";
    -
    -sp.web.lists.getByTitle("LookupFields").items.add({
    -    Title: getGUID(),
    -    LookupFieldId: 2,       // allows a single lookup value
    -    MuptiLookupFieldId: {
    -        results: [ 1, 56 ]  // allows multiple lookup value
    -    }
    -}).then(console.log).catch(console.log);
    -
    - - -

    Add Multiple Items

    -
    import { sp } from "@pnp/sp";
    -
    -let list = sp.web.lists.getByTitle("rapidadd");
    -
    -list.getListItemEntityTypeFullName().then(entityTypeFullName => {
    -
    -    let batch = sp.web.createBatch();
    -
    -    list.items.inBatch(batch).add({ Title: "Batch 6" }, entityTypeFullName).then(b => {
    -        console.log(b);
    -    });
    -
    -    list.items.inBatch(batch).add({ Title: "Batch 7" }, entityTypeFullName).then(b => {
    -        console.log(b);
    -    });
    -
    -    batch.execute().then(d => console.log("Done"));
    -});
    -
    - - -

    Update

    -

    The update method is very similar to the add method in that it takes a plain object representing the fields to update. The property names are the internal names of the fields. If you aren't sure you can always do a get request for an item in the list and see the field names that come back - you would use these same names to update the item.

    -
    import { sp } from "@pnp/sp";
    -
    -let list = sp.web.lists.getByTitle("MyList");
    -
    -list.items.getById(1).update({
    -    Title: "My New Title",
    -    Description: "Here is a new description"
    -}).then(i => {
    -    console.log(i);
    -});
    -
    - - -

    Getting and updating a collection using filter

    -
    import { sp } from "@pnp/sp";
    -
    -// you are getting back a collection here
    -sp.web.lists.getByTitle("MyList").items.top(1).filter("Title eq 'A Title'").get().then((items: any[]) => {
    -    // see if we got something
    -    if (items.length > 0) {
    -        sp.web.lists.getByTitle("MyList").items.getById(items[0].Id).update({
    -            Title: "Updated Title",
    -        }).then(result => {
    -            // here you will have updated the item
    -            console.log(JSON.stringify(result));
    -        });
    -    }
    -});
    -
    - - -

    Update Multiple Items

    -

    This approach avoids multiple calls for the same list's entity type name.

    -
    import { sp } from "@pnp/sp";
    -
    -let list = sp.web.lists.getByTitle("rapidupdate");
    -
    -list.getListItemEntityTypeFullName().then(entityTypeFullName => {
    -
    -    let batch = sp.web.createBatch();
    -
    -    // note requirement of "*" eTag param - or use a specific eTag value as needed
    -    list.items.getById(1).inBatch(batch).update({ Title: "Batch 6" }, "*", entityTypeFullName).then(b => {
    -        console.log(b);
    -    });
    -
    -    list.items.getById(2).inBatch(batch).update({ Title: "Batch 7" }, "*", entityTypeFullName).then(b => {
    -        console.log(b);
    -    });
    -
    -    batch.execute().then(d => console.log("Done"));
    -});
    -
    - - -

    Recycle

    -

    Sending an item to the Recycle Bin is as simple as calling the .recycle method.

    -
    import { sp } from "@pnp/sp";
    -
    -let list = sp.web.lists.getByTitle("MyList");
    -
    -list.items.getById(1).recycle().then(_ => {});
    -
    - - -

    Delete

    -

    Delete is as simple as calling the .delete method. It optionally takes an eTag if you need to manage concurrency.

    -
    import { sp } from "@pnp/sp";
    -
    -let list = sp.web.lists.getByTitle("MyList");
    -
    -list.items.getById(1).delete().then(_ => {});
    -
    - - -

    Resolving field names

    -

    It's a very common mistake trying wrong field names in the requests. -Field's EntityPropertyName value should be used.

    -

    The easiest way to get know EntityPropertyName is to use the following snippet:

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.lists
    -  .getByTitle('[Lists_Title]')
    -  .fields
    -  .select('Title, EntityPropertyName')
    -  .filter(`Hidden eq false and Title eq '[Field's_Display_Name]'`)
    -  .get()
    -  .then(response => {
    -    console.log(response.map(field => {
    -      return {
    -        Title: field.Title,
    -    EntityPropertyName: field.EntityPropertyName
    -      };
    -    }));
    -  })
    -  .catch(console.log);
    -
    - - -

    Lookup fields' names should be ended with additional Id suffix. E.g. for Editor EntityPropertyName EditorId should be used.

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/navigation-service/index.html b/docs/v3/v1/sp/docs/navigation-service/index.html deleted file mode 100644 index b625c31e7..000000000 --- a/docs/v3/v1/sp/docs/navigation-service/index.html +++ /dev/null @@ -1,1791 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Navigation Service - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/navigation service

    -

    The global navigation service located at "_api/navigation" provides access to the SiteMapProvider instances available in a given site collection.

    -

    getMenuState

    -

    The MenuState service operation returns a Menu-State (dump) of a SiteMapProvider on a site. It will return an exception if the SiteMapProvider cannot be found on the site, the SiteMapProvider does not implement the IEditableSiteMapProvider interface or the SiteMapNode key cannot be found within the provider hierarchy.

    -

    The IEditableSiteMapProvider also supports Custom Properties which is an optional feature. What will be return in the custom properties is up to the IEditableSiteMapProvider implementation and can differ for for each SiteMapProvider implementation. The custom properties can be requested by providing a comma seperated string of property names like: property1,property2,property3\,containingcomma

    -

    NOTE: the , seperator can be escaped using the \ as escape character as done in the example above. The string above would split like: - property1 - property2 -* property3,containingcomma

    -
    import { sp } from "@pnp/sp";
    -
    -// Will return a menu state of the default SiteMapProvider 'SPSiteMapProvider' where the dump starts a the RootNode (within the site) with a depth of 10 levels.
    -sp.navigation.getMenuState().then(r => {
    -
    -    console.log(JSON.stringify(r, null, 4));
    -
    -}).catch(console.error);
    -
    -// Will return the menu state of the 'SPSiteMapProvider', starting with the node with the key '1002' with a depth of 5
    -sp.navigation.getMenuState("1002", 5).then(r => {
    -
    -    console.log(JSON.stringify(r, null, 4));
    -
    -}).catch(console.error);
    -
    -// Will return the menu state of the 'CurrentNavSiteMapProviderNoEncode' from the root node of the provider with a depth of 5
    -sp.navigation.getMenuState(null, 5, "CurrentNavSiteMapProviderNoEncode").then(r => {
    -
    -    console.log(JSON.stringify(r, null, 4));
    -
    -}).catch(console.error);
    -
    - - -

    getMenuNodeKey

    -

    Tries to get a SiteMapNode.Key for a given URL within a site collection. If the SiteMapNode cannot be found an Exception is returned. The method is using SiteMapProvider.FindSiteMapNodeFromKey(string rawUrl) to lookup the SiteMapNode. Depending on the actual implementation of FindSiteMapNodeFromKey the matching can differ for different SiteMapProviders.

    -
    import { sp } from "@pnp/sp";
    -
    -sp.navigation.getMenuNodeKey("/sites/dev/Lists/SPPnPJSExampleList/AllItems.aspx").then(r => {
    -
    -    console.log(JSON.stringify(r, null, 4));
    -
    -}).catch(console.error);
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/permissions/index.html b/docs/v3/v1/sp/docs/permissions/index.html deleted file mode 100644 index b1d09ae99..000000000 --- a/docs/v3/v1/sp/docs/permissions/index.html +++ /dev/null @@ -1,1860 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Permissions - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp - permissions

    -

    A common task is to determine if a user or the current user has a certain permission level. It is a great idea to check before performing a task such as creating a list to ensure a user can without getting back an error. This allows you to provide a better experience to the user.

    -

    Permissions in SharePoint are assigned to the set of securable objects which include Site, Web, List, and List Item. These are the four level to which unique permissions can be assigned. As such @pnp/sp provides a set of methods defined in the QueryableSecurable class to handle these permissions. These examples all use the Web to get the values, however the methods work identically on all securables.

    -

    Get Role Assignments

    -

    This gets a collection of all the role assignments on a given securable. The property returns a RoleAssignments collection which supports the OData collection operators.

    -
    import { sp } from "@pnp/sp";
    -import { Logger } from "@pnp/logging";
    -
    -sp.web.roleAssignments.get().then(roles => {
    -
    -    Logger.writeJSON(roles);
    -});
    -
    - - -

    First Unique Ancestor Securable Object

    -

    This method can be used to find the securable parent up the hierarchy that has unique permissions. If everything inherits permissions this will be the Site. If a sub web has unique permissions it will be the web, and so on.

    -
    import { sp } from "@pnp/sp";
    -import { Logger } from "@pnp/logging";
    -
    -sp.web.firstUniqueAncestorSecurableObject.get().then(obj => {
    -
    -    Logger.writeJSON(obj);
    -});
    -
    - - -

    User Effective Permissions

    -

    This method returns the BasePermissions for a given user or the current user. This value contains the High and Low values for a user on the securable you have queried.

    -
    import { sp } from "@pnp/sp";
    -import { Logger } from "@pnp/logging";
    -
    -sp.web.getUserEffectivePermissions("i:0#.f|membership|user@site.com").then(perms => {
    -
    -    Logger.writeJSON(perms);
    -});
    -
    -sp.web.getCurrentUserEffectivePermissions().then(perms => {
    -
    -    Logger.writeJSON(perms);
    -});
    -
    - - -

    User Has Permissions

    -

    Because the High and Low values in the BasePermission don't obviously mean anything you can use these methods along with the PermissionKind enumeration to check actual rights on the securable.

    -
    import { sp, PermissionKind } from "@pnp/sp";
    -
    -sp.web.userHasPermissions("i:0#.f|membership|user@site.com", PermissionKind.ApproveItems).then(perms => {
    -
    -    console.log(perms);
    -});
    -
    -sp.web.currentUserHasPermissions(PermissionKind.ApproveItems).then(perms => {
    -
    -    console.log(perms);
    -});
    -
    - - -

    Has Permissions

    -

    If you need to check multiple permissions it can be more efficient to get the BasePermissions once and then use the hasPermissions method to check them as shown below.

    -
    import { sp, PermissionKind } from "@pnp/sp";
    -
    -sp.web.getCurrentUserEffectivePermissions().then(perms => {
    -
    -    if (sp.web.hasPermissions(perms, PermissionKind.AddListItems) && sp.web.hasPermissions(perms, PermissionKind.DeleteVersions)) {
    -        // ...
    -    }
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/profiles/index.html b/docs/v3/v1/sp/docs/profiles/index.html deleted file mode 100644 index bb8c1fc30..000000000 --- a/docs/v3/v1/sp/docs/profiles/index.html +++ /dev/null @@ -1,1794 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Profiles - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/profiles

    -

    The profile services allows to to work with the SharePoint User Profile Store.

    -

    Profiles

    -

    Profiles is accessed directly from the root sp object.

    -
    import { sp } from "@pnp/sp";
    -
    - - -

    GET

    -

    Get profile properties for a specific user

    -

    getPropertiesFor(loginName: string): Promise<any>;

    -
    sp
    -  .profiles
    -  .getPropertiesFor(loginName).then((profile: any) => {
    -
    -    console.log(profile.DisplayName);
    -    console.log(profile.Email);
    -    console.log(profile.Title);
    -    console.log(profile.UserProfileProperties.length);
    -
    -    // Properties are stored in inconvenient Key/Value pairs,
    -    // so parse into an object called userProperties
    -    var properties = {};
    -    profile.UserProfileProperties.forEach(function(prop) {
    -    properties[prop.Key] = prop.Value;
    -    });
    -    profile.userProperties = properties;
    -
    -}
    -
    - - -

    Get a specific property for a specific user

    -

    getUserProfilePropertyFor(loginName: string, propertyName: string): Promise<string>;

    -
    sp
    -  .profiles
    -  .getUserProfilePropertyFor(loginName, propName).then((prop: string) => {
    -    console.log(prop);
    -};
    -
    - - -

    Find whether a user is following another user

    -

    isFollowing(follower: string, followee: string): Promise<boolean>;

    -
    sp
    -  .profiles
    -  .isFollowing(follower, followee).then((followed: boolean) => {
    -    console.log(followed);
    -};
    -
    - - -

    Find out who a user is following

    -

    getPeopleFollowedBy(loginName: string): Promise<any[]>;

    -
    sp
    -  .profiles
    -  .getPeopleFollowedBy(loginName).then((followed: any[]) => {
    -    console.log(followed.length);
    -};
    -
    - - -

    Find out if the current user is followed by another user

    -

    amIFollowedBy(loginName: string): Promise<boolean>;

    -

    Returns a boolean indicating if the current user is followed by the user with loginName. -Get a specific property for the specified user.

    -
    sp
    -  .profiles
    -  .amIFollowedBy(loginName).then((followed: boolean) => {
    -    console.log(followed);
    -};
    -
    - - -

    Get the people who are following the specified user

    -

    getFollowersFor(loginName: string): Promise<any[]>;

    -
    sp
    -  .profiles
    -  .getFollowersFor(loginName).then((followed: any) => {
    -    console.log(followed.length);
    -};
    -
    - - -

    SET

    -

    Set a single value property value

    -

    setSingleValueProfileProperty(accountName: string, propertyName: string, propertyValue: string)

    -

    Set a user's user profile property.

    -
    sp
    -  .profiles
    -  .setSingleValueProfileProperty(accountName, propertyName, propertyValue);
    -
    - - -

    Set multi valued User Profile property

    -

    setMultiValuedProfileProperty(accountName: string, propertyName: string, propertyValues: string[]): Promise<void>;

    -
    sp
    -  .profiles
    -  .setSingleValueProfileProperty(accountName, propertyName, propertyValues);
    -
    - - -

    Upload and set the user profile picture

    -

    Users can upload a picture to their own profile only). Not supported for batching. -Blob data representing the user's picture in BMP, JPEG, or PNG format of up to 4.76MB

    -

    setMyProfilePic(profilePicSource: Blob): Promise<void>;

    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/related-items/index.html b/docs/v3/v1/sp/docs/related-items/index.html deleted file mode 100644 index 9ae3ab2ce..000000000 --- a/docs/v3/v1/sp/docs/related-items/index.html +++ /dev/null @@ -1,1886 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Related Items - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/relateditems

    -

    Related items are used in Task and Workflow lists (as well as others) to track items that have relationships similar to database relationships.

    -

    All methods chain off the Web's relatedItems property as shown below:

    -

    getRelatedItems

    -

    Expects the named library to exist within the contextual web.

    -
    import { sp, RelatedItem } from "@pnp/sp";
    -
    -sp.web.relatedItems.getRelatedItems("Documents", 1).then((result: RelatedItem[]) => {
    -
    -    console.log(result);
    -});
    -
    - - -

    getPageOneRelatedItems

    -

    Expects the named library to exist within the contextual web.

    -
    import { sp, RelatedItem } from "@pnp/sp";
    -
    -sp.web.relatedItems.getPageOneRelatedItems("Documents", 1).then((result: RelatedItem[]) => {
    -
    -    console.log(result);
    -});
    -
    - - - -
    import { sp } from "@pnp/sp";
    -
    -sp.web.relatedItems.addSingleLink("RelatedItemsList1", 2, "https://site.sharepoint.com/sites/dev/subsite", "RelatedItemsList2", 1, "https://site.sharepoint.com/sites/dev").then(_ => {
    -
    -    // ... return is void
    -});
    -
    -sp.web.relatedItems.addSingleLink("RelatedItemsList1", 2, "https://site.sharepoint.com/sites/dev/subsite", "RelatedItemsList2", 1, "https://site.sharepoint.com/sites/dev", true).then(_ => {
    -
    -    // ... return is void
    -});
    -
    - - -

    addSingleLinkToUrl

    -

    Adds a related item link from an item specified by list name and item id, to an item specified by url

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.relatedItems.addSingleLinkToUrl("RelatedItemsList1", 2, "https://site.sharepoint.com/sites/dev/subsite/Documents/test.txt").then(_ => {
    -
    -    // ... return is void
    -});
    -
    -sp.web.relatedItems.addSingleLinkToUrl("RelatedItemsList1", 2, "https://site.sharepoint.com/sites/dev/subsite/Documents/test.txt", true).then(_ => {
    -    // ... return is void
    -});
    -
    - - -

    addSingleLinkFromUrl

    -

    Adds a related item link from an item specified by url, to an item specified by list name and item id

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.relatedItems.addSingleLinkFromUrl("https://site.sharepoint.com/sites/dev/subsite/Documents/test.txt", "RelatedItemsList1", 2).then(_ => {
    -    // ... return is void
    -});
    -
    -sp.web.relatedItems.addSingleLinkFromUrl("https://site.sharepoint.com/sites/dev/subsite/Documents/test.txt", "RelatedItemsList1", 2, true).then(_ => {
    -
    -    // ... return is void
    -});
    -
    - - - -
    import { sp } from "@pnp/sp";
    -
    -sp.web.relatedItems.deleteSingleLink("RelatedItemsList1", 2, "https://site.sharepoint.com/sites/dev/subsite", "RelatedItemsList2", 1, "https://site.sharepoint.com/sites/dev").then(_ => {
    -
    -    // ... return is void
    -});
    -
    -sp.web.relatedItems.deleteSingleLink("RelatedItemsList1", 2, "https://site.sharepoint.com/sites/dev/subsite", "RelatedItemsList2", 1, "https://site.sharepoint.com/sites/dev", true).then(_ => {
    -
    -    // ... return is void
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/search/index.html b/docs/v3/v1/sp/docs/search/index.html deleted file mode 100644 index 0a8f29fbe..000000000 --- a/docs/v3/v1/sp/docs/search/index.html +++ /dev/null @@ -1,1830 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Search - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/search

    -

    Using search you can access content throughout your organization in a secure and consistent manner. The library provides support for searching and search suggest - as well as some interfaces and helper classes to make building your queries and processing responses easier.

    -

    Search

    -

    Search is accessed directly from the root sp object and can take either a string representing the query text, a plain object matching the SearchQuery interface, or a SearchQueryBuilder instance. The first two are shown below.

    -
    import { sp, SearchQuery, SearchResults } from "@pnp/sp";
    -
    -// text search using SharePoint default values for other parameters
    -sp.search("test").then((r: SearchResults) => {
    -
    -    console.log(r.ElapsedTime);
    -    console.log(r.RowCount);
    -    console.log(r.PrimarySearchResults);
    -});
    -
    -// define a search query object matching the SearchQuery interface
    -sp.search(<SearchQuery>{
    -    Querytext: "test",
    -    RowLimit: 10,
    -    EnableInterleaving: true,
    -}).then((r: SearchResults) => {
    -
    -    console.log(r.ElapsedTime);
    -    console.log(r.RowCount);
    -    console.log(r.PrimarySearchResults);
    -});
    -
    - - -

    Search Result Caching

    -

    Added in 1.1.5

    -

    As of version 1.1.5 you can also use the searchWithCaching method to enable cache support for your search results this option works with any of the options for providing a query, just replace "search" with "searchWithCaching" in your method chain and gain all the benefits of caching. The second parameter is optional and allows you to specify the cache options

    -
    import { sp, SearchQuery, SearchResults, SearchQueryBuilder } from "@pnp/sp";
    -
    -sp.searchWithCaching(<SearchQuery>{
    -    Querytext: "test",
    -    RowLimit: 10,
    -    EnableInterleaving: true,
    -}).then((r: SearchResults) => {
    -
    -    console.log(r.ElapsedTime);
    -    console.log(r.RowCount);
    -    console.log(r.PrimarySearchResults);
    -});
    -
    -
    -const builder = SearchQueryBuilder().text("test").rowLimit(3);
    -
    -// supply a search query builder and caching options
    -sp.searchWithCaching(builder, { key: "mykey", expiration: dateAdd(new Date(), "month", 1) }).then(r2 => {
    -
    -    console.log(r2.TotalRows);
    -});
    -
    - - -

    Paging with SearchResults.getPage

    -

    Paging is controlled by a start row and page size parameter. You can specify both arguments in your initial query however you can use the getPage method to jump to any page. The second parameter page size is optional and will use the previous RowLimit or default to 10.

    -
    import { sp, SearchQueryBuilder, SearchResults } from "@pnp/sp";
    -
    -// this will hold our current results
    -let currentResults: SearchResults = null;
    -let page = 1;
    -
    -// triggered on page load through some means
    -function onStart() {
    -
    -    // construct our query that will be throughout the paging process, likely from user input
    -    const q = SearchQueryBuilder.create("test").rowLimit(5);
    -    sp.search(q).then((r: SearchResults) => {
    -
    -        currentResults = r; // update the current results
    -        page = 1; // reset if needed
    -        // update UI with data...
    -    });
    -}
    -
    -// triggered by an event
    -function next() {
    -    currentResults.getPage(++page).then((r: SearchResults) => {
    -
    -        currentResults = r; // update the current results
    -        // update UI with data...
    -    });
    -}
    -
    -// triggered by an event
    -function prev() {
    -    currentResults.getPage(--page).then((r: SearchResults) => {
    -
    -        currentResults = r; // update the current results
    -        // update UI with data...
    -    });
    -}
    -
    - - -

    SearchQueryBuilder

    -

    The SearchQueryBuilder allows you to build your queries in a fluent manner. It also accepts constructor arguments for query text and a base query plain object, should you have a shared configuration for queries in an application you can define them once. The methods and properties match those on the SearchQuery interface. Boolean properties add the flag to the query while methods require that you supply one or more arguments. Also arguments supplied later in the chain will overwrite previous values.

    -
    import { SearchQueryBuilder } from "@pnp/sp";
    -
    -// basic usage
    -let q = SearchQueryBuilder().text("test").rowLimit(4).enablePhonetic;
    -
    -sp.search(q).then(h => { /* ... */ });
    -
    -// provide a default query text in the create()
    -let q2 = SearchQueryBuilder("text").rowLimit(4).enablePhonetic;
    -
    -sp.search(q2).then(h => { /* ... */ });
    -
    -// provide query text and a template
    -
    -// shared settings across queries
    -const appSearchSettings: SearchQuery = {
    -    EnablePhonetic: true,
    -    HiddenConstraints: "reports"
    -};
    -
    -let q3 = SearchQueryBuilder("test", appSearchSettings).enableQueryRules;
    -let q4 = SearchQueryBuilder("financial data", appSearchSettings).enableSorting.enableStemming;
    -sp.search(q3).then(h => { /* ... */ });
    -sp.search(q4).then(h => { /* ... */ });
    -
    - - -

    Search Suggest

    -

    Search suggest works in much the same way as search, except against the suggest end point. It takes a string or a plain object that matches SearchSuggestQuery.

    -
    import { sp, SearchSuggestQuery, SearchSuggestResult } from "@pnp/sp";
    -
    -sp.searchSuggest("test").then((r: SearchSuggestResult) => {
    -
    -    console.log(r);
    -});
    -
    -sp.searchSuggest(<SearchSuggestQuery>{
    -    querytext: "test",
    -    count: 5,
    -}).then((r: SearchSuggestResult) => {
    -
    -    console.log(r);
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/sharing/index.html b/docs/v3/v1/sp/docs/sharing/index.html deleted file mode 100644 index ed1915e02..000000000 --- a/docs/v3/v1/sp/docs/sharing/index.html +++ /dev/null @@ -1,2058 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Sharing - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - -

    @pnp/sp/sharing

    -

    Note: This API is still considered "beta" meaning it may change and some behaviors may differ across tenants by version. It is also supported only in SharePoint Online.

    -

    One of the newer abilities in SharePoint is the ability to share webs, files, or folders with both internal and external folks. It is important to remember that these settings are managed at the tenant level and override anything you may supply as an argument to these methods. If you receive an InvalidOperationException when using these methods please check your tenant sharing settings to ensure sharing is not blocked before submitting an issue.

    - -

    Applies to: Item, Folder, File

    -

    Creates a sharing link for the given resource with an optional expiration.

    -
    import { sp , SharingLinkKind, ShareLinkResponse } from "@pnp/sp";
    -import { dateAdd } from "@pnp/core";
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/folder1").getShareLink(SharingLinkKind.AnonymousView).then(((result: ShareLinkResponse) => {
    -
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/folder1").getShareLink(SharingLinkKind.AnonymousView, dateAdd(new Date(), "day", 5)).then((result: ShareLinkResponse) => {
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    - - -

    shareWith

    -

    Applies to: Item, Folder, File, Web

    -

    Shares the given resource with the specified permissions (View or Edit) and optionally sends an email to the users. You can supply a single string for the loginnames parameter or an array of loginnames. The folder method takes an optional parameter "shareEverything" which determines if the shared permissions are pushed down to all items in the folder, even those with unique permissions.

    -
    import { sp , SharingResult, SharingRole } from "@pnp/sp";
    -
    -sp.web.shareWith("i:0#.f|membership|user@site.com").then((result: SharingResult) => {
    -
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    -sp.web.shareWith("i:0#.f|membership|user@site.com", SharingRole.Edit).then((result: SharingResult) => {
    -
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/folder1").shareWith("i:0#.f|membership|user@site.com").then((result: SharingResult) => {
    -
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").shareWith("i:0#.f|membership|user@site.com", SharingRole.Edit, true, true).then((result: SharingResult) => {
    -
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    -sp.web.getFileByServerRelativeUrl("/sites/dev/Shared Documents/test.txt").shareWith("i:0#.f|membership|user@site.com").then((result: SharingResult) => {
    -
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    -sp.web.getFileByServerRelativeUrl("/sites/dev/Shared Documents/test.txt").shareWith("i:0#.f|membership|user@site.com", SharingRole.Edit).then((result: SharingResult) => {
    -
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    - - -

    shareObject & shareObjectRaw

    -

    Applies to: Web

    -

    Allows you to share any shareable object in a web by providing the appropriate parameters. These two methods differ in that shareObject will try and fix up your query based on the supplied parameters where shareObjectRaw will send your supplied json object directly to the server. The later method is provided for the greatest amount of flexibility.

    -
    import { sp , SharingResult, SharingRole } from "@pnp/sp";
    -
    -sp.web.shareObject("https://mysite.sharepoint.com/sites/dev/Docs/test.txt", "i:0#.f|membership|user@site.com", SharingRole.View).then((result: SharingResult) => {
    -
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    -sp.web.shareObjectRaw({
    -    url: "https://mysite.sharepoint.com/sites/dev/Docs/test.txt",
    -    peoplePickerInput: [{ Key: "i:0#.f|membership|user@site.com" }],
    -    roleValue: "role: 1973741327",
    -    groupId: 0,
    -    propagateAcl: false,
    -    sendEmail: true,
    -    includeAnonymousLinkInEmail: false,
    -    emailSubject: "subject",
    -    emailBody: "body",
    -    useSimplifiedRoles: true,
    -});
    -
    - - -

    unshareObject

    -

    Applies to: Web

    -
    import { sp , SharingResult } from "@pnp/sp";
    -
    -sp.web.unshareObject("https://mysite.sharepoint.com/sites/dev/Docs/test.txt").then((result: SharingResult) => {
    -
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    - - -

    checkSharingPermissions

    -

    Applies to: Item, Folder, File

    -

    Checks Permissions on the list of Users and returns back role the users have on the Item.

    -
    import { sp , SharingEntityPermission } from "@pnp/sp";
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").checkSharingPermissions([{ alias: "i:0#.f|membership|user@site.com" }]).then((result: SharingEntityPermission[]) => {
    -
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    - - -

    getSharingInformation

    -

    Applies to: Item, Folder, File

    -

    Get Sharing Information.

    -
    import { sp , SharingInformation } from "@pnp/sp";
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").getSharingInformation().then((result: SharingInformation) => {
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    - - -

    getObjectSharingSettings

    -

    Applies to: Item, Folder, File

    -

    Gets the sharing settings

    -
    import { sp , ObjectSharingSettings } from "@pnp/sp";
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").getObjectSharingSettings().then((result: ObjectSharingSettings) => {
    -
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    - - -

    unshare

    -

    Applies to: Item, Folder, File

    -

    Unshares a given resource

    -
    import { sp , SharingResult } from "@pnp/sp";
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").unshare().then((result: SharingResult) => {
    -
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    - - -

    deleteSharingLinkByKind

    -

    Applies to: Item, Folder, File

    -
    import { sp , SharingLinkKind, SharingResult } from "@pnp/sp";
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").deleteSharingLinkByKind(SharingLinkKind.AnonymousEdit).then((result: SharingResult) => {
    -
    -    console.log(result);
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    - - - -

    Applies to: Item, Folder, File

    -
    import { sp , SharingLinkKind } from "@pnp/sp";
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").unshareLink(SharingLinkKind.AnonymousEdit).then(_ => {
    -
    -    console.log("done");
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    -sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").unshareLink(SharingLinkKind.AnonymousEdit, "12345").then(_ => {
    -
    -    console.log("done");
    -}).catch(e => {
    -    console.error(e);
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/sitedesigns/index.html b/docs/v3/v1/sp/docs/sitedesigns/index.html deleted file mode 100644 index c2b2d9132..000000000 --- a/docs/v3/v1/sp/docs/sitedesigns/index.html +++ /dev/null @@ -1,1851 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Site Designs - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/sitedesigns

    -

    You can create site designs to provide reusable lists, themes, layouts, pages, or custom actions so that your users can quickly build new SharePoint sites with the features they need. -Check out SharePoint site design and site script overview for more information.

    -

    Site Designs

    -

    Create a new site design

    -
    import { sp } from "@pnp/sp";
    -
    -// WebTemplate: 64 Team site template, 68 Communication site template
    -const siteDesign = await sp.siteDesigns.createSiteDesign({
    -    SiteScriptIds: ["884ed56b-1aab-4653-95cf-4be0bfa5ef0a"],
    -    Title: "SiteDesign001",
    -    WebTemplate: "64",
    -});
    -
    -console.log(siteDesign.Title);
    -
    - - -

    Applying a site design to a site

    -
    import { sp } from "@pnp/sp";
    -
    -// Limited to 30 actions in a site script, but runs synchronously
    -await sp.siteDesigns.applySiteDesign("75b9d8fe-4381-45d9-88c6-b03f483ae6a8","https://contoso.sharepoint.com/sites/teamsite-pnpjs001");
    -
    -// Better use the following method for 300 actions in a site script
    -const task = await sp.web.addSiteDesignTask("75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
    -
    - - -

    Retrieval

    -
    import { sp } from "@pnp/sp";
    -
    -// Retrieving all site designs
    -const allSiteDesigns = await sp.siteDesigns.getSiteDesigns();
    -console.log(`Total site designs: ${allSiteDesigns.length}`);
    -
    -// Retrieving a single site design by Id
    -const siteDesign = await sp.siteDesigns.getSiteDesignMetadata("75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
    -console.log(siteDesign.Title);
    -
    - - -

    Update and delete

    -
    import { sp } from "@pnp/sp";
    -
    -// Update
    -const updatedSiteDesign = await sp.siteDesigns.updateSiteDesign({ Id: "75b9d8fe-4381-45d9-88c6-b03f483ae6a8", Title: "SiteDesignUpdatedTitle001" });
    -
    -// Delete
    -await sp.siteDesigns.deleteSiteDesign("75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
    -
    - - -

    Setting Rights/Permissions

    -
    import { sp } from "@pnp/sp";
    -
    -// Get
    -const rights = await sp.siteDesigns.getSiteDesignRights("75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
    -console.log(rights.length > 0 ? rights[0].PrincipalName : "");
    -
    -// Grant
    -await sp.siteDesigns.grantSiteDesignRights("75b9d8fe-4381-45d9-88c6-b03f483ae6a8", ["user@contoso.onmicrosoft.com"]);
    -
    -// Revoke
    -await sp.siteDesigns.revokeSiteDesignRights("75b9d8fe-4381-45d9-88c6-b03f483ae6a8", ["user@contoso.onmicrosoft.com"]);
    -
    -// Reset all view rights
    -const rights = await sp.siteDesigns.getSiteDesignRights("75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
    -await sp.siteDesigns.revokeSiteDesignRights("75b9d8fe-4381-45d9-88c6-b03f483ae6a8", rights.map(u => u.PrincipalName));
    -
    - - -

    Get a history of site designs that have run on a web

    -
    import { sp } from "@pnp/sp";
    -
    -const runs = await sp.web.getSiteDesignRuns();
    -const runs2 = await sp.siteDesigns.getSiteDesignRun("https://TENANT.sharepoint.com/sites/mysite");
    -
    -// Get runs specific to a site design
    -const runs3 = await sp.web.getSiteDesignRuns("75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
    -const runs4 = await sp.siteDesigns.getSiteDesignRun("https://TENANT.sharepoint.com/sites/mysite", "75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
    -
    -// For more information about the site script actions
    -const runStatus = await sp.web.getSiteDesignRunStatus(runs[0].ID);
    -const runStatus2 = await sp.siteDesigns.getSiteDesignRunStatus("https://TENANT.sharepoint.com/sites/mysite", runs[0].ID);
    -
    - - -

    Site Scripts

    -

    Create a new site script

    -
    import { sp } from "@pnp/sp";
    -
    -const sitescriptContent = {
    -    "$schema": "schema.json",
    -    "actions": [
    -        {
    -            "themeName": "Theme Name 123",
    -            "verb": "applyTheme",
    -        },
    -    ],
    -    "bindata": {},
    -    "version": 1,
    -};
    -
    -const siteScript = await sp.siteScripts.createSiteScript("Title", "description", sitescriptContent);
    -
    -console.log(siteScript.Title);
    -
    - - -

    Retrieval

    -
    import { sp } from "@pnp/sp";
    -
    -// Retrieving all site scripts
    -const allSiteScripts = await sp.siteScripts.getSiteScripts();
    -console.log(allSiteScripts.length > 0 ? allSiteScripts[0].Title : "");
    -
    -// Retrieving a single site script by Id
    -const siteScript = await sp.siteScripts.getSiteScriptMetadata("884ed56b-1aab-4653-95cf-4be0bfa5ef0a");
    -console.log(siteScript.Title);
    -
    - - -

    Update and delete

    -
    import { sp } from "@pnp/sp";
    -
    -// Update
    -const updatedSiteScript = await sp.siteScripts.updateSiteScript({ Id: "884ed56b-1aab-4653-95cf-4be0bfa5ef0a", Title: "New Title" });
    -console.log(updatedSiteScript.Title);
    -
    -// Delete
    -await sp.siteScripts.deleteSiteScript("884ed56b-1aab-4653-95cf-4be0bfa5ef0a");
    -
    - - -

    Get site script from a list

    -
    import { sp } from "@pnp/sp";
    -
    -// Using the absolute URL of the list
    -const ss = await sp.siteScripts.getSiteScriptFromList("https://TENANT.sharepoint.com/Lists/mylist");
    -
    -// Using the PnPjs web object to fetch the site script from a specific list
    -const ss2 = await sp.web.lists.getByTitle("mylist").getSiteScript();
    -
    - - -

    Get site script from a web

    -
    import { sp } from "@pnp/sp";
    -
    -const extractInfo = {
    -    IncludeBranding: true,
    -    IncludeLinksToExportedItems: true,
    -    IncludeRegionalSettings: true,
    -    IncludeSiteExternalSharingCapability: true,
    -    IncludeTheme: true,
    -    IncludedLists: ["Lists/MyList"]
    -};
    -
    -const ss = await sp.siteScripts.getSiteScriptFromWeb("https://TENANT.sharepoint.com/sites/mysite", extractInfo);
    -
    -// Using the PnPjs web object to fetch the site script from a specific web
    -const ss2 = await sp.web.getSiteScript(extractInfo);
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/sites/index.html b/docs/v3/v1/sp/docs/sites/index.html deleted file mode 100644 index 863050ded..000000000 --- a/docs/v3/v1/sp/docs/sites/index.html +++ /dev/null @@ -1,2186 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Sites - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - -

    @pnp/sp/site - Site properties

    -

    Site collection are one of the fundamental entry points while working with SharePoint. Sites serve as container for webs, lists, features and other entity types.

    -

    Get context information for the current site collection

    -

    Using the library, you can get the context information of the current site collection

    -
    import { sp } from "@pnp/sp";
    -
    -sp.site.getContextInfo().then(d =>{
    -       console.log(d.FormDigestValue); 
    -});
    -
    - - -

    Get document libraries of a web

    -

    Using the library, you can get a list of the document libraries present in the a given web.

    -

    Note: Works only in SharePoint online

    -
    import { sp } from "@pnp/sp";
    -
    -sp.site.getDocumentLibraries("https://tenant.sharepoint.com/sites/test/subsite").then((d:DocumentLibraryInformation[]) => {
    -    // iterate over the array of doc lib
    -});
    -
    - - -

    Open Web By Id

    -

    Because this method is a POST request you can chain off it directly. You will get back the full web properties in the data property of the return object. You can also chain directly off the returned Web instance on the web property.

    -
    sp.site.openWebById("111ca453-90f5-482e-a381-cee1ff383c9e").then(w => {
    -
    -    //we got all the data from the web as well
    -    console.log(w.data);
    -
    -    // we can chain
    -    w.web.select("Title").get().then(w2 => {
    -        // ...
    -    });
    -});
    -
    - - -

    Get site collection url from page

    -

    Using the library, you can get the site collection url by providing a page url

    -
    import { sp } from "@pnp/sp";
    -
    -sp.site.getWebUrlFromPageUrl("https://tenant.sharepoint.com/sites/test/Pages/test.aspx").then(d => {
    -        console.log(d);
    -});
    -
    - - -

    Join a hub site

    -

    Added in 1.2.4

    -

    Note: Works only in SharePoint online

    -

    Join the current site collection to a hub site collection

    -
    import { sp, Site } from "@pnp/sp";
    -
    -var site = new Site("https://tenant.sharepoint.com/sites/HubSite/");
    -
    -var hubSiteID = "";
    -
    -site.select("ID").get().then(d => {
    -    // get ID of the hub site collection
    -    hubSiteID = d.Id;
    -
    -    // associate the current site collection the hub site collection
    -    sp.site.joinHubSite(hubSiteID).then(d => {
    -        console.log(d);
    -    });
    -
    -});
    -
    - - -

    Disassociate the current site collection from a hub site collection

    -

    Added in 1.2.4

    -

    Note: Works only in SharePoint online

    -
    import { sp } from "@pnp/sp";
    -
    -sp.site.joinHubSite("00000000-0000-0000-0000-000000000000").then(d => {
    -    console.log(d);
    -});
    -
    - - -

    Register a hub site

    -

    Added in 1.2.4

    -

    Note: Works only in SharePoint online

    -

    Registers the current site collection as a hub site collection

    -
    import { sp } from "@pnp/sp";
    -
    -sp.site.registerHubSite().then(d => {
    -    console.log(d);
    -});
    -
    - - -

    Un-Register a hub site

    -

    Added in 1.2.4

    -

    Note: Works only in SharePoint online

    -

    Un-Registers the current site collection as a hub site collection

    -
    import { sp } from "@pnp/sp";
    -
    -sp.site.unRegisterHubSite().then(d => {
    -    console.log(d);
    -});
    -
    - - -

    Create a modern communication site

    -

    Added in 1.2.6

    -

    Note: Works only in SharePoint online

    -

    Creates a modern communication site.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PropertyTypeRequiredDescription
    TitlestringyesThe title of the site to create.
    lcidnumberyesThe default language to use for the site.
    shareByEmailEnabledbooleanyesIf set to true, it will enable sharing files via Email. By default it is set to false
    urlstringyesThe fully qualified URL (e.g. https://yourtenant.sharepoint.com/sites/mysitecollection) of the site.
    descriptionstringnoThe description of the communication site.
    classificationstringnoThe Site classification to use. For instance 'Contoso Classified'. See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information
    siteDesignIdstringnoThe Guid of the site design to be used.
    You can use the below default OOTB GUIDs:
    Topic: null
    Showcase: 6142d2a0-63a5-4ba0-aede-d9fefca2c767
    Blank: f6cc5403-0d63-442e-96c0-285923709ffc
    hubSiteIdstringnoThe Guid of the already existing Hub site
    ownerstringnoRequired when using app-only context. Owner principal name e.g. user@tenant.onmicrosoft.com
    -
    import { sp } from "@pnp/sp";
    -
    -const s = await sp.site.createCommunicationSite(
    -            "Title",
    -            1033,
    -            true,
    -            "https://tenant.sharepoint.com/sites/commSite",
    -            "Description",
    -            "HBI",
    -            "f6cc5403-0d63-442e-96c0-285923709ffc",
    -            "a00ec589-ea9f-4dba-a34e-67e78d41e509",
    -            "user@TENANT.onmicrosoft.com");
    -
    - - -

    Create a modern team site

    -

    Added in 1.2.6

    -

    Note: Works only in SharePoint online. It wont work with App only tokens

    -

    Creates a modern team site backed by O365 group.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PropertyTypeRequiredDescription
    displayNamestringyesThe title/displayName of the site to be created.
    aliasstringyesAlias of the underlying Office 365 Group.
    isPublicbooleanyesDefines whether the Office 365 Group will be public (default), or private.
    lcidnumberyesThe language to use for the site. If not specified will default to English (1033).
    descriptionstringnoThe description of the modern team site.
    classificationstringnoThe Site classification to use. For instance 'Contoso Classified'. See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information
    ownersstring array (string[])noThe Owners of the site to be created
    hubSiteIdstringnoThe Guid of the already existing Hub site
    -
    import { sp } from "@pnp/sp";
    -
    -sp.site.createModernTeamSite(
    -        "displayName",
    -        "alias",
    -        true,
    -        1033,
    -        "description",
    -        "HBI",
    -        ["user1@tenant.onmicrosoft.com","user2@tenant.onmicrosoft.com","user3@tenant.onmicrosoft.com"],
    -        "a00ec589-ea9f-4dba-a34e-67e78d41e509")
    -        .then(d => {
    -            console.log(d);
    -        });
    -
    - - -

    Delete a site collection

    -
    import { sp } from "@pnp/sp";
    -
    -// Delete the current site
    -await sp.site.delete();
    -
    -// Specify which site to delete
    -const siteUrl = "https://tenant.sharepoint.com/sites/tstpnpsitecoldelete5";
    -const site2 = new Site(siteUrl);
    -await site2.delete();
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/social/index.html b/docs/v3/v1/sp/docs/social/index.html deleted file mode 100644 index eaf0886cf..000000000 --- a/docs/v3/v1/sp/docs/social/index.html +++ /dev/null @@ -1,2011 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Social - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/social

    -

    The social API allows you to track followed sites, people, and docs. Note, many of these methods only work with the context of a logged in user, and not -with app-only permissions.

    -

    getFollowedSitesUri

    -

    Gets a URI to a site that lists the current user's followed sites.

    -
    import { sp } from "@pnp/sp";
    -
    -const uri = await sp.social.getFollowedSitesUri();
    -
    - - -

    getFollowedDocumentsUri

    -

    Gets a URI to a site that lists the current user's followed documents.

    -
    import { sp } from "@pnp/sp";
    -
    -const uri = await sp.social.getFollowedDocumentsUri();
    -
    - - -

    follow

    -

    Makes the current user start following a user, document, site, or tag

    -
    import { sp, SocialActorType } from "@pnp/sp";
    -
    -// follow a site
    -const r1 = await sp.social.follow({
    -    ActorType: SocialActorType.Site,
    -    ContentUri: "htts://tenant.sharepoint.com/sites/site",
    -});
    -
    -// follow a person
    -const r2 = await sp.social.follow({
    -    AccountName: "i:0#.f|membership|person@tenant.com",
    -    ActorType: SocialActorType.User,
    -});
    -
    -// follow a doc
    -const r3 = await sp.social.follow({
    -    ActorType: SocialActorType.Document,
    -    ContentUri: "https://tenant.sharepoint.com/sites/dev/SitePages/Test.aspx",
    -});
    -
    -// follow a tag
    -// You need the tag GUID to start following a tag.
    -// You can't get the GUID by using the REST service, but you can use the .NET client object model or the JavaScript object model.
    -// See How to get a tag's GUID based on the tag's name by using the JavaScript object model.
    -// https://docs.microsoft.com/en-us/sharepoint/dev/general-development/follow-content-in-sharepoint#bk_getTagGuid
    -const r4 = await sp.social.follow({
    -    ActorType: SocialActorType.Tag,
    -    TagGuid: "19a4a484-c1dc-4bc5-8c93-bb96245ce928",
    -});
    -
    - - -

    isFollowed

    -

    Indicates whether the current user is following a specified user, document, site, or tag

    -
    import { sp, SocialActorType } from "@pnp/sp";
    -
    -// pass the same social actor struct as shown in follow example for each type
    -const r = await sp.social.isFollowed({
    -    AccountName: "i:0#.f|membership|person@tenant.com",
    -    ActorType: SocialActorType.User,
    -});
    -
    - - -

    stopFollowing

    -

    Makes the current user stop following a user, document, site, or tag

    -
    import { sp, SocialActorType } from "@pnp/sp";
    -
    -// pass the same social actor struct as shown in follow example for each type
    -const r = await sp.social.stopFollowing({
    -    AccountName: "i:0#.f|membership|person@tenant.com",
    -    ActorType: SocialActorType.User,
    -});
    -
    - - -

    my

    -

    get

    -

    Gets this user's social information

    -
    import { sp } from "@pnp/sp";
    -
    -const r = await sp.social.my.get();
    -
    - - -

    followed

    -

    Gets users, documents, sites, and tags that the current user is following based on the supplied flags.

    -
    import { sp, SocialActorTypes } from "@pnp/sp";
    -
    -// get all the followed documents
    -const r1 = await sp.social.my.followed(SocialActorTypes.Document);
    -
    -// get all the followed documents and sites
    -const r2 = await sp.social.my.followed(SocialActorTypes.Document | SocialActorTypes.Site);
    -
    -// get all the followed sites updated in the last 24 hours
    -const r3 = await sp.social.my.followed(SocialActorTypes.Site | SocialActorTypes.WithinLast24Hours);
    -
    - - -

    followedCount

    -

    Works as followed but returns on the count of actors specifed by the query

    -
    import { sp, SocialActorTypes } from "@pnp/sp";
    -
    -// get the followed documents count
    -const r = await sp.social.my.followedCount(SocialActorTypes.Document);
    -
    - - -

    followers

    -

    Gets the users who are following the current user.

    -
    import { sp } from "@pnp/sp";
    -
    -// get the followed documents count
    -const r = await sp.social.my.followers();
    -
    - - -

    suggestions

    -

    Gets users who the current user might want to follow.

    -
    import { sp } from "@pnp/sp";
    -
    -// get the followed documents count
    -const r = await sp.social.my.suggestions();
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/sp-utilities-utility/index.html b/docs/v3/v1/sp/docs/sp-utilities-utility/index.html deleted file mode 100644 index e3d52def5..000000000 --- a/docs/v3/v1/sp/docs/sp-utilities-utility/index.html +++ /dev/null @@ -1,2061 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SP.Utilities.Utility - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - -

    @pnp/sp/utilities

    -

    Through the REST api you are able to call a subset of the SP.Utilities.Utility methods. We have explicitly defined some of these methods and provided a method to call any others in a generic manner. These methods are exposed on pnp.sp.utility and support batching and caching.

    -

    sendEmail

    -

    This methods allows you to send an email based on the supplied arguments. The method takes a single argument, a plain object defined by the EmailProperties interface (shown below).

    -

    EmailProperties

    -
    export interface EmailProperties {
    -
    -    To: string[];
    -    CC?: string[];
    -    BCC?: string[];
    -    Subject: string;
    -    Body: string;
    -    AdditionalHeaders?: TypedHash<string>;
    -    From?: string;
    -}
    -
    - - -

    Usage

    -

    You must define the To, Subject, and Body values - the remaining are optional.

    -
    import { sp, EmailProperties } from "@pnp/sp";
    -
    -const emailProps: EmailProperties = {
    -    To: ["user@site.com"],
    -    CC: ["user2@site.com", "user3@site.com"],
    -    Subject: "This email is about...",
    -    Body: "Here is the body. <b>It supports html</b>",
    -};
    -
    -sp.utility.sendEmail(emailProps).then(_ => {
    -
    -    console.log("Email Sent!");
    -});
    -
    - - -

    getCurrentUserEmailAddresses

    -

    This method returns the current user's email addresses known to SharePoint.

    -
    import { sp } from "@pnp/sp";
    -
    -sp.utility.getCurrentUserEmailAddresses().then((addressString: string) => {
    -
    -    console.log(addressString);
    -});
    -
    - - -

    resolvePrincipal

    -

    Gets information about a principal that matches the specified Search criteria

    -
    import { sp , PrincipalType, PrincipalSource, PrincipalInfo } from "@pnp/sp";
    -
    -sp.utility.resolvePrincipal("user@site.com",
    -    PrincipalType.User,
    -    PrincipalSource.All,
    -    true,
    -    false).then((principal: PrincipalInfo) => {
    -
    -
    -        console.log(principal);
    -    });
    -
    - - -

    searchPrincipals

    -

    Gets information about the principals that match the specified Search criteria.

    -
    import { sp , PrincipalType, PrincipalSource, PrincipalInfo } from "@pnp/sp";
    -
    -sp.utility.searchPrincipals("john",
    -    PrincipalType.User,
    -    PrincipalSource.All,
    -    "",
    -    10).then((principals: PrincipalInfo[]) => {
    -
    -        console.log(principals);
    -    });
    -
    - - -

    createEmailBodyForInvitation

    -

    Gets the external (outside the firewall) URL to a document or resource in a site.

    -
    import { sp } from "@pnp/sp";
    -
    -sp.utility.createEmailBodyForInvitation("https://contoso.sharepoint.com/sites/dev/SitePages/DevHome.aspx").then((r: string) => {
    -
    -    console.log(r);
    -});
    -
    - - -

    expandGroupsToPrincipals

    -

    Resolves the principals contained within the supplied groups

    -
    import { sp , PrincipalInfo } from "@pnp/sp";
    -
    -sp.utility.expandGroupsToPrincipals(["Dev Owners", "Dev Members"]).then((principals: PrincipalInfo[]) => {
    -
    -    console.log(principals);
    -});
    -
    -// optionally supply a max results count. Default is 30.
    -sp.utility.expandGroupsToPrincipals(["Dev Owners", "Dev Members"], 10).then((principals: PrincipalInfo[]) => {
    -
    -    console.log(principals);
    -});
    -
    - - -

    createWikiPage

    -
    import { sp , CreateWikiPageResult } from "@pnp/sp";
    -
    -sp.utility.createWikiPage({
    -    ServerRelativeUrl: "/sites/dev/SitePages/mynewpage.aspx",
    -    WikiHtmlContent: "This is my <b>page</b> content. It supports rich html.",
    -}).then((result: CreateWikiPageResult) => {
    -
    -    // result contains the raw data returned by the service
    -    console.log(result.data);
    -
    -    // result contains a File instance you can use to further update the new page
    -    result.file.get().then(f => {
    -
    -        console.log(f);
    -    });
    -});
    -
    - - -

    containsInvalidFileFolderChars

    -

    Checks if file or folder name contains invalid characters

    -
    import { sp } from "@pnp/sp";
    -
    -const isInvalid = sp.utility.containsInvalidFileFolderChars("Filename?.txt");
    -console.log(isInvalid); // true
    -
    - - -

    stripInvalidFileFolderChars

    -

    Removes invalid characters from file or folder name

    -
    import { sp } from "@pnp/sp";
    -
    -const validName = sp.utility.stripInvalidFileFolderChars("Filename?.txt");
    -console.log(validName); // Filename.txt
    -
    - - -

    Call Other Methods

    -

    Even if a method does not have an explicit implementation on the utility api you can still call it using the UtilityMethod class. In this example we will show calling the GetLowerCaseString method, but the technique works for any of the utility methods.

    -
    import { UtilityMethod } from "@pnp/sp";
    -
    -// the first parameter is the web url. You can use an empty string for the current web,
    -// or specify it to call other web's. The second parameter is the method name.
    -const method = new UtilityMethod("", "GetLowerCaseString");
    -
    -// you must supply the correctly formatted parameters to the execute method which
    -// is generic and types the result as the supplied generic type parameter.
    -method.excute<string>({
    -    sourceValue: "HeRe IS my StrINg",
    -    lcid: 1033,
    -}).then((s: string) => {
    -
    -    console.log(s);
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/tenant-properties/index.html b/docs/v3/v1/sp/docs/tenant-properties/index.html deleted file mode 100644 index b16248f46..000000000 --- a/docs/v3/v1/sp/docs/tenant-properties/index.html +++ /dev/null @@ -1,1795 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Tenant Properties - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/web - tenant properties

    -

    You can set, read, and remove tenant properties using the methods shown below:

    -

    setStorageEntity

    -

    This method MUST be called in the context of the app catalog web or you will get an access denied message.

    -
    import { Web } from "@pnp/sp";
    -
    -const w = new Web("https://tenant.sharepoint.com/sites/appcatalog/");
    -
    -// specify required key and value
    -await w.setStorageEntity("Test1", "Value 1");
    -
    -// specify optional description and comments
    -await w.setStorageEntity("Test2", "Value 2", "description", "comments");
    -
    - - -

    getStorageEntity

    -

    This method can be used from any web to retrieve values previsouly set.

    -
    import { sp, StorageEntity } from "@pnp/sp";
    -
    -const prop: StorageEntity = await sp.web.getStorageEntity("Test1");
    -
    -console.log(prop.Value);
    -
    - - -

    removeStorageEntity

    -

    This method MUST be called in the context of the app catalog web or you will get an access denied message.

    -
    import { Web } from "@pnp/sp";
    -
    -const w = new Web("https://tenant.sharepoint.com/sites/appcatalog/");
    -
    -await w.removeStorageEntity("Test1");
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/views/index.html b/docs/v3/v1/sp/docs/views/index.html deleted file mode 100644 index 3d8bdc89b..000000000 --- a/docs/v3/v1/sp/docs/views/index.html +++ /dev/null @@ -1,1869 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Views - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - -

    @pnp/sp/views

    -

    Views define the columns, ordering, and other details we see when we look at a list. You can have multiple views for a list, including private views - and one default view.

    -

    Get a View's Properties

    -

    To get a views properties you need to know it's id or title. You can use the standard OData operators as expected to select properties. For a list of the properties, please see this article.

    -
    import { sp } from "@pnp/sp";
    -// know a view's GUID id
    -sp.web.lists.getByTitle("Documents").getView("2B382C69-DF64-49C4-85F1-70FB9CECACFE").select("Title").get().then(v => {
    -
    -    console.log(v);
    -});
    -
    -// get by the display title of the view
    -sp.web.lists.getByTitle("Documents").views.getByTitle("All Documents").select("Title").get().then(v => {
    -
    -    console.log(v);
    -});
    -
    - - -

    Add a View

    -

    To add a view you use the add method of the views collection. You must supply a title and can supply other parameters as well.

    -
    import { sp, ViewAddResult } from "@pnp/sp";
    -// create a new view with default fields and properties
    -sp.web.lists.getByTitle("Documents").views.add("My New View").then(v => {
    -
    -    console.log(v);
    -});
    -
    -// create a new view with specific properties
    -sp.web.lists.getByTitle("Documents").views.add("My New View 2", false, {
    -
    -    RowLimit: 10,
    -    ViewQuery: "<OrderBy><FieldRef Name='Modified' Ascending='False' /></OrderBy>",
    -}).then((v: ViewAddResult) => {
    -
    -    // manipulate the view's fields
    -    v.view.fields.removeAll().then(_ => {
    -
    -        Promise.all([
    -            v.view.fields.add("Title"),
    -            v.view.fields.add("Modified"),
    -        ]).then(_ =>{
    -
    -            console.log("View created");
    -        });
    -    });
    -});
    -
    - - -

    Update a View

    -
    import { sp, ViewUpdateResult } from "@pnp/sp";
    -
    -sp.web.lists.getByTitle("Documents").views.getByTitle("My New View").update({
    -    RowLimit: 20,
    -}).then((v: ViewUpdateResult) => {
    -
    -    console.log(v);
    -});
    -
    - - -

    Set View XML

    -

    Added in 1.2.6

    -
    import { sp } from "@pnp/sp";
    -
    -const viewXml: string = "...";
    -
    -await sp.web.lists.getByTitle("Documents").views.getByTitle("My New View").setViewXml(viewXml);
    -
    - - -

    Delete a View

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.lists.getByTitle("Documents").views.getByTitle("My New View").delete().then(_ => {
    -
    -    console.log("View deleted");
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v1/sp/docs/webs/index.html b/docs/v3/v1/sp/docs/webs/index.html deleted file mode 100644 index 95d106c26..000000000 --- a/docs/v3/v1/sp/docs/webs/index.html +++ /dev/null @@ -1,1972 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Webs - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -
    - -
    - -
    - - - - - - - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    - -
    - - -
    -
    - - - - - -

    @pnp/sp/webs

    -

    Webs are one of the fundamental entry points when working with SharePoint. Webs serve as a container for lists, features, sub-webs, and all of the entity types.

    -

    Add a Web

    -

    Using the library you can add a web to another web's collection of subwebs. The basic usage requires only a title and url. This will result in a team site with all of the default settings.

    -
    import { sp, WebAddResult } from "@pnp/sp";
    -
    -sp.web.webs.add("title", "subweb1").then((w: WebAddResult) => {
    -
    -    // show the response from the server when adding the web
    -    console.log(w.data);
    -
    -    w.web.select("Title").get().then(w => {
    -
    -        // show our title
    -        console.log(w.Title);
    -    });
    -});
    -
    - - -

    You can also provide other settings such as description, template, language, and inherit permissions.

    -
    import { sp, WebAddResult } from "@pnp/sp";
    -
    -// create a German language wiki site with title, url, description, which inherits permissions
    -sp.web.webs.add("wiki", "subweb2", "a wiki web", "WIKI#0", 1031, true).then((w: WebAddResult) => {
    -
    -    // show the response from the server when adding the web
    -    console.log(w.data);
    -
    -    w.web.select("Title").get().then(w => {
    -
    -        // show our title
    -        console.log(w.Title);
    -    });
    -});
    -
    - - -

    Create Default Associated Groups

    -

    If you create a web that doesn't inherit permissions from the parent web, you can create its default associated groups (Members, Owners, Visitors) with the default role assigments (Contribute, Full Control, Read)

    -
    import { sp, WebAddResult } from "@pnp/sp";
    -
    -sp.web.webs.add("title", "subweb1", "a wiki web", "WIKI#0", 1031, false).then((w: WebAddResult) => {
    -
    -    w.web.createDefaultAssociatedGroups().then(() => {
    -
    -        // ...
    -    });
    -});
    -
    - - -

    Get A Web's properties

    -
    import { sp } from "@pnp/sp";
    -
    -// basic get of the webs properties
    -sp.web.get().then(w => {
    -
    -    console.log(w.Title);
    -});
    -
    -// use odata operators to get specific fields
    -sp.web.select("Title").get().then(w => {
    -
    -    console.log(w.Title);
    -});
    -
    -// use with get to give the result a type
    -sp.web.select("Title").get<{ Title: string }>().then(w => {
    -
    -    console.log(w.Title);
    -});
    -
    - - -

    Get Complex Properties

    -

    Some properties, such as AllProperties, are not returned by default. You can still access them using the expand operator.

    -
    import { sp } from "@pnp/sp";
    -
    -sp.web.select("AllProperties").expand("AllProperties").get().then(w => {
    -
    -    console.log(w.AllProperties);
    -});
    -
    - - -

    Get a Web Directly

    -

    You can also use the Web object directly to get any web, though of course the current user must have the necessary permissions. This is done by importing the web object.

    -
    import { Web } from "@pnp/sp";
    -
    -let web = new Web("https://my-tenant.sharepoint.com/sites/mysite");
    -
    -web.get().then(w => {
    -
    -    console.log(w);
    -});
    -
    - - -

    Open Web By Id

    -

    Because this method is a POST request you can chain off it directly. You will get back the full web properties in the data property of the return object. You can also chain directly off the returned Web instance on the web property.

    -
    sp.site.openWebById("111ca453-90f5-482e-a381-cee1ff383c9e").then(w => {
    -
    -    //we got all the data from the web as well
    -    console.log(w.data);
    -
    -    // we can chain
    -    w.web.select("Title").get().then(w2 => {
    -        // ...
    -    });
    -});
    -
    - - -

    Update Web Properties

    -

    You can update web properties using the update method. The properties available for update are listed in this table. Updating is a simple as passing a plain object with the properties you want to update.

    -
    import { Web } from "@pnp/sp";
    -
    -let web = new Web("https://my-tenant.sharepoint.com/sites/mysite");
    -
    -web.update({
    -    Title: "New Title",
    -    CustomMasterUrl: "{path to masterpage}",
    -    Description: "My new description",
    -}).then(w => {
    -
    -    console.log(w);
    -});
    -
    - - -

    Delete a Web

    -
    import { Web } from "@pnp/sp";
    -
    -let web = new Web("https://my-tenant.sharepoint.com/sites/mysite");
    -
    -web.delete().then(w => {
    -
    -    console.log(w);
    -});
    -
    - - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - spacer - - - \ No newline at end of file diff --git a/docs/v3/v2/404.html b/docs/v3/v2/404.html deleted file mode 100644 index c147c3f0f..000000000 --- a/docs/v3/v2/404.html +++ /dev/null @@ -1,2108 +0,0 @@ - - - - - - - - - - - - - - - - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - -
    - -
    -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - -
    -
    - -

    404 - Not found

    - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/SPFx-on-premises/index.html b/docs/v3/v2/SPFx-on-premises/index.html deleted file mode 100644 index 3b65b82c4..000000000 --- a/docs/v3/v2/SPFx-on-premises/index.html +++ /dev/null @@ -1,2278 +0,0 @@ - - - - - - - - - - - - - - - - - - SPFx On-Premises - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Workaround for on-premises SPFx TypeScript Version (SharePoint 2016 or 2019)

    -

    Note this article applies to version 1.4.1 SharePoint Framework projects targeting on-premises only. Also we have had reports that after version 2.0.9 of hte library this workaround no longer works.

    -

    When using the Yeoman generator to create a SharePoint Framework 1.4.1 project targeting on-premises it installs TypeScript version 2.2.2 (SP2016) or 2.4.2/2.4.1 (SP2019). Unfortunately this library relies on 3.6.4 or later due to extensive use of default values for generic type parameters in the libraries. To work around this limitation you can follow the steps in this article.

    -
    npm i
    -npm i -g rimraf # used to remove the node_modules folder (much better/faster)
    -
    -
      -
    1. Ensure that the @pnp/sp package is already installed npm i @pnp/sp
    2. -
    3. Remove the package-lock.json file & node_modules rimraf node_modules folder and execute npm install
    4. -
    5. Open package-lock.json from the root folder
    6. -
    7. Search for "typescript" or similar with version 2.4.1 (SP2019) 2.2.2 (SP2016)
    8. -
    9. Replace "2.4.1" or "2.2.2" with "3.6.4"
    10. -
    11. -

      Search for the next "typescript" occurrence and replace the block with:

      -

      JSON -"typescript": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz", - "integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==", - "dev": true -}

      -
    12. -
    13. -

      Remove node_modules folder rimraf node_modules

      -
    14. -
    15. Run npm install
    16. -
    -

    Alternative using npm-force-resolutions

    -
      -
    1. -

      Install resolutions package and TypeScript providing considered version explicitly:

      -

      bash -npm i -D npm-force-resolutions typescript@3.6.4

      -
    2. -
    3. -

      Add a resolution for TypeScript and preinstall script into package.json to a corresponding code blocks:

      -

      JSON -{ - "scripts": { - "preinstall": "npx npm-force-resolutions" - }, - "resolutions": { - "typescript": "3.6.4" - } -}

      -
    4. -
    5. -

      Run npm install to trigger preinstall script and bumping TypeScript version into package-lock.json

      -
    6. -
    7. Run npm run build, should produce no errors
    8. -
    -

    Installing additional dependencies should be safe then.

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/_theme/main.html b/docs/v3/v2/_theme/main.html deleted file mode 100644 index 2f621a908..000000000 --- a/docs/v3/v2/_theme/main.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "base.html" %} - -{% block analytics %} - spacer -{% endblock %} diff --git a/docs/v3/v2/adaljsclient/adalclient/index.html b/docs/v3/v2/adaljsclient/adalclient/index.html deleted file mode 100644 index 7df21dfb6..000000000 --- a/docs/v3/v2/adaljsclient/adalclient/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Redirecting... - - - - - - -Redirecting... - - diff --git a/docs/v3/v2/adaljsclient/index.html b/docs/v3/v2/adaljsclient/index.html deleted file mode 100644 index 56ac110b8..000000000 --- a/docs/v3/v2/adaljsclient/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Redirecting... - - - - - - -Redirecting... - - diff --git a/docs/v3/v2/assets/images/favicon.png b/docs/v3/v2/assets/images/favicon.png deleted file mode 100644 index 1cf13b9f9d978896599290a74f77d5dbe7d1655c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1870 zcmV-U2eJ5xP)Gc)JR9QMau)O=X#!i9;T z37kk-upj^(fsR36MHs_+1RCI)NNu9}lD0S{B^g8PN?Ww(5|~L#Ng*g{WsqleV}|#l zz8@ri&cTzw_h33bHI+12+kK6WN$h#n5cD8OQt`5kw6p~9H3()bUQ8OS4Q4HTQ=1Ol z_JAocz`fLbT2^{`8n~UAo=#AUOf=SOq4pYkt;XbC&f#7lb$*7=$na!mWCQ`dBQsO0 zLFBSPj*N?#u5&pf2t4XjEGH|=pPQ8xh7tpx;US5Cx_Ju;!O`ya-yF`)b%TEt5>eP1ZX~}sjjA%FJF?h7cX8=b!DZl<6%Cv z*G0uvvU+vmnpLZ2paivG-(cd*y3$hCIcsZcYOGh{$&)A6*XX&kXZd3G8m)G$Zz-LV z^GF3VAW^Mdv!)4OM8EgqRiz~*Cji;uzl2uC9^=8I84vNp;ltJ|q-*uQwGp2ma6cY7 z;`%`!9UXO@fr&Ebapfs34OmS9^u6$)bJxrucutf>`dKPKT%%*d3XlFVKunp9 zasduxjrjs>f8V=D|J=XNZp;_Zy^WgQ$9WDjgY=z@stwiEBm9u5*|34&1Na8BMjjgf3+SHcr`5~>oz1Y?SW^=K z^bTyO6>Gar#P_W2gEMwq)ot3; zREHn~U&Dp0l6YT0&k-wLwYjb?5zGK`W6S2v+K>AM(95m2C20L|3m~rN8dprPr@t)5lsk9Hu*W z?pS990s;Ez=+Rj{x7p``4>+c0G5^pYnB1^!TL=(?HLHZ+HicG{~4F1d^5Awl_2!1jICM-!9eoLhbbT^;yHcefyTAaqRcY zmuctDopPT!%k+}x%lZRKnzykr2}}XfG_ne?nRQO~?%hkzo;@RN{P6o`&mMUWBYMTe z6i8ChtjX&gXl`nvrU>jah)2iNM%JdjqoaeaU%yVn!^70x-flljp6Q5tK}5}&X8&&G zX3fpb3E(!rH=zVI_9Gjl45w@{(ITqngWFe7@9{mX;tO25Z_8 zQHEpI+FkTU#4xu>RkN>b3Tnc3UpWzPXWm#o55GKF09j^Mh~)K7{QqbO_~(@CVq! zS<8954|P8mXN2MRs86xZ&Q4EfM@JB94b=(YGuk)s&^jiSF=t3*oNK3`rD{H`yQ?d; ztE=laAUoZx5?RC8*WKOj`%LXEkgDd>&^Q4M^z`%u0rg-It=hLCVsq!Z%^6eB-OvOT zFZ28TN&cRmgU}Elrnk43)!>Z1FCPL2K$7}gwzIc48NX}#!A1BpJP?#v5wkNprhV** z?Cpalt1oH&{r!o3eSKc&ap)iz2BTn_VV`4>9M^b3;(YY}4>#ML6{~(4mH+?%07*qo IM6N<$f(jP3KmY&$ diff --git a/docs/v3/v2/assets/javascripts/bundle.9554a270.min.js b/docs/v3/v2/assets/javascripts/bundle.9554a270.min.js deleted file mode 100644 index a2089af3a..000000000 --- a/docs/v3/v2/assets/javascripts/bundle.9554a270.min.js +++ /dev/null @@ -1,2 +0,0 @@ -!function(e,t){for(var n in t)e[n]=t[n]}(window,function(e){function t(t){for(var c,o,i=t[0],u=t[1],b=t[2],f=0,O=[];f"focus"===e),Object(d.a)(e===b()))}function m(e){return{x:e.scrollLeft,y:e.scrollTop}}function v(e){return Object(j.a)(Object(r.a)(e,"scroll"),Object(r.a)(window,"resize")).pipe(Object(p.a)(()=>m(e)),Object(d.a)(m(e)))}function g(e){if(!(e instanceof HTMLInputElement))throw new Error("Not implemented");e.select()}var $=n(61),y=n(32),w=n(62),x=n(46),S=n(82),C=n(20),T=n(64),k=n(75),E=n(65),A=n(83);const _=new y.a,L=Object(w.a)(()=>Object(x.a)(new $.a(e=>{for(const t of e)_.next(t)}))).pipe(Object(C.a)(e=>Object(j.a)(Object(x.a)(e),S.a).pipe(Object(T.a)(()=>e.disconnect()))),Object(k.a)({bufferSize:1,refCount:!0}));function R(e){return L.pipe(Object(E.a)(t=>t.observe(e)),Object(C.a)(t=>_.pipe(Object(A.a)(({target:t})=>t===e),Object(T.a)(()=>t.unobserve(e)),Object(p.a)(({contentRect:e})=>({width:e.width,height:e.height})))),Object(d.a)(function(e){return{width:e.offsetWidth,height:e.offsetHeight}}(e)))}var M=n(94);function z(e){switch(e.tagName){case"INPUT":case"SELECT":case"TEXTAREA":return!0;default:return e.isContentEditable}}function P(){return Object(r.a)(window,"keydown").pipe(Object(A.a)(e=>!(e.metaKey||e.ctrlKey)),Object(p.a)(e=>({type:e.key,claim(){e.preventDefault(),e.stopPropagation()}})),Object(M.a)())}var H=n(84);function U(e){location.href=e.href}function q(e,t=location){return e.host===t.host&&/^(?:\/[\w-]+)*(?:\/?|\.html)$/i.test(e.pathname)}function N(e,t=location){return e.pathname===t.pathname&&e.hash.length>0}function I(){return new H.a(new URL(location.href))}var D=n(85);function Y(e,{location$:t}){return t.pipe(Object(D.a)(1),Object(p.a)(({href:t})=>new URL(e,t).toString().replace(/\/$/,"")),Object(k.a)({bufferSize:1,refCount:!0}))}function B(){return location.hash.substring(1)}function F(e){const t=f("a");t.href=e,t.addEventListener("click",e=>e.stopPropagation()),t.click()}function J(){return Object(r.a)(window,"hashchange").pipe(Object(p.a)(B),Object(d.a)(B()),Object(A.a)(e=>e.length>0),Object(M.a)())}var K=n(6);function Q(e){const t=matchMedia(e);return new K.a(e=>{t.addListener(t=>e.next(t.matches))}).pipe(Object(d.a)(t.matches),Object(k.a)({bufferSize:1,refCount:!0}))}const W={drawer:u("[data-md-toggle=drawer]"),search:u("[data-md-toggle=search]")};function X(e){return W[e].checked}function V(e,t){W[e].checked!==t&&W[e].click()}function G(e){const t=W[e];return Object(r.a)(t,"change").pipe(Object(p.a)(()=>t.checked),Object(d.a)(t.checked))}var Z=n(47),ee=n(76);function te(){return{x:Math.max(0,pageXOffset),y:Math.max(0,pageYOffset)}}function ne({x:e,y:t}){window.scrollTo(e||0,t||0)}function ce(){return{width:innerWidth,height:innerHeight}}function re(){return Object(Z.a)([Object(j.a)(Object(r.a)(window,"scroll",{passive:!0}),Object(r.a)(window,"resize",{passive:!0})).pipe(Object(p.a)(te),Object(d.a)(te())),Object(r.a)(window,"resize",{passive:!0}).pipe(Object(p.a)(ce),Object(d.a)(ce()))]).pipe(Object(p.a)(([e,t])=>({offset:e,size:t})),Object(k.a)({bufferSize:1,refCount:!0}))}function ae(e,{header$:t,viewport$:n}){const c=n.pipe(Object(ee.a)("size")),r=Object(Z.a)([c,t]).pipe(Object(p.a)(()=>({x:e.offsetLeft,y:e.offsetTop})));return Object(Z.a)([t,n,r]).pipe(Object(p.a)(([{height:e},{offset:t,size:n},{x:c,y:r}])=>({offset:{x:t.x-c,y:t.y-r+e},size:n})))}var oe=n(86),ie=n(87);function ue(e,{tx$:t}){const n=Object(r.a)(e,"message").pipe(Object(p.a)(({data:e})=>e));return t.pipe(Object(oe.a)(()=>n,{leading:!0,trailing:!0}),Object(E.a)(t=>e.postMessage(t)),Object(ie.a)(n),Object(M.a)())}},,function(e,t,n){"use strict";function c(e){return"object"==typeof e&&"string"==typeof e.base&&"object"==typeof e.features&&"object"==typeof e.search}function r(e,t){if("string"==typeof t||"number"==typeof t)e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(const n of t)r(e,n)}function a(e,t,...n){const c=document.createElement(e);if(t)for(const e of Object.keys(t))"boolean"!=typeof t[e]?c.setAttribute(e,t[e]):t[e]&&c.setAttribute(e,"");for(const e of n)r(c,e);return c}n.d(t,"d",(function(){return c})),n.d(t,"b",(function(){return a})),n.d(t,"a",(function(){return u})),n.d(t,"f",(function(){return f})),n.d(t,"g",(function(){return O})),n.d(t,"e",(function(){return j})),n.d(t,"c",(function(){return p}));var o=n(62),i=n(46);function u(e,t){return Object(o.a)(()=>{const n=sessionStorage.getItem(e);if(n)return Object(i.a)(JSON.parse(n));{const n=t();return n.subscribe(t=>{try{sessionStorage.setItem(e,JSON.stringify(t))}catch(e){}}),n}})}var b=n(0);let s;function f(e,t){if(void 0===s){const e=Object(b.d)("#__lang");s=JSON.parse(e.textContent)}if(void 0===s[e])throw new ReferenceError("Invalid translation: "+e);return void 0!==t?s[e].replace("#",t.toString()):s[e]}function O(e,t){let n=t;if(e.length>n){for(;" "!==e[n]&&--n>0;);return e.substring(0,n)+"..."}return e}function j(e){if(e>999){return((e+1e-6)/1e3).toFixed(+((e-950)%1e3>99))+"k"}return e.toString()}function p(e){let t=0;for(let n=0,c=e.length;n{Object(b.e)("pre > code").forEach((e,t)=>{const n=e.parentElement;n.id="__code_"+t,n.insertBefore(Object(s.a)(n.id),e)})});const n=new a.a(e=>{new c(".md-clipboard").on("success",t=>e.next(t))}).pipe(Object(o.a)());return n.pipe(Object(i.a)(e=>e.clearSelection()),Object(u.a)(Object(f.f)("clipboard.copied"))).subscribe(t),n}var j=n(32),p=n(46),d=n(68),l=n(18),h=n(20),m=n(11),v=n(67),g=n(97);function $({duration:e}={}){const t=new j.a,n=Object(b.a)("div");return n.classList.add("md-dialog","md-typeset"),t.pipe(Object(h.a)(t=>Object(p.a)(document.body).pipe(Object(m.a)(e=>e.appendChild(n)),Object(v.a)(d.a),Object(g.a)(1),Object(i.a)(e=>{e.innerHTML=t,e.setAttribute("data-md-state","open")}),Object(g.a)(e||2e3),Object(i.a)(e=>e.removeAttribute("data-md-state")),Object(g.a)(400),Object(i.a)(e=>{e.innerHTML="",e.remove()})))).subscribe(l.a),t}var y=n(80),w=n(96),x=n(9),S=n(83),C=n(36),T=n(76),k=n(89),E=n(90),A=n(88),_=n(77),L=n(91),R=n(78);function M(e,{document$:t,viewport$:n,location$:c}){"scrollRestoration"in history&&(history.scrollRestoration="manual"),Object(y.a)(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"});const a=Object(b.c)('link[rel="shortcut icon"]');void 0!==a&&(a.href=a.href);const i=Object(y.a)(document.body,"click").pipe(Object(S.a)(e=>!(e.metaKey||e.ctrlKey)),Object(h.a)(t=>{if(t.target instanceof HTMLElement){const n=t.target.closest("a");if(n&&!n.target&&Object(b.h)(n)&&e.includes(n.href))return Object(b.g)(n)||t.preventDefault(),Object(p.a)(n)}return r.a}),Object(m.a)(e=>({url:new URL(e.href)})),Object(o.a)());i.subscribe(()=>{Object(b.o)("search",!1)});const u=i.pipe(Object(S.a)(({url:e})=>!Object(b.g)(e)),Object(o.a)()),s=Object(y.a)(window,"popstate").pipe(Object(S.a)(e=>null!==e.state),Object(m.a)(e=>({url:new URL(location.href),offset:e.state})),Object(o.a)());Object(w.a)(u,s).pipe(Object(C.a)((e,t)=>e.url.href===t.url.href),Object(m.a)(({url:e})=>e)).subscribe(c);const f=c.pipe(Object(T.a)("pathname"),Object(k.a)(1),Object(h.a)(e=>Object(x.a)(fetch(e.href,{credentials:"same-origin"}).then(e=>e.text())).pipe(Object(E.a)(()=>(Object(b.m)(e),r.a)))),Object(o.a)());u.pipe(Object(A.a)(f)).subscribe(({url:e})=>{history.pushState({},"",e.toString())});const O=new DOMParser;f.pipe(Object(m.a)(e=>O.parseFromString(e,"text/html"))).subscribe(t);const j=Object(w.a)(u,s).pipe(Object(A.a)(t));j.subscribe(({url:e,offset:t})=>{e.hash&&!t?Object(b.n)(e.hash):Object(b.p)(t||{y:0})}),j.pipe(Object(_.a)(t)).subscribe(([,{title:e,head:t}])=>{document.title=e;for(const e of['link[rel="canonical"]','meta[name="author"]','meta[name="description"]']){const n=Object(b.c)(e,t),c=Object(b.c)(e,document.head);void 0!==n&&void 0!==c&&Object(b.j)(c,n)}document.dispatchEvent(new CustomEvent("DOMContentSwitch"))}),n.pipe(Object(L.a)(250),Object(T.a)("offset")).subscribe(({offset:e})=>{history.replaceState(e,"")}),Object(w.a)(i,s).pipe(Object(R.a)(2,1),Object(S.a)(([e,t])=>e.url.pathname===t.url.pathname&&!Object(b.g)(t.url)),Object(m.a)(([,e])=>e)).subscribe(({offset:e})=>{Object(b.p)(e||{y:0})})}var z=n(8);function P(){const e=Object(b.u)().pipe(Object(m.a)(e=>Object.assign({mode:Object(b.f)("search")?"search":"global"},e)),Object(S.a)(({mode:e})=>{if("global"===e){const e=Object(b.b)();if(void 0!==e)return!Object(b.i)(e)}return!0}),Object(o.a)());return e.pipe(Object(S.a)(({mode:e})=>"search"===e),Object(_.a)(Object(z.useComponent)("search-query"),Object(z.useComponent)("search-result"))).subscribe(([e,t,n])=>{const c=Object(b.b)();switch(e.type){case"Enter":c===t&&e.claim();break;case"Escape":case"Tab":Object(b.o)("search",!1),Object(b.k)(t,!1);break;case"ArrowUp":case"ArrowDown":if(void 0===c)Object(b.k)(t);else{const r=[t,...Object(b.e)(":not(details) > [href], summary, details[open] [href]",n)],a=Math.max(0,(Math.max(0,r.indexOf(c))+r.length+("ArrowUp"===e.type?-1:1))%r.length);Object(b.k)(r[a])}e.claim();break;default:t!==Object(b.b)()&&Object(b.k)(t)}}),e.pipe(Object(S.a)(({mode:e})=>"global"===e),Object(_.a)(Object(z.useComponent)("search-query"))).subscribe(([e,t])=>{switch(e.type){case"f":case"s":case"/":Object(b.k)(t),Object(b.l)(t),e.claim();break;case"p":case",":const n=Object(b.c)("[href][rel=prev]");void 0!==n&&n.click();break;case"n":case".":const c=Object(b.c)("[href][rel=next]");void 0!==c&&c.click()}}),e}var H=n(35)},,,,,function(e,t,n){"use strict";n.d(t,"a",(function(){return O})),n.d(t,"b",(function(){return j}));var c=n(46),r=n(26),a=n(11),o=n(66),i=n(75),u=n(20),b=n(36),s=n(0);let f;function O(e,{document$:t}){f=t.pipe(Object(a.a)(t=>e.reduce((e,n)=>{const c=Object(s.c)(`[data-md-component=${n}]`,t);return Object.assign(Object.assign({},e),void 0!==c?{[n]:c}:{})},{})),Object(o.a)((t,n)=>{for(const c of e)switch(c){case"announce":case"header-title":case"container":case"skip":c in t&&void 0!==t[c]&&(Object(s.j)(t[c],n[c]),t[c]=n[c]);break;default:void 0!==n[c]?t[c]=Object(s.c)(`[data-md-component=${c}]`):delete t[c]}return t}),Object(i.a)({bufferSize:1,refCount:!0}))}function j(e){return f.pipe(Object(u.a)(t=>void 0!==t[e]?Object(c.a)(t[e]):r.a),Object(b.a)())}},,,,function(e,t,n){"use strict";function c(e,t){e.setAttribute("data-md-state",t?"blur":"")}function r(e){e.removeAttribute("data-md-state")}function a(e,t){e.classList.toggle("md-nav__link--active",t)}function o(e){e.classList.remove("md-nav__link--active")}n.d(t,"d",(function(){return c})),n.d(t,"b",(function(){return r})),n.d(t,"c",(function(){return a})),n.d(t,"a",(function(){return o}))},,,,function(e,t,n){"use strict";var c=n(49);n.o(c,"applySidebar")&&n.d(t,"applySidebar",(function(){return c.applySidebar})),n.o(c,"mountTableOfContents")&&n.d(t,"mountTableOfContents",(function(){return c.mountTableOfContents})),n.o(c,"mountTabs")&&n.d(t,"mountTabs",(function(){return c.mountTabs})),n.o(c,"watchSidebar")&&n.d(t,"watchSidebar",(function(){return c.watchSidebar}))},function(e,t,n){"use strict";n.d(t,"a",(function(){return a})),n.d(t,"b",(function(){return i})),n.d(t,"c",(function(){return u})),n.d(t,"d",(function(){return b}));var c,r=n(2);function a(e){return Object(r.b)("button",{class:"md-clipboard md-icon",title:Object(r.f)("clipboard.copy"),"data-clipboard-target":`#${e} > code`})}function o(e,t){const n=t&c.PARENT,a=t&c.TEASER,o=Object.keys(e.terms).filter(t=>!e.terms[t]).map(e=>[Object(r.b)("del",null,e)," "]).flat().slice(0,-1),i=e.location;return Object(r.b)("a",{href:i,class:"md-search-result__link",tabIndex:-1},Object(r.b)("article",{class:["md-search-result__article",...n?["md-search-result__article--document"]:[]].join(" "),"data-md-score":e.score.toFixed(2)},n>0&&Object(r.b)("div",{class:"md-search-result__icon md-icon"}),Object(r.b)("h1",{class:"md-search-result__title"},e.title),a>0&&e.text.length>0&&Object(r.b)("p",{class:"md-search-result__teaser"},Object(r.g)(e.text,320)),a>0&&o.length>0&&Object(r.b)("p",{class:"md-search-result__terms"},Object(r.f)("search.result.term.missing"),": ",o)))}function i(e,t=1/0){const n=[...e],a=n.findIndex(e=>!e.location.includes("#")),[i]=n.splice(a,1);let u=n.findIndex(e=>e.scoreo(e,c.TEASER)),...s.length?[Object(r.b)("details",{class:"md-search-result__more"},Object(r.b)("summary",{tabIndex:-1},s.length>0&&1===s.length?Object(r.f)("search.result.more.one"):Object(r.f)("search.result.more.other",s.length)),s.map(e=>o(e,c.TEASER)))]:[]];return Object(r.b)("li",{class:"md-search-result__item"},f)}function u(e){return Object(r.b)("ul",{class:"md-source__facts"},e.map(e=>Object(r.b)("li",{class:"md-source__fact"},e)))}function b(e){return Object(r.b)("div",{class:"md-typeset__scrollwrap"},Object(r.b)("div",{class:"md-typeset__table"},e))}!function(e){e[e.TEASER=1]="TEASER",e[e.PARENT=2]="PARENT"}(c||(c={}))},,,function(e,t,n){"use strict";function c(e,t){e.style.top=t+"px"}function r(e){e.style.top=""}function a(e,t){e.style.height=t+"px"}function o(e){e.style.height=""}n.d(t,"d",(function(){return c})),n.d(t,"b",(function(){return r})),n.d(t,"c",(function(){return a})),n.d(t,"a",(function(){return o}))},,,,,,,function(e,t,n){"use strict";var c=n(54);n.o(c,"applyAnchorList")&&n.d(t,"applyAnchorList",(function(){return c.applyAnchorList})),n.o(c,"watchAnchorList")&&n.d(t,"watchAnchorList",(function(){return c.watchAnchorList}));var r=n(55);n.d(t,"applyAnchorList",(function(){return r.a})),n.d(t,"watchAnchorList",(function(){return r.b}));n(19)},function(e,t,n){"use strict";n.d(t,"b",(function(){return c})),n.d(t,"f",(function(){return h})),n.d(t,"a",(function(){return r})),n.d(t,"d",(function(){return j})),n.d(t,"c",(function(){return p})),n.d(t,"e",(function(){return d}));n(63);function c(e){return e.split(/"([^"]+)"/g).map((e,t)=>1&t?e.replace(/^\b|^(?![^\x00-\x7F]|$)|\s+/g," +"):e).join("").replace(/"|(?:^|\s+)[*+\-:^~]+(?=\s+|$)/g,"").trim()}var r,a=n(32),o=n(42),i=n(77),u=n(11),b=n(94),s=n(67),f=n(0),O=n(2);function j(e){return e.type===r.READY}function p(e){return e.type===r.QUERY}function d(e){return e.type===r.RESULT}function l({config:e,docs:t,index:n}){1===e.lang.length&&"en"===e.lang[0]&&(e.lang=[Object(O.f)("search.config.lang")]),"[\\s\\-]+"===e.separator&&(e.separator=Object(O.f)("search.config.separator"));return{config:e,docs:t,index:n,pipeline:Object(O.f)("search.config.pipeline").split(/\s*,\s*/).filter(Boolean)}}function h(e,{index$:t,base$:n}){const c=new Worker(e),O=new a.a,j=Object(f.C)(c,{tx$:O}).pipe(Object(i.a)(n),Object(u.a)(([e,t])=>{if(d(e))for(const n of e.data)for(const e of n)e.location=`${t}/${e.location}`;return e}),Object(b.a)());return t.pipe(Object(u.a)(e=>({type:r.SETUP,data:l(e)})),Object(s.a)(o.a)).subscribe(O.next.bind(O)),{tx$:O,rx$:j}}!function(e){e[e.SETUP=0]="SETUP",e[e.READY=1]="READY",e[e.QUERY=2]="QUERY",e[e.RESULT=3]="RESULT"}(r||(r={}))},,,,,,,,,,,,,function(e,t,n){"use strict";n.d(t,"a",(function(){return u}));var c=n(33),r=n(46),a=n(20),o=n(11),i=n(23);function u({header$:e,main$:t,viewport$:n,screen$:u}){return Object(c.a)(Object(a.a)(c=>u.pipe(Object(a.a)(a=>a?Object(i.watchSidebar)(c,{main$:t,viewport$:n}).pipe(Object(i.applySidebar)(c,{header$:e}),Object(o.a)(e=>({sidebar:e}))):Object(r.a)({})))))}},function(e,t,n){"use strict";var c=n(50);n.o(c,"applySidebar")&&n.d(t,"applySidebar",(function(){return c.applySidebar})),n.o(c,"mountTableOfContents")&&n.d(t,"mountTableOfContents",(function(){return c.mountTableOfContents})),n.o(c,"mountTabs")&&n.d(t,"mountTabs",(function(){return c.mountTabs})),n.o(c,"watchSidebar")&&n.d(t,"watchSidebar",(function(){return c.watchSidebar}));var r=n(51);n.d(t,"applySidebar",(function(){return r.a})),n.d(t,"watchSidebar",(function(){return r.b}));n(27)},function(e,t){},function(e,t,n){"use strict";n.d(t,"b",(function(){return j})),n.d(t,"a",(function(){return p}));var c=n(47),r=n(33),a=n(68),o=n(11),i=n(36),u=n(67),b=n(77),s=n(65),f=n(64),O=n(27);function j(e,{main$:t,viewport$:n}){const r=e.parentElement.offsetTop-e.parentElement.parentElement.offsetTop;return Object(c.a)([t,n]).pipe(Object(o.a)(([{offset:e,height:t},{offset:{y:n}}])=>({height:t=t+Math.min(r,Math.max(0,n-e))-r,lock:n>=e+r})),Object(i.a)((e,t)=>e.height===t.height&&e.lock===t.lock))}function p(e,{header$:t}){return Object(r.a)(Object(u.a)(a.a),Object(b.a)(t),Object(s.a)(([{height:t,lock:n},{height:c}])=>{Object(O.c)(e,t),n?Object(O.d)(e,c):Object(O.b)(e)}),Object(o.a)(([e])=>e),Object(f.a)(()=>{Object(O.b)(e),Object(O.a)(e)}))}},function(e,t,n){"use strict";var c=n(53);n.d(t,"mountTableOfContents",(function(){return c.a}));n(34)},function(e,t,n){"use strict";n.d(t,"a",(function(){return f}));var c=n(33),r=n(47),a=n(46),o=n(20),i=n(11),u=n(0),b=n(23),s=n(34);function f({header$:e,main$:t,viewport$:n,tablet$:f}){return Object(c.a)(Object(o.a)(c=>f.pipe(Object(o.a)(o=>{if(o){const a=Object(u.e)(".md-nav__link",c),o=Object(b.watchSidebar)(c,{main$:t,viewport$:n}).pipe(Object(b.applySidebar)(c,{header$:e})),f=Object(s.watchAnchorList)(a,{header$:e,viewport$:n}).pipe(Object(s.applyAnchorList)(a));return Object(r.a)([o,f]).pipe(Object(i.a)(([e,t])=>({sidebar:e,anchors:t})))}return Object(a.a)({})}))))}},function(e,t){},function(e,t,n){"use strict";n.d(t,"b",(function(){return m})),n.d(t,"a",(function(){return v}));var c=n(47),r=n(33),a=n(68),o=n(11),i=n(76),u=n(20),b=n(66),s=n(36),f=n(79),O=n(78),j=n(67),p=n(65),d=n(64),l=n(0),h=n(19);function m(e,{header$:t,viewport$:n}){const r=new Map;for(const t of e){const e=decodeURIComponent(t.hash.substring(1)),n=Object(l.c)(`[id="${e}"]`);void 0!==n&&r.set(t,n)}const a=t.pipe(Object(o.a)(e=>18+e.height));return Object(l.t)(document.body).pipe(Object(i.a)("height"),Object(o.a)(()=>{let e=[];return[...r].reduce((t,[n,c])=>{for(;e.length;){if(!(r.get(e[e.length-1]).tagName>=c.tagName))break;e.pop()}let a=c.offsetTop;for(;!a&&c.parentElement;)a=(c=c.parentElement).offsetTop;return t.set([...e=[...e,n]].reverse(),a)},new Map)}),Object(u.a)(e=>Object(c.a)([a,n]).pipe(Object(b.a)(([e,t],[n,{offset:{y:c}}])=>{for(;t.length;){const[,r]=t[0];if(!(r-n=c))break;t=[e.pop(),...t]}return[e,t]},[[],[...e]]),Object(s.a)((e,t)=>e[0]===t[0]&&e[1]===t[1])))).pipe(Object(o.a)(([e,t])=>({prev:e.map(([e])=>e),next:t.map(([e])=>e)})),Object(f.a)({prev:[],next:[]}),Object(O.a)(2,1),Object(o.a)(([e,t])=>e.prev.length{for(const[e]of t)Object(h.a)(e),Object(h.b)(e);e.forEach(([t],n)=>{Object(h.c)(t,n===e.length-1),Object(h.d)(t,!0)})}),Object(d.a)(()=>{for(const t of e)Object(h.a)(t),Object(h.b)(t)}))}},function(e,t,n){"use strict";n.d(t,"a",(function(){return j})),n.d(t,"b",(function(){return $})),n.d(t,"c",(function(){return S})),n.d(t,"d",(function(){return z}));var c=n(33),r=n(47),a=n(20),o=n(83),i=n(81),u=n(79),b=n(88),s=n(85),f=n(11),O=n(35);function j({rx$:e,tx$:t},{query$:n,reset$:j,result$:p}){return Object(c.a)(Object(a.a)(()=>{const c=e.pipe(Object(o.a)(O.d),Object(i.a)("ready"),Object(u.a)("waiting"));return t.pipe(Object(o.a)(O.c),Object(b.a)(c),Object(s.a)(1)).subscribe(t.next.bind(t)),Object(r.a)([c,n,p,j]).pipe(Object(f.a)(([e,t,n])=>({status:e,query:t,result:n})))}))}var p=n(76),d=n(0),l=n(10),h=n(96),m=n(80),v=n(97),g=n(36);function $({tx$:e},t={}){return Object(c.a)(Object(a.a)(n=>{const c=function(e,{transform:t}={}){const n=t||l.b,c=Object(h.a)(Object(m.a)(e,"keyup"),Object(m.a)(e,"focus").pipe(Object(v.a)(1))).pipe(Object(f.a)(()=>n(e.value)),Object(u.a)(n(e.value)),Object(g.a)()),a=Object(d.r)(e);return Object(r.a)([c,a]).pipe(Object(f.a)(([e,t])=>({value:e,focus:t})))}(n,t);return c.pipe(Object(p.a)("value"),Object(f.a)(({value:e})=>({type:l.a.QUERY,data:e}))).subscribe(e.next.bind(e)),c.pipe(Object(p.a)("focus")).subscribe(({focus:e})=>{e&&Object(d.o)("search",e)}),c}))}var y=n(87),w=n(65),x=n(15);function S(){return Object(c.a)(Object(a.a)(e=>function(e){return Object(m.a)(e,"click").pipe(Object(i.a)(void 0))}(e).pipe(Object(y.a)(Object(x.b)("search-query")),Object(w.a)(d.k),Object(i.a)(void 0))),Object(u.a)(void 0))}var C=n(68),T=n(77),k=n(67),E=n(66),A=n(64),_=n(24),L=n(2);function R(e,t){e.appendChild(t)}function M(e,{query$:t,ready$:n,fetch$:r}){const o=Object(d.d)(".md-search-result__list",e),u=Object(d.d)(".md-search-result__meta",e);return Object(c.a)(Object(T.a)(t,n),Object(f.a)(([e,t])=>(t.value?function(e,t){switch(t){case 0:e.textContent=Object(L.f)("search.result.none");break;case 1:e.textContent=Object(L.f)("search.result.one");break;default:e.textContent=Object(L.f)("search.result.other",t)}}(u,e.length):function(e){e.textContent=Object(L.f)("search.result.placeholder")}(u),e)),Object(a.a)(t=>{const n=[...t.map(([e])=>e.score),0];return r.pipe(Object(k.a)(C.a),Object(E.a)(c=>{const r=e.parentElement;for(;c16)););return c},0),Object(i.a)(t),Object(A.a)(()=>{!function(e){e.innerHTML=""}(o)}))}))}function z({rx$:e},{query$:t}){return Object(c.a)(Object(a.a)(n=>{const c=n.parentElement,r=e.pipe(Object(o.a)(l.c),Object(i.a)(!0)),a=Object(d.s)(c).pipe(Object(f.a)(({y:e})=>e>=c.scrollHeight-c.offsetHeight-16),Object(g.a)(),Object(o.a)(Boolean));return e.pipe(Object(o.a)(l.d),Object(f.a)(({data:e})=>e),M(n,{query$:t,ready$:r,fetch$:a}),Object(u.a)([]))}))}},function(e,t,n){"use strict";n.d(t,"a",(function(){return v}));var c=n(33),r=n(47),a=n(20),o=n(11),i=n(83),u=n(95),b=n(36),s=n(79),f=n(0),O=n(15),j=n(46),p=n(68),d=n(75),l=n(67),h=n(65),m=n(64);function v({document$:e,viewport$:t}){return Object(c.a)(Object(a.a)(n=>{const v=function(e,{document$:t}){return t.pipe(Object(o.a)(()=>{const t=getComputedStyle(e);return["sticky","-webkit-sticky"].includes(t.position)}),Object(b.a)(),Object(a.a)(t=>t?Object(f.t)(e).pipe(Object(o.a)(({height:e})=>({sticky:!0,height:e}))):Object(j.a)({sticky:!1,height:0})),Object(d.a)({bufferSize:1,refCount:!0}))}(n,{document$:e}),g=Object(O.b)("main").pipe(Object(o.a)(e=>Object(f.c)("h1, h2, h3, h4, h5, h6",e)),Object(i.a)(e=>void 0!==e),Object(u.a)(Object(O.b)("header-title")),Object(a.a)(([e,n])=>Object(f.B)(e,{header$:v,viewport$:t}).pipe(Object(o.a)(({offset:{y:t}})=>t>=e.offsetHeight?"page":"site"),Object(b.a)(),function(e){return Object(c.a)(Object(l.a)(p.a),Object(h.a)(t=>{!function(e,t){e.setAttribute("data-md-state",t?"active":"")}(e,"page"===t)}),Object(m.a)(()=>{!function(e){e.removeAttribute("data-md-state")}(e)}))}(n))),Object(s.a)("site"));return Object(r.a)([v,g]).pipe(Object(o.a)(([e,t])=>Object.assign({type:t},e)))}))}},function(e,t,n){"use strict";n.d(t,"a",(function(){return h}));var c=n(32),r=n(18),a=n(33),o=n(20),i=n(76),u=n(65),b=n(64),s=n(15),f=n(47),O=n(68),j=n(11),p=n(36),d=n(67),l=n(0);function h({header$:e,viewport$:t}){const n=new c.a;return Object(s.b)("header").pipe(Object(o.a)(e=>{return n.pipe(Object(i.a)("active"),(t=e,Object(a.a)(Object(d.a)(O.a),Object(u.a)(({active:e})=>{!function(e,t){e.setAttribute("data-md-state",t?"shadow":"")}(t,e)}),Object(b.a)(()=>{!function(e){e.removeAttribute("data-md-state")}(t)}))));var t})).subscribe(r.a),Object(a.a)(Object(o.a)(n=>function(e,{header$:t,viewport$:n}){const c=t.pipe(Object(j.a)(({height:e})=>e),Object(p.a)()),r=c.pipe(Object(o.a)(()=>Object(l.t)(e).pipe(Object(j.a)(({height:t})=>({top:e.offsetTop,bottom:e.offsetTop+t})),Object(i.a)("bottom"))));return Object(f.a)([c,r,n]).pipe(Object(j.a)(([e,{top:t,bottom:n},{offset:{y:c},size:{height:r}}])=>({offset:t-e,height:r=Math.max(0,r-Math.max(0,t-c,e)-Math.max(0,r+c-n)),active:t-e<=c})),Object(p.a)((e,t)=>e.offset===t.offset&&e.height===t.height&&e.active===t.active))}(n,{header$:e,viewport$:t})),Object(u.a)(e=>n.next(e)),Object(b.a)(()=>n.complete()))}},function(e,t,n){"use strict";n.d(t,"a",(function(){return j}));var c=n(33),r=n(46),a=n(20),o=n(11),i=n(76),u=n(0),b=n(68),s=n(67),f=n(65),O=n(64);function j({header$:e,viewport$:t,screen$:n}){return Object(c.a)(Object(a.a)(j=>n.pipe(Object(a.a)(n=>n?Object(u.B)(j,{header$:e,viewport$:t}).pipe(Object(o.a)(({offset:{y:e}})=>({hidden:e>=10})),Object(i.a)("hidden"),function(e){return Object(c.a)(Object(s.a)(b.a),Object(f.a)(({hidden:t})=>{!function(e,t){e.setAttribute("data-md-state",t?"hidden":"")}(e,t)}),Object(O.a)(()=>{!function(e){e.removeAttribute("data-md-state")}(e)}))}(j)):Object(r.a)({hidden:!0})))))}},,,,,,,,,,,,,,,function(e,t,n){"use strict";n.r(t),n.d(t,"setScrollLock",(function(){return H})),n.d(t,"resetScrollLock",(function(){return U})),n.d(t,"initialize",(function(){return q}));n(69);var c=n(62),r=n(9),a=n(46),o=n(82),i=n(47),u=n(68),b=n(80),s=n(96),f=n(75),O=n(20),j=n(90),p=n(65),d=n(97),l=n(77),h=n(67),m=n(83),v=n(11),g=n(85),$=n(0),y=n(8),w=n(10),x=n(76);var S=n(87);var C=n(6),T=n(26),k=n(18),E=n(89),A=n(92);var _=n(93),L=n(81);function R(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}var M=n(24),z=n(2);function P(e){const[t]=e.match(/(git(?:hub|lab))/i)||[];switch(t.toLowerCase()){case"github":const[,t,n]=e.match(/^.+github\.com\/([^\/]+)\/?([^\/]+)?/i);return function(e,t){const n=void 0!==t?`https://api.github.com/repos/${e}/${t}`:"https://api.github.com/users/"+e;return Object(r.a)(fetch(n).then(e=>e.json())).pipe(Object(v.a)(e=>{if(void 0!==t){const{stargazers_count:t,forks_count:n}=e;return[Object(z.e)(t||0)+" Stars",Object(z.e)(n||0)+" Forks"]}{const{public_repos:t}=e;return[Object(z.e)(t||0)+" Repositories"]}}))}(t,n);case"gitlab":const[,c,a]=e.match(/^.+?([^\/]*gitlab[^\/]+)\/(.+?)\/?$/i);return function(e,t){const n=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return Object(r.a)(fetch(n).then(e=>e.json())).pipe(Object(v.a)(({star_count:e,forks_count:t})=>[Object(z.e)(e)+" Stars",Object(z.e)(t)+" Forks"]))}(c,a);default:return o.a}}function H(e,t){e.setAttribute("data-md-state","lock"),e.style.top=`-${t}px`}function U(e){const t=-1*parseInt(e.style.top,10);e.removeAttribute("data-md-state"),e.style.top="",t&&window.scrollTo(0,t)}function q(e){if(!Object(z.d)(e))throw new SyntaxError("Invalid configuration: "+JSON.stringify(e));const t=Object($.q)(),n=Object($.v)(),q=Object($.w)(e.base,{location$:n}),N=Object($.x)(),I=Object($.A)(),D=Object($.y)("(min-width: 960px)"),Y=Object($.y)("(min-width: 1220px)");Object(y.setupComponents)(["announce","container","header","header-title","main","navigation","search","search-query","search-reset","search-result","skip","tabs","toc"],{document$:t});const B=Object(w.h)();matchMedia("(hover)").matches&&function({document$:e,viewport$:t}){const n=e.pipe(Object(v.a)(()=>Object($.e)("pre > code"))),c=t.pipe(Object(x.a)("size"));Object(i.a)([n,c]).subscribe(([e])=>{for(const t of e)t.scrollWidth>t.clientWidth?t.setAttribute("tabindex","0"):t.removeAttribute("tabindex")})}({document$:t,viewport$:I}),function({document$:e,hash$:t}){const n=e.pipe(Object(v.a)(()=>Object($.e)("details")));Object(s.a)(Object($.y)("print").pipe(Object(m.a)(Boolean)),Object(b.a)(window,"beforeprint")).pipe(Object(S.a)(n)).subscribe(e=>{for(const t of e)t.setAttribute("open","")}),t.pipe(Object(v.a)(e=>Object($.c)(`[id="${e}"]`)),Object(m.a)(e=>void 0!==e),Object(p.a)(e=>{const t=e.closest("details");t&&!t.open&&t.setAttribute("open","")})).subscribe(e=>e.scrollIntoView())}({document$:t,hash$:N}),function({document$:e}){e.pipe(Object(E.a)(1),Object(l.a)(Object(y.useComponent)("container")),Object(v.a)(([,e])=>Object($.e)("script",e))).pipe(Object(O.a)(e=>Object(a.a)(...e)),Object(A.a)(e=>{const t=Object($.a)("script");return e.src?(t.src=e.src,Object($.j)(e,t),new C.a(e=>{t.onload=()=>e.complete()})):(t.textContent=e.textContent,Object($.j)(e,t),T.a)})).subscribe(k.a)}({document$:t}),function({document$:e}){e.pipe(Object(v.a)(()=>Object($.d)(".md-source[href]")),Object(O.a)(({href:e})=>Object(z.a)(""+Object(z.c)(e),()=>P(e))),Object(j.a)(()=>o.a)).subscribe(e=>{for(const t of Object($.e)(".md-source__repository"))t.hasAttribute("data-md-state")||(t.setAttribute("data-md-state","done"),t.appendChild(Object(M.c)(e)))})}({document$:t}),function({document$:e}){const t=Object($.a)("table");e.pipe(Object(v.a)(()=>Object($.e)("table:not([class])"))).subscribe(e=>{for(const n of e)Object($.j)(n,t),Object($.j)(t,Object(M.d)(n))})}({document$:t}),function({document$:e}){const t=e.pipe(Object(v.a)(()=>Object($.e)("[data-md-scrollfix]")),Object(f.a)({bufferSize:1,refCount:!0}));t.subscribe(e=>{for(const t of e)t.removeAttribute("data-md-scrollfix")}),Object(_.a)(R,t,o.a).pipe(Object(O.a)(e=>Object(s.a)(...e.map(e=>Object(b.a)(e,"touchstart").pipe(Object(L.a)(e)))))).subscribe(e=>{const t=e.scrollTop;0===t?e.scrollTop=1:t+e.offsetHeight===e.scrollHeight&&(e.scrollTop=t-1)})}({document$:t});const F=Object(w.f)(),J=Object(w.e)({document$:t,dialog$:F}),K=Object(y.useComponent)("header").pipe(Object(y.mountHeader)({document$:t,viewport$:I}),Object(f.a)({bufferSize:1,refCount:!0})),Q=Object(y.useComponent)("main").pipe(Object(y.mountMain)({header$:K,viewport$:I}),Object(f.a)({bufferSize:1,refCount:!0})),W=Object(y.useComponent)("navigation").pipe(Object(y.mountNavigation)({header$:K,main$:Q,viewport$:I,screen$:Y}),Object(f.a)({bufferSize:1,refCount:!0})),X=Object(y.useComponent)("toc").pipe(Object(y.mountTableOfContents)({header$:K,main$:Q,viewport$:I,tablet$:D}),Object(f.a)({bufferSize:1,refCount:!0})),V=Object(y.useComponent)("tabs").pipe(Object(y.mountTabs)({header$:K,viewport$:I,screen$:Y}),Object(f.a)({bufferSize:1,refCount:!0})),G=Object(y.useComponent)("search").pipe(Object(O.a)(()=>Object(c.a)(()=>{const t=e.search&&e.search.index?e.search.index:void 0,n=void 0!==t?Object(r.a)(t):q.pipe(Object(O.a)(e=>fetch(e+"/search/search_index.json",{credentials:"same-origin"}).then(e=>e.json())));return Object(a.a)(Object(w.i)(e.search.worker,{base$:q,index$:n}))}))).pipe(Object(O.a)(t=>{const n=Object(y.useComponent)("search-query").pipe(Object(y.mountSearchQuery)(t,{transform:e.search.transform}),Object(f.a)({bufferSize:1,refCount:!0})),c=Object(y.useComponent)("search-reset").pipe(Object(y.mountSearchReset)(),Object(f.a)({bufferSize:1,refCount:!0})),r=Object(y.useComponent)("search-result").pipe(Object(y.mountSearchResult)(t,{query$:n}),Object(f.a)({bufferSize:1,refCount:!0}));return Object(y.useComponent)("search").pipe(Object(y.mountSearch)(t,{query$:n,reset$:c,result$:r}))}),Object(j.a)(()=>(Object(y.useComponent)("search").subscribe(e=>e.hidden=!0),o.a)),Object(f.a)({bufferSize:1,refCount:!0}));if(N.pipe(Object(p.a)(()=>Object($.o)("search",!1)),Object(d.a)(125)).subscribe(e=>Object($.n)("#"+e)),Object(i.a)([Object($.z)("search"),D]).pipe(Object(l.a)(I),Object(O.a)(([[e,n],{offset:{y:c}}])=>{const r=e&&!n;return t.pipe(Object(d.a)(r?400:100),Object(h.a)(u.a),Object(p.a)(({body:e})=>r?H(e,c):U(e)))})).subscribe(),Object(b.a)(document.body,"click").pipe(Object(m.a)(e=>!(e.metaKey||e.ctrlKey)),Object(m.a)(e=>{if(e.target instanceof HTMLElement){const t=e.target.closest("a");if(t&&Object($.h)(t))return!0}return!1})).subscribe(()=>{Object($.o)("drawer",!1)}),e.features.includes("navigation.instant")&&"file:"!==location.protocol){const e=new DOMParser;q.pipe(Object(O.a)(t=>Object(r.a)(fetch(t+"/sitemap.xml").then(e=>e.text()).then(t=>e.parseFromString(t,"text/xml")))),Object(l.a)(q),Object(v.a)(([e,t])=>{const n=Object($.e)("loc",e).map(e=>e.textContent);if(n.length>1){const[e,c]=n.sort((e,t)=>e.length-t.length);let r=0;if(e===c)r=e.length;else for(;e.charAt(r)===c.charAt(r);)r++;for(let c=0;c{Object(w.g)(e,{document$:t,location$:n,viewport$:I})})}B.pipe(Object(m.a)(e=>"global"===e.mode&&"Tab"===e.type),Object(g.a)(1)).subscribe(()=>{for(const e of Object($.e)(".headerlink"))e.style.visibility="visible"});const Z={document$:t,location$:n,viewport$:I,header$:K,main$:Q,navigation$:W,search$:G,tabs$:V,toc$:X,clipboard$:J,keyboard$:B,dialog$:F};return Object(s.a)(...Object.values(Z)).subscribe(),Z}document.documentElement.classList.remove("no-js"),document.documentElement.classList.add("js"),navigator.userAgent.match(/(iPad|iPhone|iPod)/g)&&document.documentElement.classList.add("ios")}])); -//# sourceMappingURL=bundle.9554a270.min.js.map \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/bundle.9554a270.min.js.map b/docs/v3/v2/assets/javascripts/bundle.9554a270.min.js.map deleted file mode 100644 index d28a93f79..000000000 --- a/docs/v3/v2/assets/javascripts/bundle.9554a270.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/assets/javascripts/browser/document/index.ts","webpack:///./src/assets/javascripts/browser/element/_/index.ts","webpack:///./src/assets/javascripts/browser/element/focus/index.ts","webpack:///./src/assets/javascripts/browser/element/offset/index.ts","webpack:///./src/assets/javascripts/browser/element/select/index.ts","webpack:///./src/assets/javascripts/browser/element/size/index.ts","webpack:///./src/assets/javascripts/browser/keyboard/index.ts","webpack:///./src/assets/javascripts/browser/location/_/index.ts","webpack:///./src/assets/javascripts/browser/location/base/index.ts","webpack:///./src/assets/javascripts/browser/location/hash/index.ts","webpack:///./src/assets/javascripts/browser/media/index.ts","webpack:///./src/assets/javascripts/browser/toggle/index.ts","webpack:///./src/assets/javascripts/browser/viewport/offset/index.ts","webpack:///./src/assets/javascripts/browser/viewport/size/index.ts","webpack:///./src/assets/javascripts/browser/viewport/_/index.ts","webpack:///./src/assets/javascripts/browser/worker/index.ts","webpack:///./src/assets/javascripts/utilities/config/index.ts","webpack:///./src/assets/javascripts/utilities/jsx/index.ts","webpack:///./src/assets/javascripts/utilities/rxjs/index.ts","webpack:///./src/assets/javascripts/utilities/string/index.ts","webpack:///./src/assets/javascripts/components/index.ts","webpack:///./src/assets/javascripts/integrations/clipboard/index.ts","webpack:///./src/assets/javascripts/integrations/dialog/index.ts","webpack:///./src/assets/javascripts/integrations/instant/index.ts","webpack:///./src/assets/javascripts/integrations/keyboard/index.ts","webpack:///./src/assets/javascripts/components/_/index.ts","webpack:///./src/assets/javascripts/components/toc/anchor/set/index.ts","webpack:///./src/assets/javascripts/components/shared/index.ts","webpack:///./src/assets/javascripts/templates/search/index.tsx","webpack:///./src/assets/javascripts/templates/clipboard/index.tsx","webpack:///./src/assets/javascripts/templates/source/index.tsx","webpack:///./src/assets/javascripts/templates/table/index.tsx","webpack:///./src/assets/javascripts/components/shared/sidebar/set/index.ts","webpack:///./src/assets/javascripts/components/toc/anchor/index.ts","webpack:///./src/assets/javascripts/integrations/search/query/transform/index.ts","webpack:///./src/assets/javascripts/integrations/search/worker/message/index.ts","webpack:///./src/assets/javascripts/integrations/search/worker/_/index.ts","webpack:///./src/assets/javascripts/components/navigation/index.ts","webpack:///./src/assets/javascripts/components/shared/sidebar/index.ts","webpack:///./src/assets/javascripts/components/shared/sidebar/react/index.ts","webpack:///./src/assets/javascripts/components/toc/index.ts","webpack:///./src/assets/javascripts/components/toc/_/index.ts","webpack:///./src/assets/javascripts/components/toc/anchor/react/index.ts","webpack:///./src/assets/javascripts/components/search/_/index.ts","webpack:///./src/assets/javascripts/components/search/query/_/index.ts","webpack:///./src/assets/javascripts/components/search/query/react/index.ts","webpack:///./src/assets/javascripts/components/search/reset/_/index.ts","webpack:///./src/assets/javascripts/components/search/reset/react/index.ts","webpack:///./src/assets/javascripts/components/search/result/set/index.ts","webpack:///./src/assets/javascripts/components/search/result/react/index.ts","webpack:///./src/assets/javascripts/components/search/result/_/index.ts","webpack:///./src/assets/javascripts/components/header/_/index.ts","webpack:///./src/assets/javascripts/components/header/react/index.ts","webpack:///./src/assets/javascripts/components/header/set/index.ts","webpack:///./src/assets/javascripts/components/main/_/index.ts","webpack:///./src/assets/javascripts/components/main/react/index.ts","webpack:///./src/assets/javascripts/components/main/set/index.ts","webpack:///./src/assets/javascripts/components/tabs/_/index.ts","webpack:///./src/assets/javascripts/components/tabs/react/index.ts","webpack:///./src/assets/javascripts/components/tabs/set/index.ts","webpack:///./src/assets/javascripts/patches/scrollfix/index.ts","webpack:///./src/assets/javascripts/patches/source/index.ts","webpack:///./src/assets/javascripts/patches/source/github/index.ts","webpack:///./src/assets/javascripts/patches/source/gitlab/index.ts","webpack:///./src/assets/javascripts/index.ts","webpack:///./src/assets/javascripts/patches/code/index.ts","webpack:///./src/assets/javascripts/patches/details/index.ts","webpack:///./src/assets/javascripts/patches/script/index.ts","webpack:///./src/assets/javascripts/patches/table/index.ts"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","executeModules","i","resolves","length","Object","prototype","hasOwnProperty","call","installedChunks","push","modules","parentJsonpFunction","shift","deferredModules","apply","checkDeferredModules","result","deferredModule","fulfilled","j","depId","splice","__webpack_require__","s","installedModules","0","exports","module","l","m","c","d","name","getter","o","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","p","jsonpArray","window","oldJsonpFunction","slice","watchDocument","document$","ReplaySubject","fromEvent","document","pipe","mapTo","subscribe","getElement","selector","node","querySelector","undefined","getElementOrThrow","el","ReferenceError","getActiveElement","activeElement","HTMLElement","getElements","Array","from","querySelectorAll","createElement","tagName","replaceElement","source","target","replaceWith","setElementFocus","focus","blur","watchElementFocus","merge","map","type","startWith","getElementOffset","x","scrollLeft","y","scrollTop","watchElementOffset","setElementSelection","HTMLInputElement","Error","select","entry$","Subject","observer$","defer","of","entries","entry","next","switchMap","resize","finalize","disconnect","shareReplay","bufferSize","refCount","watchElementSize","tap","observer","observe","filter","unobserve","contentRect","width","height","offsetWidth","offsetHeight","getElementSize","isSusceptibleToKeyboard","isContentEditable","watchKeyboard","ev","metaKey","ctrlKey","preventDefault","stopPropagation","share","setLocation","url","location","href","isLocalLocation","ref","host","test","pathname","isAnchorLocation","hash","watchLocation","BehaviorSubject","URL","watchLocationBase","base","location$","take","toString","replace","getLocationHash","substring","setLocationHash","addEventListener","click","watchLocationHash","watchMedia","query","media","matchMedia","Observable","subscriber","addListener","matches","toggles","drawer","search","getToggle","checked","setToggle","watchToggle","getViewportOffset","Math","max","pageXOffset","pageYOffset","setViewportOffset","scrollTo","getViewportSize","innerWidth","innerHeight","watchViewport","combineLatest","passive","offset","size","watchViewportAt","header$","viewport$","size$","distinctUntilKeyChanged","offset$","offsetLeft","offsetTop","watchWorker","worker","tx$","rx$","throttle","leading","trailing","message","postMessage","switchMapTo","isConfig","config","features","appendChild","child","innerHTML","Node","isArray","h","tag","attributes","children","attr","keys","setAttribute","cache","factory","sessionStorage","getItem","JSON","parse","value$","setItem","stringify","err","lang","translate","textContent","truncate","round","toFixed","len","charCodeAt","setupClipboard","dialog$","forEach","block","index","parent","parentElement","id","insertBefore","clipboard$","on","clearSelection","setupDialog","duration","dialog","classList","add","text","body","container","observeOn","animationFrame","delay","removeAttribute","remove","noop","setupInstantLoading","urls","history","scrollRestoration","favicon","state$","closest","includes","push$","pop$","state","distinctUntilChanged","prev","ajax$","skip","fetch","credentials","then","res","catchError","sample","pushState","dom","DOMParser","response","parseFromString","instant$","withLatestFrom","title","head","dispatchEvent","CustomEvent","debounceTime","replaceState","bufferCount","setupKeyboard","keyboard$","active","claim","els","indexOf","components$","setupComponents","names","reduce","components","useComponent","setAnchorBlur","resetAnchorBlur","setAnchorActive","toggle","resetAnchorActive","Flag","renderClipboardButton","class","renderSearchDocument","flag","PARENT","teaser","TEASER","missing","terms","flat","tabIndex","join","score","renderSearchResult","threshold","Infinity","docs","findIndex","doc","article","best","more","section","renderSource","facts","fact","renderTable","table","setSidebarOffset","style","top","resetSidebarOffset","setSidebarHeight","resetSidebarHeight","defaultTransform","split","trim","SearchMessageType","isSearchReadyMessage","READY","isSearchQueryMessage","QUERY","isSearchResultMessage","RESULT","setupSearchIndex","separator","pipeline","Boolean","setupSearchWorker","index$","base$","Worker","SETUP","mountNavigation","main$","screen$","screen","sidebar","watchSidebar","adjust","min","lock","a","b","applySidebar","mountTableOfContents","tablet$","tablet","sidebar$","anchors$","anchors","watchAnchorList","Map","decodeURIComponent","set","adjust$","header","path","anchor","pop","reverse","applyAnchorList","mountSearch","query$","reset$","result$","status$","status","mountSearchQuery","options","transform","fn","focus$","watchSearchQuery","mountSearchReset","watchSearchReset","addToSearchResultList","applySearchResult","ready$","fetch$","list","meta","setSearchResultMeta","resetSearchResultMeta","thresholds","scan","scrollHeight","resetSearchResultList","mountSearchResult","mountHeader","styles","getComputedStyle","position","sticky","watchHeader","type$","main","hx","zipWith","setHeaderTitleActive","resetHeaderTitleActive","applyHeaderType","mountMain","setHeaderShadow","resetHeaderShadow","border$","bottom","watchMain","complete","mountTabs","hidden","setTabsHidden","resetTabsHidden","applyTabs","isAppleDevice","navigator","userAgent","fetchSourceFacts","match","toLowerCase","user","repo","json","stargazers_count","forks_count","public_repos","fetchSourceFactsFromGitHub","slug","project","encodeURIComponent","star_count","fetchSourceFactsFromGitLab","setScrollLock","resetScrollLock","parseInt","initialize","SyntaxError","hash$","els$","scrollWidth","clientWidth","patchCodeBlocks","details","open","scrollIntoView","patchDetails","concatMap","script","src","onload","patchScripts","hasAttribute","patchSource","sentinel","patchTables","iif","patchScrollfix","navigation$","toc$","tabs$","search$","protocol","sort","charAt","link","visibility","values","documentElement"],"mappings":"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","file":"assets/javascripts/bundle.9554a270.min.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n \t\tvar executeModules = data[2];\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t\t// add entry modules from loaded chunk to deferred list\n \t\tdeferredModules.push.apply(deferredModules, executeModules || []);\n\n \t\t// run deferred modules when all chunks ready\n \t\treturn checkDeferredModules();\n \t};\n \tfunction checkDeferredModules() {\n \t\tvar result;\n \t\tfor(var i = 0; i < deferredModules.length; i++) {\n \t\t\tvar deferredModule = deferredModules[i];\n \t\t\tvar fulfilled = true;\n \t\t\tfor(var j = 1; j < deferredModule.length; j++) {\n \t\t\t\tvar depId = deferredModule[j];\n \t\t\t\tif(installedChunks[depId] !== 0) fulfilled = false;\n \t\t\t}\n \t\t\tif(fulfilled) {\n \t\t\t\tdeferredModules.splice(i--, 1);\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = deferredModule[0]);\n \t\t\t}\n \t\t}\n\n \t\treturn result;\n \t}\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t0: 0\n \t};\n\n \tvar deferredModules = [];\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \tvar jsonpArray = window[\"webpackJsonp\"] = window[\"webpackJsonp\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// add entry module to deferred list\n \tdeferredModules.push([74,1]);\n \t// run deferred modules when ready\n \treturn checkDeferredModules();\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { ReplaySubject, Subject, fromEvent } from \"rxjs\"\nimport { mapTo } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch document\n *\n * Documents must be implemented as subjects, so all downstream observables are\n * automatically updated when a new document is emitted. This enabled features\n * like instant loading.\n *\n * @return Document subject\n */\nexport function watchDocument(): Subject {\n const document$ = new ReplaySubject()\n fromEvent(document, \"DOMContentLoaded\")\n .pipe(\n mapTo(document)\n )\n .subscribe(document$)\n\n /* Return document */\n return document$\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve an element matching the query selector\n *\n * @template T - Element type\n *\n * @param selector - Query selector\n * @param node - Node of reference\n *\n * @return Element or nothing\n */\nexport function getElement(\n selector: string, node: ParentNode = document\n): T | undefined {\n return node.querySelector(selector) || undefined\n}\n\n/**\n * Retrieve an element matching a query selector or throw a reference error\n *\n * @template T - Element type\n *\n * @param selector - Query selector\n * @param node - Node of reference\n *\n * @return Element\n */\nexport function getElementOrThrow(\n selector: string, node: ParentNode = document\n): T {\n const el = getElement(selector, node)\n if (typeof el === \"undefined\")\n throw new ReferenceError(\n `Missing element: expected \"${selector}\" to be present`\n )\n return el\n}\n\n/**\n * Retrieve the currently active element\n *\n * @return Element or nothing\n */\nexport function getActiveElement(): HTMLElement | undefined {\n return document.activeElement instanceof HTMLElement\n ? document.activeElement\n : undefined\n}\n\n/**\n * Retrieve all elements matching the query selector\n *\n * @template T - Element type\n *\n * @param selector - Query selector\n * @param node - Node of reference\n *\n * @return Elements\n */\nexport function getElements(\n selector: string, node: ParentNode = document\n): T[] {\n return Array.from(node.querySelectorAll(selector))\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Create an element\n *\n * @template T - Tag name type\n *\n * @param tagName - Tag name\n *\n * @return Element\n */\nexport function createElement(\n tagName: T\n): HTMLElementTagNameMap[T] {\n return document.createElement(tagName)\n}\n\n/**\n * Replace an element with another element\n *\n * @param source - Source element\n * @param target - Target element\n */\nexport function replaceElement(\n source: HTMLElement, target: Node\n): void {\n source.replaceWith(target)\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, merge } from \"rxjs\"\nimport { map, startWith } from \"rxjs/operators\"\n\nimport { getActiveElement } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set element focus\n *\n * @param el - Element\n * @param value - Whether the element should be focused\n */\nexport function setElementFocus(\n el: HTMLElement, value: boolean = true\n): void {\n if (value)\n el.focus()\n else\n el.blur()\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch element focus\n *\n * @param el - Element\n *\n * @return Element focus observable\n */\nexport function watchElementFocus(\n el: HTMLElement\n): Observable {\n return merge(\n fromEvent(el, \"focus\"),\n fromEvent(el, \"blur\")\n )\n .pipe(\n map(({ type }) => type === \"focus\"),\n startWith(el === getActiveElement())\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, merge } from \"rxjs\"\nimport { map, startWith } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Element offset\n */\nexport interface ElementOffset {\n x: number /* Horizontal offset */\n y: number /* Vertical offset */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve element offset\n *\n * @param el - Element\n *\n * @return Element offset\n */\nexport function getElementOffset(el: HTMLElement): ElementOffset {\n return {\n x: el.scrollLeft,\n y: el.scrollTop\n }\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch element offset\n *\n * @param el - Element\n *\n * @return Element offset observable\n */\nexport function watchElementOffset(\n el: HTMLElement\n): Observable {\n return merge(\n fromEvent(el, \"scroll\"),\n fromEvent(window, \"resize\")\n )\n .pipe(\n map(() => getElementOffset(el)),\n startWith(getElementOffset(el))\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set element text selection\n *\n * @param el - Element\n */\nexport function setElementSelection(\n el: HTMLElement\n): void {\n if (el instanceof HTMLInputElement)\n el.select()\n else\n throw new Error(\"Not implemented\")\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport ResizeObserver from \"resize-observer-polyfill\"\nimport {\n NEVER,\n Observable,\n Subject,\n defer,\n merge,\n of\n} from \"rxjs\"\nimport {\n filter,\n finalize,\n map,\n shareReplay,\n startWith,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Element offset\n */\nexport interface ElementSize {\n width: number /* Element width */\n height: number /* Element height */\n}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Resize observer entry subject\n */\nconst entry$ = new Subject()\n\n/**\n * Resize observer observable\n *\n * This observable will create a `ResizeObserver` on the first subscription\n * and will automatically terminate it when there are no more subscribers.\n * It's quite important to centralize observation in a single `ResizeObserver`,\n * as the performance difference can be quite dramatic, as the link shows.\n *\n * @see https://bit.ly/3iIYfEm - Google Groups on performance\n */\nconst observer$ = defer(() => of(\n new ResizeObserver(entries => {\n for (const entry of entries)\n entry$.next(entry)\n })\n))\n .pipe(\n switchMap(resize => merge(of(resize), NEVER)\n .pipe(\n finalize(() => resize.disconnect())\n )\n ),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve element size\n *\n * @param el - Element\n *\n * @return Element size\n */\nexport function getElementSize(el: HTMLElement): ElementSize {\n return {\n width: el.offsetWidth,\n height: el.offsetHeight\n }\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch element size\n *\n * This function returns an observable that will subscribe to a single internal\n * instance of `ResizeObserver` upon subscription, and emit resize events until\n * termination. Note that this function should not be called with the same\n * element twice, as the first unsubscription will terminate observation.\n *\n * @param el - Element\n *\n * @return Element size observable\n */\nexport function watchElementSize(\n el: HTMLElement\n): Observable {\n return observer$\n .pipe(\n tap(observer => observer.observe(el)),\n switchMap(observer => entry$\n .pipe(\n filter(({ target }) => target === el),\n finalize(() => observer.unobserve(el)),\n map(({ contentRect }) => ({\n width: contentRect.width,\n height: contentRect.height\n }))\n )\n ),\n startWith(getElementSize(el))\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent } from \"rxjs\"\nimport { filter, map, share } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Key\n */\nexport interface Key {\n type: string /* Key type */\n claim(): void /* Key claim */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Check whether an element may receive keyboard input\n *\n * @param el - Element\n *\n * @return Test result\n */\nexport function isSusceptibleToKeyboard(el: HTMLElement): boolean {\n switch (el.tagName) {\n\n /* Form elements */\n case \"INPUT\":\n case \"SELECT\":\n case \"TEXTAREA\":\n return true\n\n /* Everything else */\n default:\n return el.isContentEditable\n }\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch keyboard\n *\n * @return Keyboard observable\n */\nexport function watchKeyboard(): Observable {\n return fromEvent(window, \"keydown\")\n .pipe(\n filter(ev => !(ev.metaKey || ev.ctrlKey)),\n map(ev => ({\n type: ev.key,\n claim() {\n ev.preventDefault()\n ev.stopPropagation()\n }\n })),\n share()\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { BehaviorSubject, Subject } from \"rxjs\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve location\n *\n * This function will return a `URL` object (and not `Location`) in order to\n * normalize typings across the application. Furthermore, locations need to be\n * tracked without setting them and `Location` is a singleton which represents\n * the current location.\n *\n * @return URL\n */\nexport function getLocation(): URL {\n return new URL(location.href)\n}\n\n/**\n * Set location\n *\n * @param url - URL to change to\n */\nexport function setLocation(url: URL): void {\n location.href = url.href\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Check whether a URL is a local link or a file (except `.html`)\n *\n * @param url - URL or HTML anchor element\n * @param ref - Reference URL\n *\n * @return Test result\n */\nexport function isLocalLocation(\n url: URL | HTMLAnchorElement,\n ref: URL | Location = location\n): boolean {\n return url.host === ref.host\n && /^(?:\\/[\\w-]+)*(?:\\/?|\\.html)$/i.test(url.pathname)\n}\n\n/**\n * Check whether a URL is an anchor link on the current page\n *\n * @param url - URL or HTML anchor element\n * @param ref - Reference URL\n *\n * @return Test result\n */\nexport function isAnchorLocation(\n url: URL | HTMLAnchorElement,\n ref: URL | Location = location\n): boolean {\n return url.pathname === ref.pathname\n && url.hash.length > 0\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch location\n *\n * @return Location subject\n */\nexport function watchLocation(): Subject {\n return new BehaviorSubject(getLocation())\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable } from \"rxjs\"\nimport { map, shareReplay, take } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n location$: Observable /* Location observable */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch location base\n *\n * @return Location base observable\n */\nexport function watchLocationBase(\n base: string, { location$ }: WatchOptions\n): Observable {\n return location$\n .pipe(\n take(1),\n map(({ href }) => new URL(base, href)\n .toString()\n .replace(/\\/$/, \"\")\n ),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent } from \"rxjs\"\nimport { filter, map, share, startWith } from \"rxjs/operators\"\n\nimport { createElement } from \"browser\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve location hash\n *\n * @return Location hash\n */\nexport function getLocationHash(): string {\n return location.hash.substring(1)\n}\n\n/**\n * Set location hash\n *\n * Setting a new fragment identifier via `location.hash` will have no effect\n * if the value doesn't change. When a new fragment identifier is set, we want\n * the browser to target the respective element at all times, which is why we\n * use this dirty little trick.\n *\n * @param hash - Location hash\n */\nexport function setLocationHash(hash: string): void {\n const el = createElement(\"a\")\n el.href = hash\n el.addEventListener(\"click\", ev => ev.stopPropagation())\n el.click()\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch location hash\n *\n * @return Location hash observable\n */\nexport function watchLocationHash(): Observable {\n return fromEvent(window, \"hashchange\")\n .pipe(\n map(getLocationHash),\n startWith(getLocationHash()),\n filter(hash => hash.length > 0),\n share()\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable } from \"rxjs\"\nimport { shareReplay, startWith } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch media query\n *\n * @param query - Media query\n *\n * @return Media observable\n */\nexport function watchMedia(query: string): Observable {\n const media = matchMedia(query)\n return new Observable(subscriber => {\n media.addListener(ev => subscriber.next(ev.matches))\n })\n .pipe(\n startWith(media.matches),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent } from \"rxjs\"\nimport { map, startWith } from \"rxjs/operators\"\n\nimport { getElementOrThrow } from \"../element\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Toggle\n */\nexport type Toggle =\n | \"drawer\" /* Toggle for drawer */\n | \"search\" /* Toggle for search */\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Toggle map\n */\nconst toggles: Record = {\n drawer: getElementOrThrow(`[data-md-toggle=drawer]`),\n search: getElementOrThrow(`[data-md-toggle=search]`)\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve the value of a toggle\n *\n * @param name - Toggle\n *\n * @return Toggle value\n */\nexport function getToggle(name: Toggle): boolean {\n return toggles[name].checked\n}\n\n/**\n * Set toggle\n *\n * Simulating a click event seems to be the most cross-browser compatible way\n * of changing the value while also emitting a `change` event. Before, Material\n * used `CustomEvent` to programmatically change the value of a toggle, but this\n * is a much simpler and cleaner solution which doesn't require a polyfill.\n *\n * @param name - Toggle\n * @param value - Toggle value\n */\nexport function setToggle(name: Toggle, value: boolean): void {\n if (toggles[name].checked !== value)\n toggles[name].click()\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch toggle\n *\n * @param name - Toggle\n *\n * @return Toggle value observable\n */\nexport function watchToggle(name: Toggle): Observable {\n const el = toggles[name]\n return fromEvent(el, \"change\")\n .pipe(\n map(() => el.checked),\n startWith(el.checked)\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, merge } from \"rxjs\"\nimport { map, startWith } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Viewport offset\n */\nexport interface ViewportOffset {\n x: number /* Horizontal offset */\n y: number /* Vertical offset */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve viewport offset\n *\n * On iOS Safari, viewport offset can be negative due to overflow scrolling.\n * As this may induce strange behaviors downstream, we'll just limit it to 0.\n *\n * @return Viewport offset\n */\nexport function getViewportOffset(): ViewportOffset {\n return {\n x: Math.max(0, pageXOffset),\n y: Math.max(0, pageYOffset)\n }\n}\n\n/**\n * Set viewport offset\n *\n * @param offset - Viewport offset\n */\nexport function setViewportOffset(\n { x, y }: Partial\n): void {\n window.scrollTo(x || 0, y || 0)\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch viewport offset\n *\n * @return Viewport offset observable\n */\nexport function watchViewportOffset(): Observable {\n return merge(\n fromEvent(window, \"scroll\", { passive: true }),\n fromEvent(window, \"resize\", { passive: true })\n )\n .pipe(\n map(getViewportOffset),\n startWith(getViewportOffset())\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent } from \"rxjs\"\nimport { map, startWith } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Viewport size\n */\nexport interface ViewportSize {\n width: number /* Viewport width */\n height: number /* Viewport height */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Retrieve viewport size\n *\n * @return Viewport size\n */\nexport function getViewportSize(): ViewportSize {\n return {\n width: innerWidth,\n height: innerHeight\n }\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Watch viewport size\n *\n * @return Viewport size observable\n */\nexport function watchViewportSize(): Observable {\n return fromEvent(window, \"resize\", { passive: true })\n .pipe(\n map(getViewportSize),\n startWith(getViewportSize())\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, combineLatest } from \"rxjs\"\nimport {\n distinctUntilKeyChanged,\n map,\n shareReplay\n} from \"rxjs/operators\"\n\nimport { Header } from \"components\"\n\nimport {\n ViewportOffset,\n watchViewportOffset\n} from \"../offset\"\nimport {\n ViewportSize,\n watchViewportSize\n} from \"../size\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Viewport\n */\nexport interface Viewport {\n offset: ViewportOffset /* Viewport offset */\n size: ViewportSize /* Viewport size */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch at options\n */\ninterface WatchAtOptions {\n header$: Observable
    /* Header observable */\n viewport$: Observable /* Viewport observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch viewport\n *\n * @return Viewport observable\n */\nexport function watchViewport(): Observable {\n return combineLatest([\n watchViewportOffset(),\n watchViewportSize()\n ])\n .pipe(\n map(([offset, size]) => ({ offset, size })),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n}\n\n/**\n * Watch viewport relative to element\n *\n * @param el - Element\n * @param options - Options\n *\n * @return Viewport observable\n */\nexport function watchViewportAt(\n el: HTMLElement, { header$, viewport$ }: WatchAtOptions\n): Observable {\n const size$ = viewport$\n .pipe(\n distinctUntilKeyChanged(\"size\")\n )\n\n /* Compute element offset */\n const offset$ = combineLatest([size$, header$])\n .pipe(\n map((): ViewportOffset => ({\n x: el.offsetLeft,\n y: el.offsetTop\n }))\n )\n\n /* Compute relative viewport, return hot observable */\n return combineLatest([header$, viewport$, offset$])\n .pipe(\n map(([{ height }, { offset, size }, { x, y }]) => ({\n offset: {\n x: offset.x - x,\n y: offset.y - y + height\n },\n size\n }))\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, Subject, fromEvent } from \"rxjs\"\nimport {\n map,\n share,\n switchMapTo,\n tap,\n throttle\n} from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Worker message\n */\nexport interface WorkerMessage {\n type: unknown /* Message type */\n data?: unknown /* Message data */\n}\n\n/**\n * Worker handler\n *\n * @template T - Message type\n */\nexport interface WorkerHandler<\n T extends WorkerMessage\n> {\n tx$: Subject /* Message transmission subject */\n rx$: Observable /* Message receive observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n *\n * @template T - Worker message type\n */\ninterface WatchOptions {\n tx$: Observable /* Message transmission observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch a web worker\n *\n * This function returns an observable that will send all values emitted by the\n * message observable to the web worker. Web worker communication is expected\n * to be bidirectional (request-response) and synchronous. Messages that are\n * emitted during a pending request are throttled, the last one is emitted.\n *\n * @param worker - Web worker\n * @param options - Options\n *\n * @return Worker message observable\n */\nexport function watchWorker(\n worker: Worker, { tx$ }: WatchOptions\n): Observable {\n\n /* Intercept messages from worker-like objects */\n const rx$ = fromEvent(worker, \"message\")\n .pipe(\n map(({ data }) => data)\n )\n\n /* Send and receive messages, return hot observable */\n return tx$\n .pipe(\n throttle(() => rx$, { leading: true, trailing: true }),\n tap(message => worker.postMessage(message)),\n switchMapTo(rx$),\n share()\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { SearchIndex, SearchTransformFn } from \"integrations\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Feature flags\n */\nexport type Feature =\n | \"navigation.tabs\" /* Tabs navigation */\n | \"navigation.instant\" /* Instant loading */\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Configuration\n */\nexport interface Config {\n base: string /* Base URL */\n features: Feature[] /* Feature flags */\n search: {\n worker: string /* Worker URL */\n index?: Promise /* Promise resolving with index */\n transform?: SearchTransformFn /* Transformation function */\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Ensure that the given value is a valid configuration\n *\n * We could use `jsonschema` or any other schema validation framework, but that\n * would just add more bloat to the bundle, so we'll keep it plain and simple.\n *\n * @param config - Configuration\n *\n * @return Test result\n */\nexport function isConfig(config: any): config is Config {\n return typeof config === \"object\"\n && typeof config.base === \"string\"\n && typeof config.features === \"object\"\n && typeof config.search === \"object\"\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n// tslint:disable no-null-keyword\n\nimport { JSX as JSXInternal } from \"preact\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * HTML attributes\n */\ntype Attributes =\n & JSXInternal.HTMLAttributes\n & JSXInternal.SVGAttributes\n & Record\n\n/**\n * Child element\n */\ntype Child =\n | HTMLElement\n | Text\n | string\n | number\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Append a child node to an element\n *\n * @param el - Element\n * @param child - Child node(s)\n */\nfunction appendChild(el: HTMLElement, child: Child | Child[]): void {\n\n /* Handle primitive types (including raw HTML) */\n if (typeof child === \"string\" || typeof child === \"number\") {\n el.innerHTML += child.toString()\n\n /* Handle nodes */\n } else if (child instanceof Node) {\n el.appendChild(child)\n\n /* Handle nested children */\n } else if (Array.isArray(child)) {\n for (const node of child)\n appendChild(el, node)\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * JSX factory\n *\n * @param tag - HTML tag\n * @param attributes - HTML attributes\n * @param children - Child elements\n *\n * @return Element\n */\nexport function h(\n tag: string, attributes: Attributes | null, ...children: Child[]\n): HTMLElement {\n const el = document.createElement(tag)\n\n /* Set attributes, if any */\n if (attributes)\n for (const attr of Object.keys(attributes))\n if (typeof attributes[attr] !== \"boolean\")\n el.setAttribute(attr, attributes[attr])\n else if (attributes[attr])\n el.setAttribute(attr, \"\")\n\n /* Append child nodes */\n for (const child of children)\n appendChild(el, child)\n\n /* Return element */\n return el\n}\n\n/* ----------------------------------------------------------------------------\n * Namespace\n * ------------------------------------------------------------------------- */\n\nexport declare namespace h {\n namespace JSX {\n type Element = HTMLElement\n type IntrinsicElements = JSXInternal.IntrinsicElements\n }\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, defer, of } from \"rxjs\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Cache the last value emitted by an observable in session storage\n *\n * If the key is not found in session storage, the factory is executed and the\n * latest value emitted will automatically be persisted to sessions storage.\n * Note that the values emitted by the returned observable must be serializable\n * as `JSON`, or data will be lost.\n *\n * @template T - Value type\n *\n * @param key - Cache key\n * @param factory - Observable factory\n *\n * @return Value observable\n */\nexport function cache(\n key: string, factory: () => Observable\n): Observable {\n return defer(() => {\n const data = sessionStorage.getItem(key)\n if (data) {\n return of(JSON.parse(data) as T)\n\n /* Retrieve value from observable factory and write to storage */\n } else {\n const value$ = factory()\n value$.subscribe(value => {\n try {\n sessionStorage.setItem(key, JSON.stringify(value))\n } catch (err) {\n /* Uncritical, just swallow */\n }\n })\n\n /* Return value */\n return value$\n }\n })\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { getElementOrThrow } from \"browser\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Translation keys\n */\ntype TranslateKey =\n | \"clipboard.copy\" /* Copy to clipboard */\n | \"clipboard.copied\" /* Copied to clipboard */\n | \"search.config.lang\" /* Search language */\n | \"search.config.pipeline\" /* Search pipeline */\n | \"search.config.separator\" /* Search separator */\n | \"search.result.placeholder\" /* Type to start searching */\n | \"search.result.none\" /* No matching documents */\n | \"search.result.one\" /* 1 matching document */\n | \"search.result.other\" /* # matching documents */\n | \"search.result.more.one\" /* 1 more on this page */\n | \"search.result.more.other\" /* # more on this page */\n | \"search.result.term.missing\" /* Missing */\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Translations\n */\nlet lang: Record\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Translate the given key\n *\n * @param key - Key to be translated\n * @param value - Value to be replaced\n *\n * @return Translation\n */\nexport function translate(\n key: TranslateKey, value?: string | number\n): string {\n if (typeof lang === \"undefined\") {\n const el = getElementOrThrow(\"#__lang\")\n lang = JSON.parse(el.textContent!)\n }\n if (typeof lang[key] === \"undefined\") {\n throw new ReferenceError(`Invalid translation: ${key}`)\n }\n return typeof value !== \"undefined\"\n ? lang[key].replace(\"#\", value.toString())\n : lang[key]\n}\n\n/**\n * Truncate a string after the given number of characters\n *\n * This is not a very reasonable approach, since the summaries kind of suck.\n * It would be better to create something more intelligent, highlighting the\n * search occurrences and making a better summary out of it, but this note was\n * written three years ago, so who knows if we'll ever fix it.\n *\n * @param value - Value to be truncated\n * @param n - Number of characters\n *\n * @return Truncated value\n */\nexport function truncate(value: string, n: number): string {\n let i = n\n if (value.length > i) {\n while (value[i] !== \" \" && --i > 0); // tslint:disable-line\n return `${value.substring(0, i)}...`\n }\n return value\n}\n\n/**\n * Round a number for display with source facts\n *\n * This is a reverse engineered version of GitHub's weird rounding algorithm\n * for stars, forks and all other numbers. While all numbers below `1,000` are\n * returned as-is, bigger numbers are converted to fixed numbers:\n *\n * - `1,049` => `1k`\n * - `1,050` => `1.1k`\n * - `1,949` => `1.9k`\n * - `1,950` => `2k`\n *\n * @param value - Original value\n *\n * @return Rounded value\n */\nexport function round(value: number): string {\n if (value > 999) {\n const digits = +((value - 950) % 1000 > 99)\n return `${((value + 0.000001) / 1000).toFixed(digits)}k`\n } else {\n return value.toString()\n }\n}\n\n/**\n * Simple hash function\n *\n * @see https://bit.ly/2wsVjJ4 - Original source\n *\n * @param value - Value to be hashed\n *\n * @return Hash as 32bit integer\n */\nexport function hash(value: string): number {\n let h = 0\n for (let i = 0, len = value.length; i < len; i++) {\n h = ((h << 5) - h) + value.charCodeAt(i)\n h |= 0 // Convert to 32bit integer\n }\n return h\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nexport * from \"./_\"\nexport * from \"./header\"\nexport * from \"./main\"\nexport * from \"./navigation\"\nexport * from \"./search\"\nexport * from \"./shared\"\nexport * from \"./tabs\"\nexport * from \"./toc\"\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport * as ClipboardJS from \"clipboard\"\nimport { NEVER, Observable, Subject } from \"rxjs\"\nimport { mapTo, share, tap } from \"rxjs/operators\"\n\nimport { getElements } from \"browser\"\nimport { renderClipboardButton } from \"templates\"\nimport { translate } from \"utilities\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Setup options\n */\ninterface SetupOptions {\n document$: Observable /* Document observable */\n dialog$: Subject /* Dialog subject */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up clipboard\n *\n * This function implements the Clipboard.js integration and injects a button\n * into all code blocks when the document changes.\n *\n * @param options - Options\n *\n * @return Clipboard observable\n */\nexport function setupClipboard(\n { document$, dialog$ }: SetupOptions\n): Observable {\n if (!ClipboardJS.isSupported())\n return NEVER\n\n /* Inject 'copy-to-clipboard' buttons */\n document$.subscribe(() => {\n const blocks = getElements(\"pre > code\")\n blocks.forEach((block, index) => {\n const parent = block.parentElement!\n parent.id = `__code_${index}`\n parent.insertBefore(\n renderClipboardButton(parent.id),\n block\n )\n })\n })\n\n /* Initialize clipboard */\n const clipboard$ = new Observable(subscriber => {\n new ClipboardJS(\".md-clipboard\").on(\"success\", ev => subscriber.next(ev))\n })\n .pipe(\n share()\n )\n\n /* Display notification for clipboard event */\n clipboard$\n .pipe(\n tap(ev => ev.clearSelection()),\n mapTo(translate(\"clipboard.copied\"))\n )\n .subscribe(dialog$)\n\n /* Return clipboard */\n return clipboard$\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Subject, animationFrameScheduler, noop, of } from \"rxjs\"\nimport {\n delay,\n map,\n observeOn,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\nimport { createElement } from \"browser\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Setup options\n */\ninterface SetupOptions {\n duration?: number /* Display duration (default: 2s) */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up dialog\n *\n * @param options - Options\n *\n * @return Dialog observable\n */\nexport function setupDialog(\n { duration }: SetupOptions = {}\n): Subject {\n const dialog$ = new Subject()\n\n /* Create dialog */\n const dialog = createElement(\"div\") // TODO: improve scoping\n dialog.classList.add(\"md-dialog\", \"md-typeset\")\n\n /* Display dialog */\n dialog$\n .pipe(\n switchMap(text => of(document.body) // useComponent(\"container\")\n .pipe(\n map(container => container.appendChild(dialog)),\n observeOn(animationFrameScheduler),\n delay(1), // Strangley it doesnt work when we push things to the new animation frame...\n tap(el => {\n el.innerHTML = text\n el.setAttribute(\"data-md-state\", \"open\")\n }),\n delay(duration || 2000),\n tap(el => el.removeAttribute(\"data-md-state\")),\n delay(400),\n tap(el => {\n el.innerHTML = \"\"\n el.remove()\n })\n )\n )\n )\n .subscribe(noop)\n\n /* Return dialog */\n return dialog$\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { NEVER, Observable, Subject, from, fromEvent, merge, of } from \"rxjs\"\nimport {\n bufferCount,\n catchError,\n debounceTime,\n distinctUntilChanged,\n distinctUntilKeyChanged,\n filter,\n map,\n sample,\n share,\n skip,\n switchMap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport {\n Viewport,\n ViewportOffset,\n getElement,\n isAnchorLocation,\n isLocalLocation,\n replaceElement,\n setLocation,\n setLocationHash,\n setToggle,\n setViewportOffset\n} from \"browser\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * History state\n */\ninterface State {\n url: URL /* State URL */\n offset?: ViewportOffset /* State viewport offset */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Setup options\n */\ninterface SetupOptions {\n document$: Subject /* Document subject */\n location$: Subject /* Location subject */\n viewport$: Observable /* Viewport observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up instant loading\n *\n * When fetching, theoretically, we could use `responseType: \"document\"`, but\n * since all MkDocs links are relative, we need to make sure that the current\n * location matches the document we just loaded. Otherwise any relative links\n * in the document could use the old location.\n *\n * This is the reason why we need to synchronize history events and the process\n * of fetching the document for navigation changes (except `popstate` events):\n *\n * 1. Fetch document via `XMLHTTPRequest`\n * 2. Set new location via `history.pushState`\n * 3. Parse and emit fetched document\n *\n * For `popstate` events, we must not use `history.pushState`, or the forward\n * history will be irreversibly overwritten. In case the request fails, the\n * location change is dispatched regularly.\n *\n * @param options - Options\n */\nexport function setupInstantLoading(\n urls: string[], { document$, viewport$, location$ }: SetupOptions\n): void {\n\n /* Disable automatic scroll restoration */\n if (\"scrollRestoration\" in history)\n history.scrollRestoration = \"manual\"\n\n /* Hack: ensure that reloads restore viewport offset */\n fromEvent(window, \"beforeunload\")\n .subscribe(() => {\n history.scrollRestoration = \"auto\"\n })\n\n /* Hack: ensure absolute favicon link to omit 404s on document switch */\n const favicon = getElement(`link[rel=\"shortcut icon\"]`)\n if (typeof favicon !== \"undefined\")\n favicon.href = favicon.href // tslint:disable-line no-self-assignment\n\n /* Intercept link clicks and convert to state change */\n const state$ = fromEvent(document.body, \"click\")\n .pipe(\n filter(ev => !(ev.metaKey || ev.ctrlKey)),\n switchMap(ev => {\n if (ev.target instanceof HTMLElement) {\n const el = ev.target.closest(\"a\")\n if (\n el && !el.target &&\n isLocalLocation(el) &&\n urls.includes(el.href)\n ) {\n if (!isAnchorLocation(el))\n ev.preventDefault()\n return of(el)\n }\n }\n return NEVER\n }),\n map(el => ({ url: new URL(el.href) })),\n share()\n )\n\n /* Always close search on link click */\n state$.subscribe(() => {\n setToggle(\"search\", false)\n })\n\n /* Filter state changes to dispatch */\n const push$ = state$\n .pipe(\n filter(({ url }) => !isAnchorLocation(url)),\n share()\n )\n\n /* Intercept popstate events (history back and forward) */\n const pop$ = fromEvent(window, \"popstate\")\n .pipe(\n filter(ev => ev.state !== null),\n map(ev => ({\n url: new URL(location.href),\n offset: ev.state\n })),\n share()\n )\n\n /* Emit location change */\n merge(push$, pop$)\n .pipe(\n distinctUntilChanged((prev, next) => prev.url.href === next.url.href),\n map(({ url }) => url)\n )\n .subscribe(location$)\n\n /* Fetch document on location change */\n const ajax$ = location$\n .pipe(\n distinctUntilKeyChanged(\"pathname\"),\n skip(1),\n switchMap(url => from(fetch(url.href, {\n credentials: \"same-origin\"\n }).then(res => res.text()))\n .pipe(\n catchError(() => {\n setLocation(url)\n return NEVER\n })\n )\n ),\n share()\n )\n\n /* Set new location as soon as the document was fetched */\n push$\n .pipe(\n sample(ajax$)\n )\n .subscribe(({ url }) => {\n history.pushState({}, \"\", url.toString())\n })\n\n /* Parse and emit document */\n const dom = new DOMParser()\n ajax$\n .pipe(\n map(response => dom.parseFromString(response, \"text/html\"))\n )\n .subscribe(document$)\n\n /* Intercept instant loading */\n const instant$ = merge(push$, pop$)\n .pipe(\n sample(document$)\n )\n\n // TODO: this must be combined with search scroll restoration on mobile\n instant$.subscribe(({ url, offset }) => {\n if (url.hash && !offset) {\n setLocationHash(url.hash)\n } else {\n setViewportOffset(offset || { y: 0 })\n }\n })\n\n /* Replace document metadata */\n instant$\n .pipe(\n withLatestFrom(document$)\n )\n .subscribe(([, { title, head }]) => {\n document.title = title\n\n /* Replace meta tags */\n for (const selector of [\n `link[rel=\"canonical\"]`,\n `meta[name=\"author\"]`,\n `meta[name=\"description\"]`\n ]) {\n const next = getElement(selector, head)\n const prev = getElement(selector, document.head)\n if (\n typeof next !== \"undefined\" &&\n typeof prev !== \"undefined\"\n ) {\n replaceElement(prev, next)\n }\n }\n\n /* Finished, dispatch document switch event */\n document.dispatchEvent(new CustomEvent(\"DOMContentSwitch\"))\n })\n\n /* Debounce update of viewport offset */\n viewport$\n .pipe(\n debounceTime(250),\n distinctUntilKeyChanged(\"offset\")\n )\n .subscribe(({ offset }) => {\n history.replaceState(offset, \"\")\n })\n\n /* Set viewport offset from history */\n merge(state$, pop$)\n .pipe(\n bufferCount(2, 1),\n filter(([prev, next]) => {\n return prev.url.pathname === next.url.pathname\n && !isAnchorLocation(next.url)\n }),\n map(([, state]) => state)\n )\n .subscribe(({ offset }) => {\n setViewportOffset(offset || { y: 0 })\n })\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable } from \"rxjs\"\nimport {\n filter,\n map,\n share,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport {\n Key,\n getActiveElement,\n getElement,\n getElements,\n getToggle,\n isSusceptibleToKeyboard,\n setElementFocus,\n setElementSelection,\n setToggle,\n watchKeyboard\n} from \"browser\"\nimport { useComponent } from \"components\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Keyboard mode\n */\nexport type KeyboardMode =\n | \"global\" /* Global */\n | \"search\" /* Search is open */\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Keyboard\n */\nexport interface Keyboard extends Key {\n mode: KeyboardMode /* Keyboard mode */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up keyboard\n *\n * This function will set up the keyboard handlers and ensure that keys are\n * correctly propagated. Currently there are two modes:\n *\n * - `global`: This mode is active when the search is closed. It is intended\n * to assign hotkeys to specific functions of the site. Currently the search,\n * previous and next page can be triggered.\n *\n * - `search`: This mode is active when the search is open. It maps certain\n * navigational keys to offer search results that can be entirely navigated\n * through keyboard input.\n *\n * The keyboard observable is returned and can be used to monitor the keyboard\n * in order toassign further hotkeys to custom functions.\n *\n * @return Keyboard observable\n */\nexport function setupKeyboard(): Observable {\n const keyboard$ = watchKeyboard()\n .pipe(\n map(key => ({\n mode: getToggle(\"search\") ? \"search\" : \"global\",\n ...key\n })),\n filter(({ mode }) => {\n if (mode === \"global\") {\n const active = getActiveElement()\n if (typeof active !== \"undefined\")\n return !isSusceptibleToKeyboard(active)\n }\n return true\n }),\n share()\n )\n\n /* Set up search keyboard handlers */\n keyboard$\n .pipe(\n filter(({ mode }) => mode === \"search\"),\n withLatestFrom(\n useComponent(\"search-query\"),\n useComponent(\"search-result\")\n )\n )\n .subscribe(([key, query, result]) => {\n const active = getActiveElement()\n switch (key.type) {\n\n /* Enter: prevent form submission */\n case \"Enter\":\n if (active === query)\n key.claim()\n break\n\n /* Escape or Tab: close search */\n case \"Escape\":\n case \"Tab\":\n setToggle(\"search\", false)\n setElementFocus(query, false)\n break\n\n /* Vertical arrows: select previous or next search result */\n case \"ArrowUp\":\n case \"ArrowDown\":\n if (typeof active === \"undefined\") {\n setElementFocus(query)\n } else {\n const els = [query, ...getElements(\n \":not(details) > [href], summary, details[open] [href]\",\n result\n )]\n const i = Math.max(0, (\n Math.max(0, els.indexOf(active)) + els.length + (\n key.type === \"ArrowUp\" ? -1 : +1\n )\n ) % els.length)\n setElementFocus(els[i])\n }\n\n /* Prevent scrolling of page */\n key.claim()\n break\n\n /* All other keys: hand to search query */\n default:\n if (query !== getActiveElement())\n setElementFocus(query)\n }\n })\n\n /* Set up global keyboard handlers */\n keyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\"),\n withLatestFrom(useComponent(\"search-query\"))\n )\n .subscribe(([key, query]) => {\n switch (key.type) {\n\n /* Open search and select query */\n case \"f\":\n case \"s\":\n case \"/\":\n setElementFocus(query)\n setElementSelection(query)\n key.claim()\n break\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getElement(\"[href][rel=prev]\")\n if (typeof prev !== \"undefined\")\n prev.click()\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getElement(\"[href][rel=next]\")\n if (typeof next !== \"undefined\")\n next.click()\n break\n }\n })\n\n /* Return keyboard */\n return keyboard$\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { EMPTY, Observable, of } from \"rxjs\"\nimport {\n distinctUntilChanged,\n map,\n scan,\n shareReplay,\n switchMap\n} from \"rxjs/operators\"\n\nimport { getElement, replaceElement } from \"browser\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Component\n */\nexport type Component =\n | \"announce\" /* Announcement bar */\n | \"container\" /* Container */\n | \"header\" /* Header */\n | \"header-title\" /* Header title */\n | \"main\" /* Main area */\n | \"navigation\" /* Navigation */\n | \"search\" /* Search */\n | \"search-query\" /* Search input */\n | \"search-reset\" /* Search reset */\n | \"search-result\" /* Search results */\n | \"skip\" /* Skip link */\n | \"tabs\" /* Tabs */\n | \"toc\" /* Table of contents */\n\n/**\n * Component map\n */\nexport type ComponentMap = {\n [P in Component]?: HTMLElement\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n document$: Observable /* Document observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Component map observable\n */\nlet components$: Observable\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up bindings to components with given names\n *\n * This function will maintain bindings to the elements identified by the given\n * names in-between document switches and update the elements in-place.\n *\n * @param names - Component names\n * @param options - Options\n */\nexport function setupComponents(\n names: Component[], { document$ }: WatchOptions\n): void {\n components$ = document$\n .pipe(\n\n /* Build component map */\n map(document => names.reduce((components, name) => {\n const el = getElement(`[data-md-component=${name}]`, document)\n return {\n ...components,\n ...typeof el !== \"undefined\" ? { [name]: el } : {}\n }\n }, {})),\n\n /* Re-compute component map on document switch */\n scan((prev, next) => {\n for (const name of names) {\n switch (name) {\n\n /* Top-level components: update */\n case \"announce\":\n case \"header-title\":\n case \"container\":\n case \"skip\":\n if (name in prev && typeof prev[name] !== \"undefined\") {\n replaceElement(prev[name]!, next[name]!)\n prev[name] = next[name]\n }\n break\n\n /* All other components: rebind */\n default:\n if (typeof next[name] !== \"undefined\")\n prev[name] = getElement(`[data-md-component=${name}]`)\n else\n delete prev[name]\n }\n }\n return prev\n }),\n\n /* Convert to hot observable */\n shareReplay({ bufferSize: 1, refCount: true })\n )\n}\n\n/**\n * Retrieve a component\n *\n * The returned observable will only re-emit if the element changed, i.e. if\n * it was replaced from a document which was switched to.\n *\n * @template T - Element type\n *\n * @param name - Component name\n *\n * @return Component observable\n */\nexport function useComponent(\n name: \"search-query\"\n): Observable\nexport function useComponent(\n name: Component\n): Observable\nexport function useComponent(\n name: Component\n): Observable {\n return components$\n .pipe(\n switchMap(components => (\n typeof components[name] !== \"undefined\"\n ? of(components[name] as T)\n : EMPTY\n )),\n distinctUntilChanged()\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set anchor blur\n *\n * @param el - Anchor element\n * @param value - Whether the anchor is blurred\n */\nexport function setAnchorBlur(\n el: HTMLElement, value: boolean\n): void {\n el.setAttribute(\"data-md-state\", value ? \"blur\" : \"\")\n}\n\n/**\n * Reset anchor blur\n *\n * @param el - Anchor element\n */\nexport function resetAnchorBlur(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Set anchor active\n *\n * @param el - Anchor element\n * @param value - Whether the anchor is active\n */\nexport function setAnchorActive(\n el: HTMLElement, value: boolean\n): void {\n el.classList.toggle(\"md-nav__link--active\", value)\n}\n\n/**\n * Reset anchor active\n *\n * @param el - Anchor element\n */\nexport function resetAnchorActive(\n el: HTMLElement\n): void {\n el.classList.remove(\"md-nav__link--active\")\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nexport * from \"./sidebar\"\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n SearchDocument,\n SearchMetadata,\n SearchResult\n} from \"integrations/search\"\nimport { h, translate, truncate } from \"utilities\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Render flag\n */\nconst enum Flag {\n TEASER = 1, /* Render teaser */\n PARENT = 2 /* Render as parent */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper function\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a search document\n *\n * @param section - Search document\n * @param flag - Render flags\n *\n * @return Element\n */\nfunction renderSearchDocument(\n document: SearchDocument & SearchMetadata, flag: Flag\n) {\n const parent = flag & Flag.PARENT\n const teaser = flag & Flag.TEASER\n\n /* Render missing query terms */\n const missing = Object.keys(document.terms)\n .filter(key => !document.terms[key])\n .map(key => [{key}, \" \"])\n .flat()\n .slice(0, -1)\n\n /* Render article or section, depending on flags */\n const url = document.location\n return (\n \n \n {parent > 0 &&
    }\n

    {document.title}

    \n {teaser > 0 && document.text.length > 0 &&\n

    \n {truncate(document.text, 320)}\n

    \n }\n {teaser > 0 && missing.length > 0 &&\n

    \n {translate(\"search.result.term.missing\")}: {...missing}\n

    \n }\n \n
    \n )\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a search result\n *\n * @param result - Search result\n * @param threshold - Score threshold\n *\n * @return Element\n */\nexport function renderSearchResult(\n result: SearchResult, threshold: number = Infinity\n) {\n const docs = [...result]\n\n /* Find and extract parent article */\n const parent = docs.findIndex(doc => !doc.location.includes(\"#\"))\n const [article] = docs.splice(parent, 1)\n\n /* Determine last index above threshold */\n let index = docs.findIndex(doc => doc.score < threshold)\n if (index === -1)\n index = docs.length\n\n /* Partition sections */\n const best = docs.slice(0, index)\n const more = docs.slice(index)\n\n /* Render children */\n const children = [\n renderSearchDocument(article, Flag.PARENT | +(!parent && index === 0)),\n ...best.map(section => renderSearchDocument(section, Flag.TEASER)),\n ...more.length ? [\n
    \n \n {more.length > 0 && more.length === 1\n ? translate(\"search.result.more.one\")\n : translate(\"search.result.more.other\", more.length)\n }\n \n {...more.map(section => renderSearchDocument(section, Flag.TEASER))}\n
    \n ] : []\n ]\n\n /* Render search result */\n return (\n
  1. \n {children}\n
  2. \n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { h, translate } from \"utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a 'copy-to-clipboard' button\n *\n * @param id - Unique identifier\n *\n * @return Element\n */\nexport function renderClipboardButton(id: string) {\n return (\n code`}\n >\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { SourceFacts } from \"patches/source\"\nimport { h } from \"utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render source facts\n *\n * @param facts - Source facts\n *\n * @return Element\n */\nexport function renderSource(\n facts: SourceFacts\n) {\n return (\n
      \n {facts.map(fact => (\n
    • {fact}
    • \n ))}\n
    \n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { h } from \"utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Render a table inside a wrapper to improve scrolling on mobile\n *\n * @param table - Table element\n *\n * @return Element\n */\nexport function renderTable(\n table: HTMLTableElement\n) {\n return (\n
    \n
    \n {table}\n
    \n
    \n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set sidebar offset\n *\n * @param el - Sidebar element\n * @param value - Sidebar offset\n */\nexport function setSidebarOffset(\n el: HTMLElement, value: number\n): void {\n el.style.top = `${value}px`\n}\n\n/**\n * Reset sidebar offset\n *\n * @param el - Sidebar element\n */\nexport function resetSidebarOffset(\n el: HTMLElement\n): void {\n el.style.top = \"\"\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Set sidebar height\n *\n * @param el - Sidebar element\n * @param value - Sidebar height\n */\nexport function setSidebarHeight(\n el: HTMLElement, value: number\n): void {\n el.style.height = `${value}px`\n}\n\n/**\n * Reset sidebar height\n *\n * @param el - Sidebar element\n */\nexport function resetSidebarHeight(\n el: HTMLElement\n): void {\n el.style.height = \"\"\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nexport * from \"./_\"\nexport * from \"./react\"\nexport * from \"./set\"\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search transformation function\n *\n * @param value - Query value\n *\n * @return Transformed query value\n */\nexport type SearchTransformFn = (value: string) => string\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Default transformation function\n *\n * 1. Search for terms in quotation marks and prepend a `+` modifier to denote\n * that the resulting document must contain all terms, converting the query\n * to an `AND` query (as opposed to the default `OR` behavior). While users\n * may expect terms enclosed in quotation marks to map to span queries, i.e.\n * for which order is important, `lunr` doesn't support them, so the best\n * we can do is to convert the terms to an `AND` query.\n *\n * 2. Replace control characters which are not located at the beginning of the\n * query or preceded by white space, or are not followed by a non-whitespace\n * character or are at the end of the query string. Furthermore, filter\n * unmatched quotation marks.\n *\n * 3. Trim excess whitespace from left and right.\n *\n * @param query - Query value\n *\n * @return Transformed query value\n */\nexport function defaultTransform(query: string): string {\n return query\n .split(/\"([^\"]+)\"/g) /* => 1 */\n .map((terms, index) => index & 1\n ? terms.replace(/^\\b|^(?![^\\x00-\\x7F]|$)|\\s+/g, \" +\")\n : terms\n )\n .join(\"\")\n .replace(/\"|(?:^|\\s+)[*+\\-:^~]+(?=\\s+|$)/g, \"\") /* => 2 */\n .trim() /* => 3 */\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A RTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { SearchIndex, SearchResult } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search message type\n */\nexport const enum SearchMessageType {\n SETUP, /* Search index setup */\n READY, /* Search index ready */\n QUERY, /* Search query */\n RESULT /* Search results */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * A message containing the data necessary to setup the search index\n */\nexport interface SearchSetupMessage {\n type: SearchMessageType.SETUP /* Message type */\n data: SearchIndex /* Message data */\n}\n\n/**\n * A message indicating the search index is ready\n */\nexport interface SearchReadyMessage {\n type: SearchMessageType.READY /* Message type */\n}\n\n/**\n * A message containing a search query\n */\nexport interface SearchQueryMessage {\n type: SearchMessageType.QUERY /* Message type */\n data: string /* Message data */\n}\n\n/**\n * A message containing results for a search query\n */\nexport interface SearchResultMessage {\n type: SearchMessageType.RESULT /* Message type */\n data: SearchResult[] /* Message data */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * A message exchanged with the search worker\n */\nexport type SearchMessage =\n | SearchSetupMessage\n | SearchReadyMessage\n | SearchQueryMessage\n | SearchResultMessage\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Type guard for search setup messages\n *\n * @param message - Search worker message\n *\n * @return Test result\n */\nexport function isSearchSetupMessage(\n message: SearchMessage\n): message is SearchSetupMessage {\n return message.type === SearchMessageType.SETUP\n}\n\n/**\n * Type guard for search ready messages\n *\n * @param message - Search worker message\n *\n * @return Test result\n */\nexport function isSearchReadyMessage(\n message: SearchMessage\n): message is SearchReadyMessage {\n return message.type === SearchMessageType.READY\n}\n\n/**\n * Type guard for search query messages\n *\n * @param message - Search worker message\n *\n * @return Test result\n */\nexport function isSearchQueryMessage(\n message: SearchMessage\n): message is SearchQueryMessage {\n return message.type === SearchMessageType.QUERY\n}\n\n/**\n * Type guard for search result messages\n *\n * @param message - Search worker message\n *\n * @return Test result\n */\nexport function isSearchResultMessage(\n message: SearchMessage\n): message is SearchResultMessage {\n return message.type === SearchMessageType.RESULT\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A RTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, Subject, asyncScheduler } from \"rxjs\"\nimport {\n map,\n observeOn,\n share,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport { WorkerHandler, watchWorker } from \"browser\"\nimport { translate } from \"utilities\"\n\nimport { SearchIndex, SearchIndexPipeline } from \"../../_\"\nimport {\n SearchMessage,\n SearchMessageType,\n SearchSetupMessage,\n isSearchResultMessage\n} from \"../message\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Setup options\n */\ninterface SetupOptions {\n index$: Observable /* Search index observable */\n base$: Observable /* Location base observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up search index\n *\n * @param data - Search index\n *\n * @return Search index\n */\nfunction setupSearchIndex(\n { config, docs, index }: SearchIndex\n): SearchIndex {\n\n /* Override default language with value from translation */\n if (config.lang.length === 1 && config.lang[0] === \"en\")\n config.lang = [translate(\"search.config.lang\")]\n\n /* Override default separator with value from translation */\n if (config.separator === \"[\\\\s\\\\-]+\")\n config.separator = translate(\"search.config.separator\")\n\n /* Set pipeline from translation */\n const pipeline = translate(\"search.config.pipeline\")\n .split(/\\s*,\\s*/)\n .filter(Boolean) as SearchIndexPipeline\n\n /* Return search index after defaulting */\n return { config, docs, index, pipeline }\n}\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set up search web worker\n *\n * This function will create a web worker to set up and query the search index\n * which is done using `lunr`. The index must be passed as an observable to\n * enable hacks like _localsearch_ via search index embedding as JSON.\n *\n * @param url - Worker URL\n * @param options - Options\n *\n * @return Worker handler\n */\nexport function setupSearchWorker(\n url: string, { index$, base$ }: SetupOptions\n): WorkerHandler {\n const worker = new Worker(url)\n\n /* Create communication channels and resolve relative links */\n const tx$ = new Subject()\n const rx$ = watchWorker(worker, { tx$ })\n .pipe(\n withLatestFrom(base$),\n map(([message, base]) => {\n if (isSearchResultMessage(message)) {\n for (const result of message.data)\n for (const document of result)\n document.location = `${base}/${document.location}`\n }\n return message\n }),\n share()\n )\n\n /* Set up search index */\n index$\n .pipe(\n map(data => ({\n type: SearchMessageType.SETUP,\n data: setupSearchIndex(data)\n })),\n observeOn(asyncScheduler)\n )\n .subscribe(tx$.next.bind(tx$))\n\n /* Return worker handler */\n return { tx$, rx$ }\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, OperatorFunction, of, pipe } from \"rxjs\"\nimport { map, switchMap } from \"rxjs/operators\"\n\nimport { Viewport } from \"browser\"\n\nimport { Header } from \"../header\"\nimport { Main } from \"../main\"\nimport {\n Sidebar,\n applySidebar,\n watchSidebar\n} from \"../shared\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Navigation for [screen -]\n */\ninterface NavigationBelowScreen {} // tslint:disable-line\n\n/**\n * Navigation for [screen +]\n */\ninterface NavigationAboveScreen {\n sidebar: Sidebar /* Sidebar */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Navigation\n */\nexport type Navigation =\n | NavigationBelowScreen\n | NavigationAboveScreen\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n header$: Observable
    /* Header observable */\n main$: Observable
    /* Main area observable */\n viewport$: Observable /* Viewport observable */\n screen$: Observable /* Screen media observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount navigation from source observable\n *\n * @param options - Options\n *\n * @return Operator function\n */\nexport function mountNavigation(\n { header$, main$, viewport$, screen$ }: MountOptions\n): OperatorFunction {\n return pipe(\n switchMap(el => screen$\n .pipe(\n switchMap(screen => {\n\n /* [screen +]: Mount navigation in sidebar */\n if (screen) {\n return watchSidebar(el, { main$, viewport$ })\n .pipe(\n applySidebar(el, { header$ }),\n map(sidebar => ({ sidebar }))\n )\n\n /* [screen -]: Mount navigation in drawer */\n } else {\n return of({})\n }\n })\n )\n )\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nexport * from \"./_\"\nexport * from \"./react\"\nexport * from \"./set\"\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n MonoTypeOperatorFunction,\n Observable,\n animationFrameScheduler,\n combineLatest,\n pipe\n} from \"rxjs\"\nimport {\n distinctUntilChanged,\n finalize,\n map,\n observeOn,\n tap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport { Viewport } from \"browser\"\n\nimport { Header } from \"../../../header\"\nimport { Main } from \"../../../main\"\nimport { Sidebar } from \"../_\"\nimport {\n resetSidebarHeight,\n resetSidebarOffset,\n setSidebarHeight,\n setSidebarOffset\n} from \"../set\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n main$: Observable
    /* Main area observable */\n viewport$: Observable /* Viewport observable */\n}\n\n/**\n * Apply options\n */\ninterface ApplyOptions {\n header$: Observable
    /* Header observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch sidebar\n *\n * This function returns an observable that computes the visual parameters of\n * the sidebar which depends on the vertical viewport offset, as well as the\n * height of the main area. When the page is scrolled beyond the header, the\n * sidebar is locked and fills the remaining space.\n *\n * @param el - Sidebar element\n * @param options - Options\n *\n * @return Sidebar observable\n */\nexport function watchSidebar(\n el: HTMLElement, { main$, viewport$ }: WatchOptions\n): Observable {\n const adjust = el.parentElement!.offsetTop\n - el.parentElement!.parentElement!.offsetTop\n\n /* Compute the sidebar's available height and if it should be locked */\n return combineLatest([main$, viewport$])\n .pipe(\n map(([{ offset, height }, { offset: { y } }]) => {\n height = height\n + Math.min(adjust, Math.max(0, y - offset))\n - adjust\n return {\n height,\n lock: y >= offset + adjust\n }\n }),\n distinctUntilChanged((a, b) => {\n return a.height === b.height\n && a.lock === b.lock\n })\n )\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Apply sidebar\n *\n * @param el - Sidebar element\n * @param options - Options\n *\n * @return Operator function\n */\nexport function applySidebar(\n el: HTMLElement, { header$ }: ApplyOptions\n): MonoTypeOperatorFunction {\n return pipe(\n\n /* Defer repaint to next animation frame */\n observeOn(animationFrameScheduler),\n withLatestFrom(header$),\n tap(([{ height, lock }, { height: offset }]) => {\n setSidebarHeight(el, height)\n\n /* Set offset in locked state depending on header height */\n if (lock)\n setSidebarOffset(el, offset)\n else\n resetSidebarOffset(el)\n }),\n\n /* Re-map to sidebar */\n map(([sidebar]) => sidebar),\n\n /* Reset on complete or error */\n finalize(() => {\n resetSidebarOffset(el)\n resetSidebarHeight(el)\n })\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nexport * from \"./_\"\nexport * from \"./anchor\"\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n OperatorFunction,\n combineLatest,\n of,\n pipe\n} from \"rxjs\"\nimport { map, switchMap } from \"rxjs/operators\"\n\nimport { Viewport, getElements } from \"browser\"\n\nimport { Header } from \"../../header\"\nimport { Main } from \"../../main\"\nimport {\n Sidebar,\n applySidebar,\n watchSidebar\n} from \"../../shared\"\nimport {\n AnchorList,\n applyAnchorList,\n watchAnchorList\n} from \"../anchor\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Table of contents for [tablet -]\n */\ninterface TableOfContentsBelowTablet {} // tslint:disable-line\n\n/**\n * Table of contents for [tablet +]\n */\ninterface TableOfContentsAboveTablet {\n sidebar: Sidebar /* Sidebar */\n anchors: AnchorList /* Anchor list */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Table of contents\n */\nexport type TableOfContents =\n | TableOfContentsBelowTablet\n | TableOfContentsAboveTablet\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n header$: Observable
    /* Header observable */\n main$: Observable
    /* Main area observable */\n viewport$: Observable /* Viewport observable */\n tablet$: Observable /* Tablet media observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount table of contents from source observable\n *\n * @param options - Options\n *\n * @return Operator function\n */\nexport function mountTableOfContents(\n { header$, main$, viewport$, tablet$ }: MountOptions\n): OperatorFunction {\n return pipe(\n switchMap(el => tablet$\n .pipe(\n switchMap(tablet => {\n\n /* [tablet +]: Mount table of contents in sidebar */\n if (tablet) {\n const els = getElements(\".md-nav__link\", el)\n\n /* Watch and apply sidebar */\n const sidebar$ = watchSidebar(el, { main$, viewport$ })\n .pipe(\n applySidebar(el, { header$ })\n )\n\n /* Watch and apply anchor list (scroll spy) */\n const anchors$ = watchAnchorList(els, { header$, viewport$ })\n .pipe(\n applyAnchorList(els)\n )\n\n /* Combine into single hot observable */\n return combineLatest([sidebar$, anchors$])\n .pipe(\n map(([sidebar, anchors]) => ({ sidebar, anchors }))\n )\n\n /* [tablet -]: Unmount table of contents */\n } else {\n return of({})\n }\n })\n )\n )\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n MonoTypeOperatorFunction,\n Observable,\n animationFrameScheduler,\n combineLatest,\n pipe\n} from \"rxjs\"\nimport {\n bufferCount,\n distinctUntilChanged,\n distinctUntilKeyChanged,\n finalize,\n map,\n observeOn,\n scan,\n startWith,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\nimport { Viewport, getElement, watchElementSize } from \"browser\"\n\nimport { Header } from \"../../../header\"\nimport { AnchorList } from \"../_\"\nimport {\n resetAnchorActive,\n resetAnchorBlur,\n setAnchorActive,\n setAnchorBlur\n} from \"../set\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n header$: Observable
    /* Header observable */\n viewport$: Observable /* Viewport observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch anchor list\n *\n * This is effectively a scroll-spy implementation which will account for the\n * fixed header and automatically re-calculate anchor offsets when the viewport\n * is resized. The returned observable will only emit if the anchor list needs\n * to be repainted.\n *\n * This implementation tracks an anchor element's entire path starting from its\n * level up to the top-most anchor element, e.g. `[h3, h2, h1]`. Although the\n * Material theme currently doesn't make use of this information, it enables\n * the styling of the entire hierarchy through customization.\n *\n * Note that the current anchor is the last item of the `prev` anchor list.\n *\n * @param els - Anchor elements\n * @param options - Options\n *\n * @return Anchor list observable\n */\nexport function watchAnchorList(\n els: HTMLAnchorElement[], { header$, viewport$ }: WatchOptions\n): Observable {\n const table = new Map()\n for (const el of els) {\n const id = decodeURIComponent(el.hash.substring(1))\n const target = getElement(`[id=\"${id}\"]`)\n if (typeof target !== \"undefined\")\n table.set(el, target)\n }\n\n /* Compute necessary adjustment for header */\n const adjust$ = header$\n .pipe(\n map(header => 18 + header.height)\n )\n\n /* Compute partition of previous and next anchors */\n const partition$ = watchElementSize(document.body)\n .pipe(\n distinctUntilKeyChanged(\"height\"),\n\n /* Build index to map anchor paths to vertical offsets */\n map(() => {\n let path: HTMLAnchorElement[] = []\n return [...table].reduce((index, [anchor, target]) => {\n while (path.length) {\n const last = table.get(path[path.length - 1])!\n if (last.tagName >= target.tagName) {\n path.pop()\n } else {\n break\n }\n }\n\n /* If the current anchor is hidden, continue with its parent */\n let offset = target.offsetTop\n while (!offset && target.parentElement) {\n target = target.parentElement\n offset = target.offsetTop\n }\n\n /* Map reversed anchor path to vertical offset */\n return index.set(\n [...path = [...path, anchor]].reverse(),\n offset\n )\n }, new Map())\n }),\n\n /* Re-compute partition when viewport offset changes */\n switchMap(index => combineLatest([adjust$, viewport$])\n .pipe(\n scan(([prev, next], [adjust, { offset: { y } }]) => {\n\n /* Look forward */\n while (next.length) {\n const [, offset] = next[0]\n if (offset - adjust < y) {\n prev = [...prev, next.shift()!]\n } else {\n break\n }\n }\n\n /* Look backward */\n while (prev.length) {\n const [, offset] = prev[prev.length - 1]\n if (offset - adjust >= y) {\n next = [prev.pop()!, ...next]\n } else {\n break\n }\n }\n\n /* Return partition */\n return [prev, next]\n }, [[], [...index]]),\n distinctUntilChanged((a, b) => {\n return a[0] === b[0]\n && a[1] === b[1]\n })\n )\n )\n )\n\n /* Compute and return anchor list migrations */\n return partition$\n .pipe(\n map(([prev, next]) => ({\n prev: prev.map(([path]) => path),\n next: next.map(([path]) => path)\n })),\n\n /* Extract anchor list migrations */\n startWith({ prev: [], next: [] }),\n bufferCount(2, 1),\n map(([a, b]) => {\n\n /* Moving down */\n if (a.prev.length < b.prev.length) {\n return {\n prev: b.prev.slice(Math.max(0, a.prev.length - 1), b.prev.length),\n next: []\n }\n\n /* Moving up */\n } else {\n return {\n prev: b.prev.slice(-1),\n next: b.next.slice(0, b.next.length - a.next.length)\n }\n }\n })\n )\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Apply anchor list\n *\n * @param els - Anchor elements\n *\n * @return Operator function\n */\nexport function applyAnchorList(\n els: HTMLAnchorElement[]\n): MonoTypeOperatorFunction {\n return pipe(\n\n /* Defer repaint to next animation frame */\n observeOn(animationFrameScheduler),\n tap(({ prev, next }) => {\n\n /* Look forward */\n for (const [el] of next) {\n resetAnchorActive(el)\n resetAnchorBlur(el)\n }\n\n /* Look backward */\n prev.forEach(([el], index) => {\n setAnchorActive(el, index === prev.length - 1)\n setAnchorBlur(el, true)\n })\n }),\n\n /* Reset on complete or error */\n finalize(() => {\n for (const el of els) {\n resetAnchorActive(el)\n resetAnchorBlur(el)\n }\n })\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, OperatorFunction, combineLatest, pipe } from \"rxjs\"\nimport {\n filter,\n map,\n mapTo,\n sample,\n startWith,\n switchMap,\n take\n} from \"rxjs/operators\"\n\nimport { WorkerHandler } from \"browser\"\nimport {\n SearchMessage,\n SearchResult,\n isSearchQueryMessage,\n isSearchReadyMessage\n} from \"integrations/search\"\n\nimport { SearchQuery } from \"../query\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search status\n */\nexport type SearchStatus =\n | \"waiting\" /* Search waiting for initialization */\n | \"ready\" /* Search ready */\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search\n */\nexport interface Search {\n status: SearchStatus /* Search status */\n query: SearchQuery /* Search query */\n result: SearchResult[] /* Search result list */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n query$: Observable /* Search query observable */\n reset$: Observable /* Search reset observable */\n result$: Observable /* Search result observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search from source observable\n *\n * @param handler - Worker handler\n * @param options - Options\n *\n * @return Operator function\n */\nexport function mountSearch(\n { rx$, tx$ }: WorkerHandler,\n { query$, reset$, result$ }: MountOptions\n): OperatorFunction {\n return pipe(\n switchMap(() => {\n\n /* Compute search status */\n const status$ = rx$\n .pipe(\n filter(isSearchReadyMessage),\n mapTo(\"ready\"),\n startWith(\"waiting\")\n ) as Observable\n\n /* Re-emit the latest query when search is ready */\n tx$\n .pipe(\n filter(isSearchQueryMessage),\n sample(status$),\n take(1)\n )\n .subscribe(tx$.next.bind(tx$))\n\n /* Combine into single observable */\n return combineLatest([status$, query$, result$, reset$])\n .pipe(\n map(([status, query, result]) => ({\n status,\n query,\n result\n }))\n )\n })\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { OperatorFunction, pipe } from \"rxjs\"\nimport {\n distinctUntilKeyChanged,\n map,\n switchMap\n} from \"rxjs/operators\"\n\nimport { WorkerHandler, setToggle } from \"browser\"\nimport {\n SearchMessage,\n SearchMessageType,\n SearchQueryMessage,\n SearchTransformFn\n} from \"integrations\"\n\nimport { watchSearchQuery } from \"../react\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search query\n */\nexport interface SearchQuery {\n value: string /* Query value */\n focus: boolean /* Query focus */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n transform?: SearchTransformFn /* Transformation function */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search query from source observable\n *\n * @param handler - Worker handler\n * @param options - Options\n *\n * @return Operator function\n */\nexport function mountSearchQuery(\n { tx$ }: WorkerHandler, options: MountOptions = {}\n): OperatorFunction {\n return pipe(\n switchMap(el => {\n const query$ = watchSearchQuery(el, options)\n\n /* Subscribe worker to search query */\n query$\n .pipe(\n distinctUntilKeyChanged(\"value\"),\n map(({ value }): SearchQueryMessage => ({\n type: SearchMessageType.QUERY,\n data: value\n }))\n )\n .subscribe(tx$.next.bind(tx$))\n\n /* Toggle search on focus */\n query$\n .pipe(\n distinctUntilKeyChanged(\"focus\")\n )\n .subscribe(({ focus }) => {\n if (focus)\n setToggle(\"search\", focus)\n })\n\n /* Return search query */\n return query$\n })\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, combineLatest, fromEvent, merge } from \"rxjs\"\nimport {\n delay,\n distinctUntilChanged,\n map,\n startWith\n} from \"rxjs/operators\"\n\nimport { watchElementFocus } from \"browser\"\nimport { SearchTransformFn, defaultTransform } from \"integrations\"\n\nimport { SearchQuery } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n transform?: SearchTransformFn /* Transformation function */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch search query\n *\n * Note that the focus event which triggers re-reading the current query value\n * is delayed by `1ms` so the input's empty state is allowed to propagate.\n *\n * @param el - Search query element\n * @param options - Options\n *\n * @return Search query observable\n */\nexport function watchSearchQuery(\n el: HTMLInputElement, { transform }: WatchOptions = {}\n): Observable {\n const fn = transform || defaultTransform\n\n /* Intercept keyboard events */\n const value$ = merge(\n fromEvent(el, \"keyup\"),\n fromEvent(el, \"focus\").pipe(delay(1))\n )\n .pipe(\n map(() => fn(el.value)),\n startWith(fn(el.value)),\n distinctUntilChanged()\n )\n\n /* Intercept focus events */\n const focus$ = watchElementFocus(el)\n\n /* Combine into single observable */\n return combineLatest([value$, focus$])\n .pipe(\n map(([value, focus]) => ({ value, focus }))\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { OperatorFunction, pipe } from \"rxjs\"\nimport {\n mapTo,\n startWith,\n switchMap,\n switchMapTo,\n tap\n} from \"rxjs/operators\"\n\nimport { setElementFocus } from \"browser\"\n\nimport { useComponent } from \"../../../_\"\nimport { watchSearchReset } from \"../react\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search reset from source observable\n *\n * @return Operator function\n */\nexport function mountSearchReset(): OperatorFunction {\n return pipe(\n switchMap(el => watchSearchReset(el)\n .pipe(\n switchMapTo(useComponent(\"search-query\")),\n tap(setElementFocus),\n mapTo(undefined)\n )\n ),\n startWith(undefined)\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent } from \"rxjs\"\nimport { mapTo } from \"rxjs/operators\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch search reset\n *\n * @param el - Search reset element\n *\n * @return Search reset observable\n */\nexport function watchSearchReset(\n el: HTMLElement\n): Observable {\n return fromEvent(el, \"click\")\n .pipe(\n mapTo(undefined)\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { translate } from \"utilities\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set number of search results\n *\n * @param el - Search result metadata element\n * @param value - Number of results\n */\nexport function setSearchResultMeta(\n el: HTMLElement, value: number\n): void {\n switch (value) {\n\n /* No results */\n case 0:\n el.textContent = translate(\"search.result.none\")\n break\n\n /* One result */\n case 1:\n el.textContent = translate(\"search.result.one\")\n break\n\n /* Multiple result */\n default:\n el.textContent = translate(\"search.result.other\", value)\n }\n}\n\n/**\n * Reset number of search results\n *\n * @param el - Search result metadata element\n */\nexport function resetSearchResultMeta(\n el: HTMLElement\n): void {\n el.textContent = translate(\"search.result.placeholder\")\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Add an element to the search result list\n *\n * @param el - Search result list element\n * @param child - Search result element\n */\nexport function addToSearchResultList(\n el: HTMLElement, child: Element\n): void {\n el.appendChild(child)\n}\n\n/**\n * Reset search result list\n *\n * @param el - Search result list element\n */\nexport function resetSearchResultList(\n el: HTMLElement\n): void {\n el.innerHTML = \"\"\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n MonoTypeOperatorFunction,\n Observable,\n animationFrameScheduler,\n pipe\n} from \"rxjs\"\nimport {\n finalize,\n map,\n mapTo,\n observeOn,\n scan,\n switchMap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport { getElementOrThrow } from \"browser\"\nimport { SearchResult } from \"integrations/search\"\nimport { renderSearchResult } from \"templates\"\n\nimport { SearchQuery } from \"../../query\"\nimport {\n addToSearchResultList,\n resetSearchResultList,\n resetSearchResultMeta,\n setSearchResultMeta\n} from \"../set\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Apply options\n */\ninterface ApplyOptions {\n query$: Observable /* Search query observable */\n ready$: Observable /* Search ready observable */\n fetch$: Observable /* Result fetch observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Apply search results\n *\n * This function will perform a lazy rendering of the search results, depending\n * on the vertical offset of the search result container. When the scroll offset\n * reaches the bottom of the element, more results are fetched and rendered.\n *\n * @param el - Search result element\n * @param options - Options\n *\n * @return Operator function\n */\nexport function applySearchResult(\n el: HTMLElement, { query$, ready$, fetch$ }: ApplyOptions\n): MonoTypeOperatorFunction {\n const list = getElementOrThrow(\".md-search-result__list\", el)\n const meta = getElementOrThrow(\".md-search-result__meta\", el)\n return pipe(\n\n /* Apply search result metadata */\n withLatestFrom(query$, ready$),\n map(([result, query]) => {\n if (query.value) {\n setSearchResultMeta(meta, result.length)\n } else {\n resetSearchResultMeta(meta)\n }\n return result\n }),\n\n /* Apply search result list */\n switchMap(result => {\n const thresholds = [...result.map(([best]) => best.score), 0]\n return fetch$\n .pipe(\n\n /* Defer repaint to next animation frame */\n observeOn(animationFrameScheduler),\n scan(index => {\n const container = el.parentElement!\n while (index < result.length) {\n addToSearchResultList(list, renderSearchResult(\n result[index++], thresholds[index]\n ))\n if (container.scrollHeight - container.offsetHeight > 16)\n break\n }\n return index\n }, 0),\n\n /* Re-map to search result */\n mapTo(result),\n\n /* Reset on complete or error */\n finalize(() => {\n resetSearchResultList(list)\n })\n )\n }\n )\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, OperatorFunction, pipe } from \"rxjs\"\nimport {\n distinctUntilChanged,\n filter,\n map,\n mapTo,\n startWith,\n switchMap\n} from \"rxjs/operators\"\n\nimport { WorkerHandler, watchElementOffset } from \"browser\"\nimport {\n SearchMessage,\n SearchResult,\n isSearchReadyMessage,\n isSearchResultMessage\n} from \"integrations\"\n\nimport { SearchQuery } from \"../../query\"\nimport { applySearchResult } from \"../react\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n query$: Observable /* Search query observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount search result from source observable\n *\n * @param handler - Worker handler\n * @param options - Options\n *\n * @return Operator function\n */\nexport function mountSearchResult(\n { rx$ }: WorkerHandler, { query$ }: MountOptions\n): OperatorFunction {\n return pipe(\n switchMap(el => {\n const container = el.parentElement!\n\n /* Compute if search is ready */\n const ready$ = rx$\n .pipe(\n filter(isSearchReadyMessage),\n mapTo(true)\n )\n\n /* Compute whether there are more search results to fetch */\n const fetch$ = watchElementOffset(container)\n .pipe(\n map(({ y }) => {\n return y >= container.scrollHeight - container.offsetHeight - 16\n }),\n distinctUntilChanged(),\n filter(Boolean)\n )\n\n /* Apply search results */\n return rx$\n .pipe(\n filter(isSearchResultMessage),\n map(({ data }) => data),\n applySearchResult(el, { query$, ready$, fetch$ }),\n startWith([])\n )\n })\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, OperatorFunction, combineLatest, pipe } from \"rxjs\"\nimport {\n distinctUntilChanged,\n filter,\n map,\n startWith,\n switchMap,\n zipWith\n} from \"rxjs/operators\"\n\nimport {\n Viewport,\n getElement,\n watchViewportAt\n} from \"browser\"\n\nimport { useComponent } from \"../../_\"\nimport {\n applyHeaderType,\n watchHeader\n} from \"../react\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Header type\n */\nexport type HeaderType =\n | \"site\" /* Header shows site title */\n | \"page\" /* Header shows page title */\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Header\n */\nexport interface Header {\n type: HeaderType /* Header type */\n sticky: boolean /* Header stickyness */\n height: number /* Header visible height */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n document$: Observable /* Document observable */\n viewport$: Observable /* Viewport observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount header from source observable\n *\n * @param options - Options\n *\n * @return Operator function\n */\nexport function mountHeader(\n { document$, viewport$ }: MountOptions\n): OperatorFunction {\n return pipe(\n switchMap(el => {\n const header$ = watchHeader(el, { document$ })\n\n /* Compute whether the header should switch to page header */\n const type$ = useComponent(\"main\")\n .pipe(\n map(main => getElement(\"h1, h2, h3, h4, h5, h6\", main)!),\n filter(hx => typeof hx !== \"undefined\"),\n zipWith(useComponent(\"header-title\")),\n switchMap(([hx, title]) => watchViewportAt(hx, { header$, viewport$ })\n .pipe(\n map(({ offset: { y } }) => {\n return y >= hx.offsetHeight ? \"page\" : \"site\"\n }),\n distinctUntilChanged(),\n applyHeaderType(title)\n )\n ),\n startWith(\"site\")\n )\n\n /* Combine into single observable */\n return combineLatest([header$, type$])\n .pipe(\n map(([header, type]): Header => ({ type, ...header }))\n )\n })\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n MonoTypeOperatorFunction,\n Observable,\n animationFrameScheduler,\n of,\n pipe\n} from \"rxjs\"\nimport {\n distinctUntilChanged,\n finalize,\n map,\n observeOn,\n shareReplay,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\nimport { watchElementSize } from \"browser\"\n\nimport { Header, HeaderType } from \"../_\"\nimport {\n resetHeaderTitleActive,\n setHeaderTitleActive\n} from \"../set\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n document$: Observable /* Document observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch header\n *\n * @param el - Header element\n *\n * @return Header observable\n */\nexport function watchHeader(\n el: HTMLElement, { document$ }: WatchOptions\n): Observable> {\n return document$\n .pipe(\n map(() => {\n const styles = getComputedStyle(el)\n return [\n \"sticky\", /* Modern browsers */\n \"-webkit-sticky\" /* Safari */\n ].includes(styles.position)\n }),\n distinctUntilChanged(),\n switchMap(sticky => {\n if (sticky) {\n return watchElementSize(el)\n .pipe(\n map(({ height }) => ({\n sticky: true,\n height\n }))\n )\n } else {\n return of({\n sticky: false,\n height: 0\n })\n }\n }),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Apply header title type\n *\n * @param el - Header title element\n *\n * @return Operator function\n */\nexport function applyHeaderType(\n el: HTMLElement\n): MonoTypeOperatorFunction {\n return pipe(\n\n /* Defer repaint to next animation frame */\n observeOn(animationFrameScheduler),\n tap(type => {\n setHeaderTitleActive(el, type === \"page\")\n }),\n\n /* Reset on complete or error */\n finalize(() => {\n resetHeaderTitleActive(el)\n })\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set header title active\n *\n * @param el - Header title element\n * @param value - Whether the title is shown\n */\nexport function setHeaderTitleActive(\n el: HTMLElement, value: boolean\n): void {\n el.setAttribute(\"data-md-state\", value ? \"active\" : \"\")\n}\n\n/**\n * Reset header title active\n *\n * @param el - Header title element\n */\nexport function resetHeaderTitleActive(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n Observable,\n OperatorFunction,\n Subject,\n noop,\n pipe\n} from \"rxjs\"\nimport {\n distinctUntilKeyChanged,\n finalize,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\nimport { Viewport } from \"browser\"\n\nimport { useComponent } from \"../../_\"\nimport { Header } from \"../../header\"\nimport {\n applyHeaderShadow,\n watchMain\n} from \"../react\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Main area\n */\nexport interface Main {\n offset: number /* Main area top offset */\n height: number /* Main area visible height */\n active: boolean /* Scrolled past top offset */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n header$: Observable
    /* Header observable */\n viewport$: Observable /* Viewport observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount main area from source observable\n *\n * The header must be connected to the main area observable outside of the\n * operator function, as the header will persist in-between document switches\n * while the main area is replaced. However, the header observable must be\n * passed to this function, so we connect both via a long-living subject.\n *\n * @param options - Options\n *\n * @return Operator function\n */\nexport function mountMain(\n { header$, viewport$ }: MountOptions\n): OperatorFunction {\n const main$ = new Subject
    ()\n\n /* Connect to main area observable via long-living subject */\n useComponent(\"header\")\n .pipe(\n switchMap(header => main$\n .pipe(\n distinctUntilKeyChanged(\"active\"),\n applyHeaderShadow(header)\n )\n )\n )\n .subscribe(noop)\n\n /* Return operator */\n return pipe(\n switchMap(el => watchMain(el, { header$, viewport$ })),\n tap(main => main$.next(main)),\n finalize(() => main$.complete())\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n MonoTypeOperatorFunction,\n Observable,\n animationFrameScheduler,\n combineLatest,\n pipe\n} from \"rxjs\"\nimport {\n distinctUntilChanged,\n distinctUntilKeyChanged,\n finalize,\n map,\n observeOn,\n switchMap,\n tap\n} from \"rxjs/operators\"\n\nimport { Viewport, watchElementSize } from \"browser\"\n\nimport { Header } from \"../../header\"\nimport { Main } from \"../_\"\nimport {\n resetHeaderShadow,\n setHeaderShadow\n} from \"../set\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch options\n */\ninterface WatchOptions {\n header$: Observable
    /* Header observable */\n viewport$: Observable /* Viewport observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Watch main area\n *\n * This function returns an observable that computes the visual parameters of\n * the main area which depends on the viewport vertical offset and height, as\n * well as the height of the header element, if the header is fixed.\n *\n * @param el - Main area element\n * @param options - Options\n *\n * @return Main area observable\n */\nexport function watchMain(\n el: HTMLElement, { header$, viewport$ }: WatchOptions\n): Observable
    {\n\n /* Compute necessary adjustment for header */\n const adjust$ = header$\n .pipe(\n map(({ height }) => height),\n distinctUntilChanged()\n )\n\n /* Compute the main area's top and bottom borders */\n const border$ = adjust$\n .pipe(\n switchMap(() => watchElementSize(el)\n .pipe(\n map(({ height }) => ({\n top: el.offsetTop,\n bottom: el.offsetTop + height\n })),\n distinctUntilKeyChanged(\"bottom\")\n )\n )\n )\n\n /* Compute the main area's offset, visible height and if we scrolled past */\n return combineLatest([adjust$, border$, viewport$])\n .pipe(\n map(([header, { top, bottom }, { offset: { y }, size: { height } }]) => {\n height = Math.max(0, height\n - Math.max(0, top - y, header)\n - Math.max(0, height + y - bottom)\n )\n return {\n offset: top - header,\n height,\n active: top - header <= y\n }\n }),\n distinctUntilChanged
    ((a, b) => {\n return a.offset === b.offset\n && a.height === b.height\n && a.active === b.active\n })\n )\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Apply header shadow\n *\n * @param el - Header element\n *\n * @return Operator function\n */\nexport function applyHeaderShadow(\n el: HTMLElement\n): MonoTypeOperatorFunction
    {\n return pipe(\n\n /* Defer repaint to next animation frame */\n observeOn(animationFrameScheduler),\n tap(({ active }) => {\n setHeaderShadow(el, active)\n }),\n\n /* Reset on complete or error */\n finalize(() => {\n resetHeaderShadow(el)\n })\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set header shadow\n *\n * @param el - Header element\n * @param value - Whether the shadow is shown\n */\nexport function setHeaderShadow(\n el: HTMLElement, value: boolean\n): void {\n el.setAttribute(\"data-md-state\", value ? \"shadow\" : \"\")\n}\n\n/**\n * Reset header shadow\n *\n * @param el - Header element\n */\nexport function resetHeaderShadow(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, OperatorFunction, of, pipe } from \"rxjs\"\nimport {\n distinctUntilKeyChanged,\n map,\n switchMap\n} from \"rxjs/operators\"\n\nimport { Viewport, watchViewportAt } from \"browser\"\n\nimport { Header } from \"../../header\"\nimport { applyTabs } from \"../react\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Tabs\n */\nexport interface Tabs {\n hidden: boolean /* Whether the tabs are hidden */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n header$: Observable
    /* Header observable */\n viewport$: Observable /* Viewport observable */\n screen$: Observable /* Media screen observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount tabs from source observable\n *\n * @param options - Options\n *\n * @return Operator function\n */\nexport function mountTabs(\n { header$, viewport$, screen$ }: MountOptions\n): OperatorFunction {\n return pipe(\n switchMap(el => screen$\n .pipe(\n switchMap(screen => {\n\n /* [screen +]: Mount tabs above screen breakpoint */\n if (screen) {\n return watchViewportAt(el, { header$, viewport$ })\n .pipe(\n map(({ offset: { y } }) => ({ hidden: y >= 10 })),\n distinctUntilKeyChanged(\"hidden\"),\n applyTabs(el)\n )\n\n /* [screen -]: Unmount tabs below screen breakpoint */\n } else {\n return of({ hidden: true })\n }\n })\n )\n )\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n MonoTypeOperatorFunction,\n animationFrameScheduler,\n pipe\n} from \"rxjs\"\nimport { finalize, observeOn, tap } from \"rxjs/operators\"\n\nimport { Tabs } from \"../_\"\nimport {\n resetTabsHidden,\n setTabsHidden\n} from \"../set\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Apply tabs\n *\n * @param el - Tabs element\n *\n * @return Operator function\n */\nexport function applyTabs(\n el: HTMLElement\n): MonoTypeOperatorFunction {\n return pipe(\n\n /* Defer repaint to next animation frame */\n observeOn(animationFrameScheduler),\n tap(({ hidden }) => {\n setTabsHidden(el, hidden)\n }),\n\n /* Reset on complete or error */\n finalize(() => {\n resetTabsHidden(el)\n })\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Set tabs hidden\n *\n * @param el - Tabs element\n * @param value - Whether the element is hidden\n */\nexport function setTabsHidden(\n el: HTMLElement, value: boolean\n): void {\n el.setAttribute(\"data-md-state\", value ? \"hidden\" : \"\")\n}\n\n/**\n * Reset tabs hidden\n *\n * @param el - Tabs element\n */\nexport function resetTabsHidden(\n el: HTMLElement\n): void {\n el.removeAttribute(\"data-md-state\")\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { NEVER, Observable, fromEvent, iif, merge } from \"rxjs\"\nimport { map, mapTo, shareReplay, switchMap } from \"rxjs/operators\"\n\nimport { getElements } from \"browser\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch options\n */\ninterface PatchOptions {\n document$: Observable /* Document observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Check whether the given device is an Apple device\n *\n * @return Test result\n */\nfunction isAppleDevice(): boolean {\n return /(iPad|iPhone|iPod)/.test(navigator.userAgent)\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch all elements with `data-md-scrollfix` attributes\n *\n * This is a year-old patch which ensures that overflow scrolling works at the\n * top and bottom of containers on iOS by ensuring a `1px` scroll offset upon\n * the start of a touch event.\n *\n * @see https://bit.ly/2SCtAOO - Original source\n *\n * @param options - Options\n */\nexport function patchScrollfix(\n { document$ }: PatchOptions\n): void {\n const els$ = document$\n .pipe(\n map(() => getElements(\"[data-md-scrollfix]\")),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n\n /* Remove marker attribute, so we'll only add the fix once */\n els$.subscribe(els => {\n for (const el of els)\n el.removeAttribute(\"data-md-scrollfix\")\n })\n\n /* Patch overflow scrolling on touch start */\n iif(isAppleDevice, els$, NEVER)\n .pipe(\n switchMap(els => merge(...els.map(el => (\n fromEvent(el, \"touchstart\")\n .pipe(\n mapTo(el)\n )\n ))))\n )\n .subscribe(el => {\n const top = el.scrollTop\n\n /* We're at the top of the container */\n if (top === 0) {\n el.scrollTop = 1\n\n /* We're at the bottom of the container */\n } else if (top + el.offsetHeight === el.scrollHeight) {\n el.scrollTop = top - 1\n }\n })\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { NEVER, Observable } from \"rxjs\"\nimport { catchError, map, switchMap } from \"rxjs/operators\"\n\nimport { getElementOrThrow, getElements } from \"browser\"\nimport { renderSource } from \"templates\"\nimport { cache, hash } from \"utilities\"\n\nimport { fetchSourceFactsFromGitHub } from \"./github\"\nimport { fetchSourceFactsFromGitLab } from \"./gitlab\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Source facts\n */\nexport type SourceFacts = string[]\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch options\n */\ninterface PatchOptions {\n document$: Observable /* Document observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch source facts\n *\n * @param url - Source repository URL\n *\n * @return Source facts observable\n */\nfunction fetchSourceFacts(\n url: string\n): Observable {\n const [type] = url.match(/(git(?:hub|lab))/i) || []\n switch (type.toLowerCase()) {\n\n /* GitHub repository */\n case \"github\":\n const [, user, repo] = url.match(/^.+github\\.com\\/([^\\/]+)\\/?([^\\/]+)?/i)\n return fetchSourceFactsFromGitHub(user, repo)\n\n /* GitLab repository */\n case \"gitlab\":\n const [, base, slug] = url.match(/^.+?([^\\/]*gitlab[^\\/]+)\\/(.+?)\\/?$/i)\n return fetchSourceFactsFromGitLab(base, slug)\n\n /* Everything else */\n default:\n return NEVER\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch elements containing repository information\n *\n * This function will retrieve the URL from the repository link and try to\n * query data from integrated source code platforms like GitHub or GitLab.\n *\n * @param options - Options\n */\nexport function patchSource(\n { document$ }: PatchOptions\n): void {\n document$\n .pipe(\n map(() => getElementOrThrow(\".md-source[href]\")),\n switchMap(({ href }) => (\n cache(`${hash(href)}`, () => fetchSourceFacts(href))\n )),\n catchError(() => NEVER)\n )\n .subscribe(facts => {\n for (const el of getElements(\".md-source__repository\")) {\n if (!el.hasAttribute(\"data-md-state\")) {\n el.setAttribute(\"data-md-state\", \"done\")\n el.appendChild(renderSource(facts))\n }\n }\n })\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Repo, User } from \"github-types\"\nimport { Observable, from } from \"rxjs\"\nimport { map } from \"rxjs/operators\"\n\nimport { round } from \"utilities\"\n\nimport { SourceFacts } from \"..\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch GitHub source facts\n *\n * @param user - GitHub user\n * @param repo - GitHub repository\n *\n * @return Source facts observable\n */\nexport function fetchSourceFactsFromGitHub(\n user: string, repo?: string\n): Observable {\n const url = typeof repo !== \"undefined\"\n ? `https://api.github.com/repos/${user}/${repo}`\n : `https://api.github.com/users/${user}`\n return from(fetch(url).then(res => res.json()))\n .pipe(\n map(data => {\n\n /* GitHub repository */\n if (typeof repo !== \"undefined\") {\n const { stargazers_count, forks_count }: Repo = data\n return [\n `${round(stargazers_count || 0)} Stars`,\n `${round(forks_count || 0)} Forks`\n ]\n\n /* GitHub user/organization */\n } else {\n const { public_repos }: User = data\n return [\n `${round(public_repos || 0)} Repositories`\n ]\n }\n })\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { ProjectSchema } from \"gitlab\"\nimport { Observable, from } from \"rxjs\"\nimport { map } from \"rxjs/operators\"\n\nimport { round } from \"utilities\"\n\nimport { SourceFacts } from \"..\"\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch GitLab source facts\n *\n * @param base - GitLab base\n * @param project - GitLab project\n *\n * @return Source facts observable\n */\nexport function fetchSourceFactsFromGitLab(\n base: string, project: string\n): Observable {\n const url = `https://${base}/api/v4/projects/${encodeURIComponent(project)}`\n return from(fetch(url).then(res => res.json()))\n .pipe(\n map(({ star_count, forks_count }: ProjectSchema) => ([\n `${round(star_count)} Stars`,\n `${round(forks_count)} Forks`\n ]))\n )\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n// DISCLAIMER: this file is still WIP. There're some refactoring opportunities\n// which must be tackled after we gathered some feedback on v5.\n// tslint:disable\n\nimport \"focus-visible\"\n\nimport {\n merge,\n combineLatest,\n animationFrameScheduler,\n fromEvent,\n from,\n defer,\n of,\n NEVER\n} from \"rxjs\"\nimport {\n delay,\n switchMap,\n tap,\n filter,\n withLatestFrom,\n observeOn,\n take,\n shareReplay,\n catchError,\n map\n} from \"rxjs/operators\"\n\nimport {\n watchToggle,\n setToggle,\n getElements,\n watchMedia,\n watchDocument,\n watchLocation,\n watchLocationHash,\n watchViewport,\n isLocalLocation,\n setLocationHash,\n watchLocationBase\n} from \"browser\"\nimport {\n mountHeader,\n mountMain,\n mountNavigation,\n mountSearch,\n mountTableOfContents,\n mountTabs,\n useComponent,\n setupComponents,\n mountSearchQuery,\n mountSearchReset,\n mountSearchResult\n} from \"components\"\nimport {\n setupClipboard,\n setupDialog,\n setupKeyboard,\n setupInstantLoading,\n setupSearchWorker,\n SearchIndex,\n SearchIndexPipeline\n} from \"integrations\"\nimport {\n patchCodeBlocks,\n patchTables,\n patchDetails,\n patchScrollfix,\n patchSource,\n patchScripts\n} from \"patches\"\nimport { isConfig } from \"utilities\"\n\n/* ------------------------------------------------------------------------- */\n\n/* Denote that JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Test for iOS */\nif (navigator.userAgent.match(/(iPad|iPhone|iPod)/g))\n document.documentElement.classList.add(\"ios\")\n\n/**\n * Set scroll lock\n *\n * @param el - Scrollable element\n * @param value - Vertical offset\n */\nexport function setScrollLock(\n el: HTMLElement, value: number\n): void {\n el.setAttribute(\"data-md-state\", \"lock\")\n el.style.top = `-${value}px`\n}\n\n/**\n * Reset scroll lock\n *\n * @param el - Scrollable element\n */\nexport function resetScrollLock(\n el: HTMLElement\n): void {\n const value = -1 * parseInt(el.style.top, 10)\n el.removeAttribute(\"data-md-state\")\n el.style.top = \"\"\n if (value)\n window.scrollTo(0, value)\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Initialize Material for MkDocs\n *\n * @param config - Configuration\n */\nexport function initialize(config: unknown) {\n if (!isConfig(config))\n throw new SyntaxError(`Invalid configuration: ${JSON.stringify(config)}`)\n\n /* Set up subjects */\n const document$ = watchDocument()\n const location$ = watchLocation()\n\n /* Set up user interface observables */\n const base$ = watchLocationBase(config.base, { location$ })\n const hash$ = watchLocationHash()\n const viewport$ = watchViewport()\n const tablet$ = watchMedia(\"(min-width: 960px)\")\n const screen$ = watchMedia(\"(min-width: 1220px)\")\n\n /* ----------------------------------------------------------------------- */\n\n /* Set up component bindings */\n setupComponents([\n \"announce\", /* Announcement bar */\n \"container\", /* Container */\n \"header\", /* Header */\n \"header-title\", /* Header title */\n \"main\", /* Main area */\n \"navigation\", /* Navigation */\n \"search\", /* Search */\n \"search-query\", /* Search input */\n \"search-reset\", /* Search reset */\n \"search-result\", /* Search results */\n \"skip\", /* Skip link */\n \"tabs\", /* Tabs */\n \"toc\" /* Table of contents */\n ], { document$ })\n\n const keyboard$ = setupKeyboard()\n\n // Hack: only make code blocks focusable on non-touch devices\n if (matchMedia(\"(hover)\").matches)\n patchCodeBlocks({ document$, viewport$ })\n patchDetails({ document$, hash$ })\n patchScripts({ document$ })\n patchSource({ document$ })\n patchTables({ document$ })\n\n /* Force 1px scroll offset to trigger overflow scrolling */\n patchScrollfix({ document$ })\n\n /* Set up clipboard and dialog */\n const dialog$ = setupDialog()\n const clipboard$ = setupClipboard({ document$, dialog$ })\n\n /* ----------------------------------------------------------------------- */\n\n /* Create header observable */\n const header$ = useComponent(\"header\")\n .pipe(\n mountHeader({ document$, viewport$ }),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n\n const main$ = useComponent(\"main\")\n .pipe(\n mountMain({ header$, viewport$ }),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n\n /* ----------------------------------------------------------------------- */\n\n const navigation$ = useComponent(\"navigation\")\n .pipe(\n mountNavigation({ header$, main$, viewport$, screen$ }),\n shareReplay({ bufferSize: 1, refCount: true }) // shareReplay because there might be late subscribers\n )\n\n const toc$ = useComponent(\"toc\")\n .pipe(\n mountTableOfContents({ header$, main$, viewport$, tablet$ }),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n\n const tabs$ = useComponent(\"tabs\")\n .pipe(\n mountTabs({ header$, viewport$, screen$ }),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n\n /* ----------------------------------------------------------------------- */\n\n /* Search worker - only if search is present */\n const worker$ = useComponent(\"search\")\n .pipe(\n switchMap(() => defer(() => {\n const index = config.search && config.search.index\n ? config.search.index\n : undefined\n\n /* Fetch index if it wasn't passed explicitly */\n const index$ = (\n typeof index !== \"undefined\"\n ? from(index)\n : base$\n .pipe(\n switchMap(base => fetch(`${base}/search/search_index.json`, {\n credentials: \"same-origin\"\n }).then(res => res.json())) // SearchIndex\n )\n )\n\n return of(setupSearchWorker(config.search.worker, {\n base$, index$\n }))\n }))\n )\n\n /* ----------------------------------------------------------------------- */\n\n /* Mount search query */\n const search$ = worker$\n .pipe(\n switchMap(worker => {\n\n const query$ = useComponent(\"search-query\")\n .pipe(\n mountSearchQuery(worker, { transform: config.search.transform }),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n\n /* Mount search reset */\n const reset$ = useComponent(\"search-reset\")\n .pipe(\n mountSearchReset(),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n\n /* Mount search result */\n const result$ = useComponent(\"search-result\")\n .pipe(\n mountSearchResult(worker, { query$ }),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n\n return useComponent(\"search\")\n .pipe(\n mountSearch(worker, { query$, reset$, result$ }),\n )\n }),\n catchError(() => {\n useComponent(\"search\")\n .subscribe(el => el.hidden = true) // TODO: Hack\n return NEVER\n }),\n shareReplay({ bufferSize: 1, refCount: true })\n )\n\n /* ----------------------------------------------------------------------- */\n\n // // put into search...\n hash$\n .pipe(\n tap(() => setToggle(\"search\", false)),\n delay(125), // ensure that it runs after the body scroll reset...\n )\n .subscribe(hash => setLocationHash(`#${hash}`))\n\n // TODO: scroll restoration must be centralized\n combineLatest([\n watchToggle(\"search\"),\n tablet$,\n ])\n .pipe(\n withLatestFrom(viewport$),\n switchMap(([[toggle, tablet], { offset: { y }}]) => {\n const active = toggle && !tablet\n return document$\n .pipe(\n delay(active ? 400 : 100),\n observeOn(animationFrameScheduler),\n tap(({ body }) => active\n ? setScrollLock(body, y)\n : resetScrollLock(body)\n )\n )\n })\n )\n .subscribe()\n\n /* ----------------------------------------------------------------------- */\n\n /* Always close drawer on click */\n fromEvent(document.body, \"click\")\n .pipe(\n filter(ev => !(ev.metaKey || ev.ctrlKey)),\n filter(ev => {\n if (ev.target instanceof HTMLElement) {\n const el = ev.target.closest(\"a\") // TODO: abstract as link click?\n if (el && isLocalLocation(el)) {\n return true\n }\n }\n return false\n })\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n })\n\n /* Enable instant loading, if not on file:// protocol */\n if (\n config.features.includes(\"navigation.instant\") &&\n location.protocol !== \"file:\"\n ) {\n const dom = new DOMParser()\n\n /* Fetch sitemap and extract URL whitelist */\n base$\n .pipe(\n switchMap(base => from(fetch(`${base}/sitemap.xml`)\n .then(res => res.text())\n .then(text => dom.parseFromString(text, \"text/xml\"))\n )),\n withLatestFrom(base$),\n map(([document, base]) => {\n const urls = getElements(\"loc\", document)\n .map(node => node.textContent!)\n\n // Hack: This is a temporary fix to normalize instant loading lookup\n // on localhost and Netlify previews. If this approach proves to be\n // suitable, we'll refactor URL whitelisting anyway. We take the two\n // shortest URLs and determine the common prefix to isolate the\n // domain. If there're no two domains, we just leave it as-is, as\n // there isn't anything to be loaded anway.\n if (urls.length > 1) {\n const [a, b] = urls.sort((a, b) => a.length - b.length)\n\n /* Determine common prefix */\n let index = 0\n if (a === b)\n index = a.length\n else\n while (a.charAt(index) === b.charAt(index))\n index++\n\n /* Replace common prefix (i.e. base) with effective base */\n for (let i = 0; i < urls.length; i++)\n urls[i] = urls[i].replace(a.slice(0, index), `${base}/`)\n }\n return urls\n })\n )\n .subscribe(urls => {\n setupInstantLoading(urls, { document$, location$, viewport$ })\n })\n }\n\n /* ----------------------------------------------------------------------- */\n\n /* Unhide permalinks on first tab */\n keyboard$\n .pipe(\n filter(key => key.mode === \"global\" && key.type === \"Tab\"),\n take(1)\n )\n .subscribe(() => {\n for (const link of getElements(\".headerlink\"))\n link.style.visibility = \"visible\"\n })\n\n /* ----------------------------------------------------------------------- */\n\n const state = {\n\n /* Browser observables */\n document$,\n location$,\n viewport$,\n\n /* Component observables */\n header$,\n main$,\n navigation$,\n search$,\n tabs$,\n toc$,\n\n /* Integration observables */\n clipboard$,\n keyboard$,\n dialog$\n }\n\n /* Subscribe to all observables */\n merge(...Object.values(state))\n .subscribe()\n return state\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, combineLatest } from \"rxjs\"\nimport { distinctUntilKeyChanged, map } from \"rxjs/operators\"\n\nimport { Viewport, getElements } from \"browser\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n document$: Observable /* Document observable */\n viewport$: Observable /* Viewport observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch all `code` elements\n *\n * This function will make overflowing code blocks focusable via keyboard, so\n * they can be scrolled without a mouse.\n *\n * @param options - Options\n */\nexport function patchCodeBlocks(\n { document$, viewport$ }: MountOptions\n): void {\n const els$ = document$\n .pipe(\n map(() => getElements(\"pre > code\"))\n )\n\n /* Observe viewport size only */\n const size$ = viewport$\n .pipe(\n distinctUntilKeyChanged(\"size\")\n )\n\n /* Make overflowing elements focusable */\n combineLatest([els$, size$])\n .subscribe(([els]) => {\n for (const el of els) {\n if (el.scrollWidth > el.clientWidth)\n el.setAttribute(\"tabindex\", \"0\")\n else\n el.removeAttribute(\"tabindex\")\n }\n })\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable, fromEvent, merge } from \"rxjs\"\nimport {\n filter,\n map,\n switchMapTo,\n tap\n} from \"rxjs/operators\"\n\nimport {\n getElement,\n getElements,\n watchMedia\n} from \"browser\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch options\n */\ninterface PatchOptions {\n document$: Observable /* Document observable */\n hash$: Observable /* Location hash observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch all `details` elements\n *\n * This function will ensure that all `details` tags are opened prior to\n * printing, so the whole content of the page is included, and on anchor jumps.\n *\n * @param options - Options\n */\nexport function patchDetails(\n { document$, hash$ }: PatchOptions\n): void {\n const els$ = document$\n .pipe(\n map(() => getElements(\"details\"))\n )\n\n /* Open all details before printing */\n merge(\n watchMedia(\"print\").pipe(filter(Boolean)), /* Webkit */\n fromEvent(window, \"beforeprint\") /* IE, FF */\n )\n .pipe(\n switchMapTo(els$)\n )\n .subscribe(els => {\n for (const el of els)\n el.setAttribute(\"open\", \"\")\n })\n\n /* Open parent details and fix anchor jump */\n hash$\n .pipe(\n map(id => getElement(`[id=\"${id}\"]`)!),\n filter(el => typeof el !== \"undefined\"),\n tap(el => {\n const details = el.closest(\"details\")\n if (details && !details.open)\n details.setAttribute(\"open\", \"\")\n })\n )\n .subscribe(el => el.scrollIntoView())\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { EMPTY, Observable, noop, of } from \"rxjs\"\nimport {\n concatMap,\n map,\n skip,\n switchMap,\n withLatestFrom\n} from \"rxjs/operators\"\n\nimport {\n createElement,\n getElements,\n replaceElement\n} from \"browser\"\nimport { useComponent } from \"components\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch options\n */\ninterface PatchOptions {\n document$: Observable /* Document observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch all `script` elements\n *\n * This function must be run after a document switch, which means the first\n * emission must be ignored.\n *\n * @param options - Options\n */\nexport function patchScripts(\n { document$ }: PatchOptions\n): void {\n const els$ = document$\n .pipe(\n skip(1),\n withLatestFrom(useComponent(\"container\")),\n map(([, el]) => getElements(\"script\", el))\n )\n\n /* Evaluate all scripts via replacement in order */\n els$\n .pipe(\n switchMap(els => of(...els)),\n concatMap(el => {\n const script = createElement(\"script\")\n if (el.src) {\n script.src = el.src\n replaceElement(el, script)\n\n /* Complete when script is loaded */\n return new Observable(observer => {\n script.onload = () => observer.complete()\n })\n\n /* Complete immediately */\n } else {\n script.textContent = el.textContent!\n replaceElement(el, script)\n return EMPTY\n }\n })\n )\n .subscribe(noop)\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { Observable } from \"rxjs\"\nimport { map } from \"rxjs/operators\"\n\nimport {\n createElement,\n getElements,\n replaceElement\n} from \"browser\"\nimport { renderTable } from \"templates\"\n\n/* ----------------------------------------------------------------------------\n * Helper types\n * ------------------------------------------------------------------------- */\n\n/**\n * Mount options\n */\ninterface MountOptions {\n document$: Observable /* Document observable */\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Patch all `table` elements\n *\n * This function will re-render all tables by wrapping them to improve overflow\n * scrolling on smaller screen sizes.\n *\n * @param options - Options\n */\nexport function patchTables(\n { document$ }: MountOptions\n): void {\n const sentinel = createElement(\"table\")\n document$\n .pipe(\n map(() => getElements(\"table:not([class])\"))\n )\n .subscribe(els => {\n for (const el of els) {\n replaceElement(el, sentinel)\n replaceElement(sentinel, renderTable(el))\n }\n })\n}\n"],"sourceRoot":""} \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.ar.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.ar.min.js deleted file mode 100644 index 248ddc5d1..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.ar.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ar=function(){this.pipeline.reset(),this.pipeline.add(e.ar.trimmer,e.ar.stopWordFilter,e.ar.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ar.stemmer))},e.ar.wordCharacters="ء-ٛٱـ",e.ar.trimmer=e.trimmerSupport.generateTrimmer(e.ar.wordCharacters),e.Pipeline.registerFunction(e.ar.trimmer,"trimmer-ar"),e.ar.stemmer=function(){var e=this;return e.result=!1,e.preRemoved=!1,e.sufRemoved=!1,e.pre={pre1:"ف ك ب و س ل ن ا ي ت",pre2:"ال لل",pre3:"بال وال فال تال كال ولل",pre4:"فبال كبال وبال وكال"},e.suf={suf1:"ه ك ت ن ا ي",suf2:"نك نه ها وك يا اه ون ين تن تم نا وا ان كم كن ني نن ما هم هن تك ته ات يه",suf3:"تين كهم نيه نهم ونه وها يهم ونا ونك وني وهم تكم تنا تها تني تهم كما كها ناه نكم هنا تان يها",suf4:"كموه ناها ونني ونهم تكما تموه تكاه كماه ناكم ناهم نيها وننا"},e.patterns=JSON.parse('{"pt43":[{"pt":[{"c":"ا","l":1}]},{"pt":[{"c":"ا,ت,ن,ي","l":0}],"mPt":[{"c":"ف","l":0,"m":1},{"c":"ع","l":1,"m":2},{"c":"ل","l":2,"m":3}]},{"pt":[{"c":"و","l":2}],"mPt":[{"c":"ف","l":0,"m":0},{"c":"ع","l":1,"m":1},{"c":"ل","l":2,"m":3}]},{"pt":[{"c":"ا","l":2}]},{"pt":[{"c":"ي","l":2}],"mPt":[{"c":"ف","l":0,"m":0},{"c":"ع","l":1,"m":1},{"c":"ا","l":2},{"c":"ل","l":3,"m":3}]},{"pt":[{"c":"م","l":0}]}],"pt53":[{"pt":[{"c":"ت","l":0},{"c":"ا","l":2}]},{"pt":[{"c":"ا,ن,ت,ي","l":0},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ت","l":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"ا","l":0},{"c":"ا","l":2}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ع","l":2,"m":3},{"c":"ل","l":3,"m":4},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"ا","l":0},{"c":"ا","l":3}],"mPt":[{"c":"ف","l":0,"m":1},{"c":"ع","l":1,"m":2},{"c":"ل","l":2,"m":4}]},{"pt":[{"c":"ا","l":3},{"c":"ن","l":4}]},{"pt":[{"c":"ت","l":0},{"c":"ي","l":3}]},{"pt":[{"c":"م","l":0},{"c":"و","l":3}]},{"pt":[{"c":"ا","l":1},{"c":"و","l":3}]},{"pt":[{"c":"و","l":1},{"c":"ا","l":2}]},{"pt":[{"c":"م","l":0},{"c":"ا","l":3}]},{"pt":[{"c":"م","l":0},{"c":"ي","l":3}]},{"pt":[{"c":"ا","l":2},{"c":"ن","l":3}]},{"pt":[{"c":"م","l":0},{"c":"ن","l":1}],"mPt":[{"c":"ا","l":0},{"c":"ن","l":1},{"c":"ف","l":2,"m":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"م","l":0},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ت","l":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"م","l":0},{"c":"ا","l":2}]},{"pt":[{"c":"م","l":1},{"c":"ا","l":3}]},{"pt":[{"c":"ي,ت,ا,ن","l":0},{"c":"ت","l":1}],"mPt":[{"c":"ف","l":0,"m":2},{"c":"ع","l":1,"m":3},{"c":"ا","l":2},{"c":"ل","l":3,"m":4}]},{"pt":[{"c":"ت,ي,ا,ن","l":0},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ت","l":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"ا","l":2},{"c":"ي","l":3}]},{"pt":[{"c":"ا,ي,ت,ن","l":0},{"c":"ن","l":1}],"mPt":[{"c":"ا","l":0},{"c":"ن","l":1},{"c":"ف","l":2,"m":2},{"c":"ع","l":3,"m":3},{"c":"ا","l":4},{"c":"ل","l":5,"m":4}]},{"pt":[{"c":"ا","l":3},{"c":"ء","l":4}]}],"pt63":[{"pt":[{"c":"ا","l":0},{"c":"ت","l":2},{"c":"ا","l":4}]},{"pt":[{"c":"ا,ت,ن,ي","l":0},{"c":"س","l":1},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"س","l":1},{"c":"ت","l":2},{"c":"ف","l":3,"m":3},{"c":"ع","l":4,"m":4},{"c":"ا","l":5},{"c":"ل","l":6,"m":5}]},{"pt":[{"c":"ا,ن,ت,ي","l":0},{"c":"و","l":3}]},{"pt":[{"c":"م","l":0},{"c":"س","l":1},{"c":"ت","l":2}],"mPt":[{"c":"ا","l":0},{"c":"س","l":1},{"c":"ت","l":2},{"c":"ف","l":3,"m":3},{"c":"ع","l":4,"m":4},{"c":"ا","l":5},{"c":"ل","l":6,"m":5}]},{"pt":[{"c":"ي","l":1},{"c":"ي","l":3},{"c":"ا","l":4},{"c":"ء","l":5}]},{"pt":[{"c":"ا","l":0},{"c":"ن","l":1},{"c":"ا","l":4}]}],"pt54":[{"pt":[{"c":"ت","l":0}]},{"pt":[{"c":"ا,ي,ت,ن","l":0}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ع","l":2,"m":2},{"c":"ل","l":3,"m":3},{"c":"ر","l":4,"m":4},{"c":"ا","l":5},{"c":"ر","l":6,"m":4}]},{"pt":[{"c":"م","l":0}],"mPt":[{"c":"ا","l":0},{"c":"ف","l":1,"m":1},{"c":"ع","l":2,"m":2},{"c":"ل","l":3,"m":3},{"c":"ر","l":4,"m":4},{"c":"ا","l":5},{"c":"ر","l":6,"m":4}]},{"pt":[{"c":"ا","l":2}]},{"pt":[{"c":"ا","l":0},{"c":"ن","l":2}]}],"pt64":[{"pt":[{"c":"ا","l":0},{"c":"ا","l":4}]},{"pt":[{"c":"م","l":0},{"c":"ت","l":1}]}],"pt73":[{"pt":[{"c":"ا","l":0},{"c":"س","l":1},{"c":"ت","l":2},{"c":"ا","l":5}]}],"pt75":[{"pt":[{"c":"ا","l":0},{"c":"ا","l":5}]}]}'),e.execArray=["cleanWord","removeDiacritics","cleanAlef","removeStopWords","normalizeHamzaAndAlef","removeStartWaw","removePre432","removeEndTaa","wordCheck"],e.stem=function(){var r=0;for(e.result=!1,e.preRemoved=!1,e.sufRemoved=!1;r=0)return!0},e.normalizeHamzaAndAlef=function(){return e.word=e.word.replace("ؤ","ء"),e.word=e.word.replace("ئ","ء"),e.word=e.word.replace(/([\u0627])\1+/gi,"ا"),!1},e.removeEndTaa=function(){return!(e.word.length>2)||(e.word=e.word.replace(/[\u0627]$/,""),e.word=e.word.replace("ة",""),!1)},e.removeStartWaw=function(){return e.word.length>3&&"و"==e.word[0]&&"و"==e.word[1]&&(e.word=e.word.slice(1)),!1},e.removePre432=function(){var r=e.word;if(e.word.length>=7){var t=new RegExp("^("+e.pre.pre4.split(" ").join("|")+")");e.word=e.word.replace(t,"")}if(e.word==r&&e.word.length>=6){var c=new RegExp("^("+e.pre.pre3.split(" ").join("|")+")");e.word=e.word.replace(c,"")}if(e.word==r&&e.word.length>=5){var l=new RegExp("^("+e.pre.pre2.split(" ").join("|")+")");e.word=e.word.replace(l,"")}return r!=e.word&&(e.preRemoved=!0),!1},e.patternCheck=function(r){for(var t=0;t3){var t=new RegExp("^("+e.pre.pre1.split(" ").join("|")+")");e.word=e.word.replace(t,"")}return r!=e.word&&(e.preRemoved=!0),!1},e.removeSuf1=function(){var r=e.word;if(0==e.sufRemoved&&e.word.length>3){var t=new RegExp("("+e.suf.suf1.split(" ").join("|")+")$");e.word=e.word.replace(t,"")}return r!=e.word&&(e.sufRemoved=!0),!1},e.removeSuf432=function(){var r=e.word;if(e.word.length>=6){var t=new RegExp("("+e.suf.suf4.split(" ").join("|")+")$");e.word=e.word.replace(t,"")}if(e.word==r&&e.word.length>=5){var c=new RegExp("("+e.suf.suf3.split(" ").join("|")+")$");e.word=e.word.replace(c,"")}if(e.word==r&&e.word.length>=4){var l=new RegExp("("+e.suf.suf2.split(" ").join("|")+")$");e.word=e.word.replace(l,"")}return r!=e.word&&(e.sufRemoved=!0),!1},e.wordCheck=function(){for(var r=(e.word,[e.removeSuf432,e.removeSuf1,e.removePre1]),t=0,c=!1;e.word.length>=7&&!e.result&&t=f.limit)return;f.cursor++}for(;!f.out_grouping(w,97,248);){if(f.cursor>=f.limit)return;f.cursor++}d=f.cursor,d=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(c,32),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del();break;case 2:f.in_grouping_b(p,97,229)&&f.slice_del()}}function t(){var e,r=f.limit-f.cursor;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.find_among_b(l,4)?(f.bra=f.cursor,f.limit_backward=e,f.cursor=f.limit-r,f.cursor>f.limit_backward&&(f.cursor--,f.bra=f.cursor,f.slice_del())):f.limit_backward=e)}function s(){var e,r,i,n=f.limit-f.cursor;if(f.ket=f.cursor,f.eq_s_b(2,"st")&&(f.bra=f.cursor,f.eq_s_b(2,"ig")&&f.slice_del()),f.cursor=f.limit-n,f.cursor>=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(m,5),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del(),i=f.limit-f.cursor,t(),f.cursor=f.limit-i;break;case 2:f.slice_from("løs")}}function o(){var e;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.out_grouping_b(w,97,248)?(f.bra=f.cursor,u=f.slice_to(u),f.limit_backward=e,f.eq_v_b(u)&&f.slice_del()):f.limit_backward=e)}var a,d,u,c=[new r("hed",-1,1),new r("ethed",0,1),new r("ered",-1,1),new r("e",-1,1),new r("erede",3,1),new r("ende",3,1),new r("erende",5,1),new r("ene",3,1),new r("erne",3,1),new r("ere",3,1),new r("en",-1,1),new r("heden",10,1),new r("eren",10,1),new r("er",-1,1),new r("heder",13,1),new r("erer",13,1),new r("s",-1,2),new r("heds",16,1),new r("es",16,1),new r("endes",18,1),new r("erendes",19,1),new r("enes",18,1),new r("ernes",18,1),new r("eres",18,1),new r("ens",16,1),new r("hedens",24,1),new r("erens",24,1),new r("ers",16,1),new r("ets",16,1),new r("erets",28,1),new r("et",-1,1),new r("eret",30,1)],l=[new r("gd",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("elig",1,1),new r("els",-1,1),new r("løst",-1,2)],w=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],p=[239,254,42,3,0,0,0,0,0,0,0,0,0,0,0,0,16],f=new i;this.setCurrent=function(e){f.setCurrent(e)},this.getCurrent=function(){return f.getCurrent()},this.stem=function(){var r=f.cursor;return e(),f.limit_backward=r,f.cursor=f.limit,n(),f.cursor=f.limit,t(),f.cursor=f.limit,s(),f.cursor=f.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.da.stemmer,"stemmer-da"),e.da.stopWordFilter=e.generateStopWordFilter("ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været".split(" ")),e.Pipeline.registerFunction(e.da.stopWordFilter,"stopWordFilter-da")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.de.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.de.min.js deleted file mode 100644 index f3b5c108c..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.de.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Lunr languages, `German` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ - -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.de=function(){this.pipeline.reset(),this.pipeline.add(e.de.trimmer,e.de.stopWordFilter,e.de.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.de.stemmer))},e.de.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.de.trimmer=e.trimmerSupport.generateTrimmer(e.de.wordCharacters),e.Pipeline.registerFunction(e.de.trimmer,"trimmer-de"),e.de.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(e,r,n){return!(!v.eq_s(1,e)||(v.ket=v.cursor,!v.in_grouping(p,97,252)))&&(v.slice_from(r),v.cursor=n,!0)}function i(){for(var r,n,i,s,t=v.cursor;;)if(r=v.cursor,v.bra=r,v.eq_s(1,"ß"))v.ket=v.cursor,v.slice_from("ss");else{if(r>=v.limit)break;v.cursor=r+1}for(v.cursor=t;;)for(n=v.cursor;;){if(i=v.cursor,v.in_grouping(p,97,252)){if(s=v.cursor,v.bra=s,e("u","U",i))break;if(v.cursor=s,e("y","Y",i))break}if(i>=v.limit)return void(v.cursor=n);v.cursor=i+1}}function s(){for(;!v.in_grouping(p,97,252);){if(v.cursor>=v.limit)return!0;v.cursor++}for(;!v.out_grouping(p,97,252);){if(v.cursor>=v.limit)return!0;v.cursor++}return!1}function t(){m=v.limit,l=m;var e=v.cursor+3;0<=e&&e<=v.limit&&(d=e,s()||(m=v.cursor,m=v.limit)return;v.cursor++}}}function c(){return m<=v.cursor}function u(){return l<=v.cursor}function a(){var e,r,n,i,s=v.limit-v.cursor;if(v.ket=v.cursor,(e=v.find_among_b(w,7))&&(v.bra=v.cursor,c()))switch(e){case 1:v.slice_del();break;case 2:v.slice_del(),v.ket=v.cursor,v.eq_s_b(1,"s")&&(v.bra=v.cursor,v.eq_s_b(3,"nis")&&v.slice_del());break;case 3:v.in_grouping_b(g,98,116)&&v.slice_del()}if(v.cursor=v.limit-s,v.ket=v.cursor,(e=v.find_among_b(f,4))&&(v.bra=v.cursor,c()))switch(e){case 1:v.slice_del();break;case 2:if(v.in_grouping_b(k,98,116)){var t=v.cursor-3;v.limit_backward<=t&&t<=v.limit&&(v.cursor=t,v.slice_del())}}if(v.cursor=v.limit-s,v.ket=v.cursor,(e=v.find_among_b(_,8))&&(v.bra=v.cursor,u()))switch(e){case 1:v.slice_del(),v.ket=v.cursor,v.eq_s_b(2,"ig")&&(v.bra=v.cursor,r=v.limit-v.cursor,v.eq_s_b(1,"e")||(v.cursor=v.limit-r,u()&&v.slice_del()));break;case 2:n=v.limit-v.cursor,v.eq_s_b(1,"e")||(v.cursor=v.limit-n,v.slice_del());break;case 3:if(v.slice_del(),v.ket=v.cursor,i=v.limit-v.cursor,!v.eq_s_b(2,"er")&&(v.cursor=v.limit-i,!v.eq_s_b(2,"en")))break;v.bra=v.cursor,c()&&v.slice_del();break;case 4:v.slice_del(),v.ket=v.cursor,e=v.find_among_b(b,2),e&&(v.bra=v.cursor,u()&&1==e&&v.slice_del())}}var d,l,m,h=[new r("",-1,6),new r("U",0,2),new r("Y",0,1),new r("ä",0,3),new r("ö",0,4),new r("ü",0,5)],w=[new r("e",-1,2),new r("em",-1,1),new r("en",-1,2),new r("ern",-1,1),new r("er",-1,1),new r("s",-1,3),new r("es",5,2)],f=[new r("en",-1,1),new r("er",-1,1),new r("st",-1,2),new r("est",2,1)],b=[new r("ig",-1,1),new r("lich",-1,1)],_=[new r("end",-1,1),new r("ig",-1,2),new r("ung",-1,1),new r("lich",-1,3),new r("isch",-1,2),new r("ik",-1,2),new r("heit",-1,3),new r("keit",-1,4)],p=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32,8],g=[117,30,5],k=[117,30,4],v=new n;this.setCurrent=function(e){v.setCurrent(e)},this.getCurrent=function(){return v.getCurrent()},this.stem=function(){var e=v.cursor;return i(),v.cursor=e,t(),v.limit_backward=e,v.cursor=v.limit,a(),v.cursor=v.limit_backward,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.de.stemmer,"stemmer-de"),e.de.stopWordFilter=e.generateStopWordFilter("aber alle allem allen aller alles als also am an ander andere anderem anderen anderer anderes anderm andern anderr anders auch auf aus bei bin bis bist da damit dann das dasselbe dazu daß dein deine deinem deinen deiner deines dem demselben den denn denselben der derer derselbe derselben des desselben dessen dich die dies diese dieselbe dieselben diesem diesen dieser dieses dir doch dort du durch ein eine einem einen einer eines einig einige einigem einigen einiger einiges einmal er es etwas euch euer eure eurem euren eurer eures für gegen gewesen hab habe haben hat hatte hatten hier hin hinter ich ihm ihn ihnen ihr ihre ihrem ihren ihrer ihres im in indem ins ist jede jedem jeden jeder jedes jene jenem jenen jener jenes jetzt kann kein keine keinem keinen keiner keines können könnte machen man manche manchem manchen mancher manches mein meine meinem meinen meiner meines mich mir mit muss musste nach nicht nichts noch nun nur ob oder ohne sehr sein seine seinem seinen seiner seines selbst sich sie sind so solche solchem solchen solcher solches soll sollte sondern sonst um und uns unse unsem unsen unser unses unter viel vom von vor war waren warst was weg weil weiter welche welchem welchen welcher welches wenn werde werden wie wieder will wir wird wirst wo wollen wollte während würde würden zu zum zur zwar zwischen über".split(" ")),e.Pipeline.registerFunction(e.de.stopWordFilter,"stopWordFilter-de")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.du.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.du.min.js deleted file mode 100644 index 49a0f3f0a..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.du.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Lunr languages, `Dutch` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ - -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");console.warn('[Lunr Languages] Please use the "nl" instead of the "du". The "nl" code is the standard code for Dutch language, and "du" will be removed in the next major versions.'),e.du=function(){this.pipeline.reset(),this.pipeline.add(e.du.trimmer,e.du.stopWordFilter,e.du.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.du.stemmer))},e.du.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.du.trimmer=e.trimmerSupport.generateTrimmer(e.du.wordCharacters),e.Pipeline.registerFunction(e.du.trimmer,"trimmer-du"),e.du.stemmer=function(){var r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){function e(){for(var e,r,i,o=C.cursor;;){if(C.bra=C.cursor,e=C.find_among(b,11))switch(C.ket=C.cursor,e){case 1:C.slice_from("a");continue;case 2:C.slice_from("e");continue;case 3:C.slice_from("i");continue;case 4:C.slice_from("o");continue;case 5:C.slice_from("u");continue;case 6:if(C.cursor>=C.limit)break;C.cursor++;continue}break}for(C.cursor=o,C.bra=o,C.eq_s(1,"y")?(C.ket=C.cursor,C.slice_from("Y")):C.cursor=o;;)if(r=C.cursor,C.in_grouping(q,97,232)){if(i=C.cursor,C.bra=i,C.eq_s(1,"i"))C.ket=C.cursor,C.in_grouping(q,97,232)&&(C.slice_from("I"),C.cursor=r);else if(C.cursor=i,C.eq_s(1,"y"))C.ket=C.cursor,C.slice_from("Y"),C.cursor=r;else if(n(r))break}else if(n(r))break}function n(e){return C.cursor=e,e>=C.limit||(C.cursor++,!1)}function o(){_=C.limit,f=_,t()||(_=C.cursor,_<3&&(_=3),t()||(f=C.cursor))}function t(){for(;!C.in_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}for(;!C.out_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}return!1}function s(){for(var e;;)if(C.bra=C.cursor,e=C.find_among(p,3))switch(C.ket=C.cursor,e){case 1:C.slice_from("y");break;case 2:C.slice_from("i");break;case 3:if(C.cursor>=C.limit)return;C.cursor++}}function u(){return _<=C.cursor}function c(){return f<=C.cursor}function a(){var e=C.limit-C.cursor;C.find_among_b(g,3)&&(C.cursor=C.limit-e,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del()))}function l(){var e;w=!1,C.ket=C.cursor,C.eq_s_b(1,"e")&&(C.bra=C.cursor,u()&&(e=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-e,C.slice_del(),w=!0,a())))}function m(){var e;u()&&(e=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-e,C.eq_s_b(3,"gem")||(C.cursor=C.limit-e,C.slice_del(),a())))}function d(){var e,r,i,n,o,t,s=C.limit-C.cursor;if(C.ket=C.cursor,e=C.find_among_b(h,5))switch(C.bra=C.cursor,e){case 1:u()&&C.slice_from("heid");break;case 2:m();break;case 3:u()&&C.out_grouping_b(z,97,232)&&C.slice_del()}if(C.cursor=C.limit-s,l(),C.cursor=C.limit-s,C.ket=C.cursor,C.eq_s_b(4,"heid")&&(C.bra=C.cursor,c()&&(r=C.limit-C.cursor,C.eq_s_b(1,"c")||(C.cursor=C.limit-r,C.slice_del(),C.ket=C.cursor,C.eq_s_b(2,"en")&&(C.bra=C.cursor,m())))),C.cursor=C.limit-s,C.ket=C.cursor,e=C.find_among_b(k,6))switch(C.bra=C.cursor,e){case 1:if(c()){if(C.slice_del(),i=C.limit-C.cursor,C.ket=C.cursor,C.eq_s_b(2,"ig")&&(C.bra=C.cursor,c()&&(n=C.limit-C.cursor,!C.eq_s_b(1,"e")))){C.cursor=C.limit-n,C.slice_del();break}C.cursor=C.limit-i,a()}break;case 2:c()&&(o=C.limit-C.cursor,C.eq_s_b(1,"e")||(C.cursor=C.limit-o,C.slice_del()));break;case 3:c()&&(C.slice_del(),l());break;case 4:c()&&C.slice_del();break;case 5:c()&&w&&C.slice_del()}C.cursor=C.limit-s,C.out_grouping_b(j,73,232)&&(t=C.limit-C.cursor,C.find_among_b(v,4)&&C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-t,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del())))}var f,_,w,b=[new r("",-1,6),new r("á",0,1),new r("ä",0,1),new r("é",0,2),new r("ë",0,2),new r("í",0,3),new r("ï",0,3),new r("ó",0,4),new r("ö",0,4),new r("ú",0,5),new r("ü",0,5)],p=[new r("",-1,3),new r("I",0,2),new r("Y",0,1)],g=[new r("dd",-1,-1),new r("kk",-1,-1),new r("tt",-1,-1)],h=[new r("ene",-1,2),new r("se",-1,3),new r("en",-1,2),new r("heden",2,1),new r("s",-1,3)],k=[new r("end",-1,1),new r("ig",-1,2),new r("ing",-1,1),new r("lijk",-1,3),new r("baar",-1,4),new r("bar",-1,5)],v=[new r("aa",-1,-1),new r("ee",-1,-1),new r("oo",-1,-1),new r("uu",-1,-1)],q=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],j=[1,0,0,17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],z=[17,67,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],C=new i;this.setCurrent=function(e){C.setCurrent(e)},this.getCurrent=function(){return C.getCurrent()},this.stem=function(){var r=C.cursor;return e(),C.cursor=r,o(),C.limit_backward=r,C.cursor=C.limit,d(),C.cursor=C.limit_backward,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.du.stemmer,"stemmer-du"),e.du.stopWordFilter=e.generateStopWordFilter(" aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou".split(" ")),e.Pipeline.registerFunction(e.du.stopWordFilter,"stopWordFilter-du")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.es.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.es.min.js deleted file mode 100644 index 2989d3426..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.es.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Lunr languages, `Spanish` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ - -!function(e,s){"function"==typeof define&&define.amd?define(s):"object"==typeof exports?module.exports=s():s()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.es=function(){this.pipeline.reset(),this.pipeline.add(e.es.trimmer,e.es.stopWordFilter,e.es.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.es.stemmer))},e.es.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.es.trimmer=e.trimmerSupport.generateTrimmer(e.es.wordCharacters),e.Pipeline.registerFunction(e.es.trimmer,"trimmer-es"),e.es.stemmer=function(){var s=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,n=new function(){function e(){if(A.out_grouping(x,97,252)){for(;!A.in_grouping(x,97,252);){if(A.cursor>=A.limit)return!0;A.cursor++}return!1}return!0}function n(){if(A.in_grouping(x,97,252)){var s=A.cursor;if(e()){if(A.cursor=s,!A.in_grouping(x,97,252))return!0;for(;!A.out_grouping(x,97,252);){if(A.cursor>=A.limit)return!0;A.cursor++}}return!1}return!0}function i(){var s,r=A.cursor;if(n()){if(A.cursor=r,!A.out_grouping(x,97,252))return;if(s=A.cursor,e()){if(A.cursor=s,!A.in_grouping(x,97,252)||A.cursor>=A.limit)return;A.cursor++}}g=A.cursor}function a(){for(;!A.in_grouping(x,97,252);){if(A.cursor>=A.limit)return!1;A.cursor++}for(;!A.out_grouping(x,97,252);){if(A.cursor>=A.limit)return!1;A.cursor++}return!0}function t(){var e=A.cursor;g=A.limit,p=g,v=g,i(),A.cursor=e,a()&&(p=A.cursor,a()&&(v=A.cursor))}function o(){for(var e;;){if(A.bra=A.cursor,e=A.find_among(k,6))switch(A.ket=A.cursor,e){case 1:A.slice_from("a");continue;case 2:A.slice_from("e");continue;case 3:A.slice_from("i");continue;case 4:A.slice_from("o");continue;case 5:A.slice_from("u");continue;case 6:if(A.cursor>=A.limit)break;A.cursor++;continue}break}}function u(){return g<=A.cursor}function w(){return p<=A.cursor}function c(){return v<=A.cursor}function m(){var e;if(A.ket=A.cursor,A.find_among_b(y,13)&&(A.bra=A.cursor,(e=A.find_among_b(q,11))&&u()))switch(e){case 1:A.bra=A.cursor,A.slice_from("iendo");break;case 2:A.bra=A.cursor,A.slice_from("ando");break;case 3:A.bra=A.cursor,A.slice_from("ar");break;case 4:A.bra=A.cursor,A.slice_from("er");break;case 5:A.bra=A.cursor,A.slice_from("ir");break;case 6:A.slice_del();break;case 7:A.eq_s_b(1,"u")&&A.slice_del()}}function l(e,s){if(!c())return!0;A.slice_del(),A.ket=A.cursor;var r=A.find_among_b(e,s);return r&&(A.bra=A.cursor,1==r&&c()&&A.slice_del()),!1}function d(e){return!c()||(A.slice_del(),A.ket=A.cursor,A.eq_s_b(2,e)&&(A.bra=A.cursor,c()&&A.slice_del()),!1)}function b(){var e;if(A.ket=A.cursor,e=A.find_among_b(S,46)){switch(A.bra=A.cursor,e){case 1:if(!c())return!1;A.slice_del();break;case 2:if(d("ic"))return!1;break;case 3:if(!c())return!1;A.slice_from("log");break;case 4:if(!c())return!1;A.slice_from("u");break;case 5:if(!c())return!1;A.slice_from("ente");break;case 6:if(!w())return!1;A.slice_del(),A.ket=A.cursor,e=A.find_among_b(C,4),e&&(A.bra=A.cursor,c()&&(A.slice_del(),1==e&&(A.ket=A.cursor,A.eq_s_b(2,"at")&&(A.bra=A.cursor,c()&&A.slice_del()))));break;case 7:if(l(P,3))return!1;break;case 8:if(l(F,3))return!1;break;case 9:if(d("at"))return!1}return!0}return!1}function f(){var e,s;if(A.cursor>=g&&(s=A.limit_backward,A.limit_backward=g,A.ket=A.cursor,e=A.find_among_b(W,12),A.limit_backward=s,e)){if(A.bra=A.cursor,1==e){if(!A.eq_s_b(1,"u"))return!1;A.slice_del()}return!0}return!1}function _(){var e,s,r,n;if(A.cursor>=g&&(s=A.limit_backward,A.limit_backward=g,A.ket=A.cursor,e=A.find_among_b(L,96),A.limit_backward=s,e))switch(A.bra=A.cursor,e){case 1:r=A.limit-A.cursor,A.eq_s_b(1,"u")?(n=A.limit-A.cursor,A.eq_s_b(1,"g")?A.cursor=A.limit-n:A.cursor=A.limit-r):A.cursor=A.limit-r,A.bra=A.cursor;case 2:A.slice_del()}}function h(){var e,s;if(A.ket=A.cursor,e=A.find_among_b(z,8))switch(A.bra=A.cursor,e){case 1:u()&&A.slice_del();break;case 2:u()&&(A.slice_del(),A.ket=A.cursor,A.eq_s_b(1,"u")&&(A.bra=A.cursor,s=A.limit-A.cursor,A.eq_s_b(1,"g")&&(A.cursor=A.limit-s,u()&&A.slice_del())))}}var v,p,g,k=[new s("",-1,6),new s("á",0,1),new s("é",0,2),new s("í",0,3),new s("ó",0,4),new s("ú",0,5)],y=[new s("la",-1,-1),new s("sela",0,-1),new s("le",-1,-1),new s("me",-1,-1),new s("se",-1,-1),new s("lo",-1,-1),new s("selo",5,-1),new s("las",-1,-1),new s("selas",7,-1),new s("les",-1,-1),new s("los",-1,-1),new s("selos",10,-1),new s("nos",-1,-1)],q=[new s("ando",-1,6),new s("iendo",-1,6),new s("yendo",-1,7),new s("ándo",-1,2),new s("iéndo",-1,1),new s("ar",-1,6),new s("er",-1,6),new s("ir",-1,6),new s("ár",-1,3),new s("ér",-1,4),new s("ír",-1,5)],C=[new s("ic",-1,-1),new s("ad",-1,-1),new s("os",-1,-1),new s("iv",-1,1)],P=[new s("able",-1,1),new s("ible",-1,1),new s("ante",-1,1)],F=[new s("ic",-1,1),new s("abil",-1,1),new s("iv",-1,1)],S=[new s("ica",-1,1),new s("ancia",-1,2),new s("encia",-1,5),new s("adora",-1,2),new s("osa",-1,1),new s("ista",-1,1),new s("iva",-1,9),new s("anza",-1,1),new s("logía",-1,3),new s("idad",-1,8),new s("able",-1,1),new s("ible",-1,1),new s("ante",-1,2),new s("mente",-1,7),new s("amente",13,6),new s("ación",-1,2),new s("ución",-1,4),new s("ico",-1,1),new s("ismo",-1,1),new s("oso",-1,1),new s("amiento",-1,1),new s("imiento",-1,1),new s("ivo",-1,9),new s("ador",-1,2),new s("icas",-1,1),new s("ancias",-1,2),new s("encias",-1,5),new s("adoras",-1,2),new s("osas",-1,1),new s("istas",-1,1),new s("ivas",-1,9),new s("anzas",-1,1),new s("logías",-1,3),new s("idades",-1,8),new s("ables",-1,1),new s("ibles",-1,1),new s("aciones",-1,2),new s("uciones",-1,4),new s("adores",-1,2),new s("antes",-1,2),new s("icos",-1,1),new s("ismos",-1,1),new s("osos",-1,1),new s("amientos",-1,1),new s("imientos",-1,1),new s("ivos",-1,9)],W=[new s("ya",-1,1),new s("ye",-1,1),new s("yan",-1,1),new s("yen",-1,1),new s("yeron",-1,1),new s("yendo",-1,1),new s("yo",-1,1),new s("yas",-1,1),new s("yes",-1,1),new s("yais",-1,1),new s("yamos",-1,1),new s("yó",-1,1)],L=[new s("aba",-1,2),new s("ada",-1,2),new s("ida",-1,2),new s("ara",-1,2),new s("iera",-1,2),new s("ía",-1,2),new s("aría",5,2),new s("ería",5,2),new s("iría",5,2),new s("ad",-1,2),new s("ed",-1,2),new s("id",-1,2),new s("ase",-1,2),new s("iese",-1,2),new s("aste",-1,2),new s("iste",-1,2),new s("an",-1,2),new s("aban",16,2),new s("aran",16,2),new s("ieran",16,2),new s("ían",16,2),new s("arían",20,2),new s("erían",20,2),new s("irían",20,2),new s("en",-1,1),new s("asen",24,2),new s("iesen",24,2),new s("aron",-1,2),new s("ieron",-1,2),new s("arán",-1,2),new s("erán",-1,2),new s("irán",-1,2),new s("ado",-1,2),new s("ido",-1,2),new s("ando",-1,2),new s("iendo",-1,2),new s("ar",-1,2),new s("er",-1,2),new s("ir",-1,2),new s("as",-1,2),new s("abas",39,2),new s("adas",39,2),new s("idas",39,2),new s("aras",39,2),new s("ieras",39,2),new s("ías",39,2),new s("arías",45,2),new s("erías",45,2),new s("irías",45,2),new s("es",-1,1),new s("ases",49,2),new s("ieses",49,2),new s("abais",-1,2),new s("arais",-1,2),new s("ierais",-1,2),new s("íais",-1,2),new s("aríais",55,2),new s("eríais",55,2),new s("iríais",55,2),new s("aseis",-1,2),new s("ieseis",-1,2),new s("asteis",-1,2),new s("isteis",-1,2),new s("áis",-1,2),new s("éis",-1,1),new s("aréis",64,2),new s("eréis",64,2),new s("iréis",64,2),new s("ados",-1,2),new s("idos",-1,2),new s("amos",-1,2),new s("ábamos",70,2),new s("áramos",70,2),new s("iéramos",70,2),new s("íamos",70,2),new s("aríamos",74,2),new s("eríamos",74,2),new s("iríamos",74,2),new s("emos",-1,1),new s("aremos",78,2),new s("eremos",78,2),new s("iremos",78,2),new s("ásemos",78,2),new s("iésemos",78,2),new s("imos",-1,2),new s("arás",-1,2),new s("erás",-1,2),new s("irás",-1,2),new s("ís",-1,2),new s("ará",-1,2),new s("erá",-1,2),new s("irá",-1,2),new s("aré",-1,2),new s("eré",-1,2),new s("iré",-1,2),new s("ió",-1,2)],z=[new s("a",-1,1),new s("e",-1,2),new s("o",-1,1),new s("os",-1,1),new s("á",-1,1),new s("é",-1,2),new s("í",-1,1),new s("ó",-1,1)],x=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,4,10],A=new r;this.setCurrent=function(e){A.setCurrent(e)},this.getCurrent=function(){return A.getCurrent()},this.stem=function(){var e=A.cursor;return t(),A.limit_backward=e,A.cursor=A.limit,m(),A.cursor=A.limit,b()||(A.cursor=A.limit,f()||(A.cursor=A.limit,_())),A.cursor=A.limit,h(),A.cursor=A.limit_backward,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.es.stemmer,"stemmer-es"),e.es.stopWordFilter=e.generateStopWordFilter("a al algo algunas algunos ante antes como con contra cual cuando de del desde donde durante e el ella ellas ellos en entre era erais eran eras eres es esa esas ese eso esos esta estaba estabais estaban estabas estad estada estadas estado estados estamos estando estar estaremos estará estarán estarás estaré estaréis estaría estaríais estaríamos estarían estarías estas este estemos esto estos estoy estuve estuviera estuvierais estuvieran estuvieras estuvieron estuviese estuvieseis estuviesen estuvieses estuvimos estuviste estuvisteis estuviéramos estuviésemos estuvo está estábamos estáis están estás esté estéis estén estés fue fuera fuerais fueran fueras fueron fuese fueseis fuesen fueses fui fuimos fuiste fuisteis fuéramos fuésemos ha habida habidas habido habidos habiendo habremos habrá habrán habrás habré habréis habría habríais habríamos habrían habrías habéis había habíais habíamos habían habías han has hasta hay haya hayamos hayan hayas hayáis he hemos hube hubiera hubierais hubieran hubieras hubieron hubiese hubieseis hubiesen hubieses hubimos hubiste hubisteis hubiéramos hubiésemos hubo la las le les lo los me mi mis mucho muchos muy más mí mía mías mío míos nada ni no nos nosotras nosotros nuestra nuestras nuestro nuestros o os otra otras otro otros para pero poco por porque que quien quienes qué se sea seamos sean seas seremos será serán serás seré seréis sería seríais seríamos serían serías seáis sido siendo sin sobre sois somos son soy su sus suya suyas suyo suyos sí también tanto te tendremos tendrá tendrán tendrás tendré tendréis tendría tendríais tendríamos tendrían tendrías tened tenemos tenga tengamos tengan tengas tengo tengáis tenida tenidas tenido tenidos teniendo tenéis tenía teníais teníamos tenían tenías ti tiene tienen tienes todo todos tu tus tuve tuviera tuvierais tuvieran tuvieras tuvieron tuviese tuvieseis tuviesen tuvieses tuvimos tuviste tuvisteis tuviéramos tuviésemos tuvo tuya tuyas tuyo tuyos tú un una uno unos vosotras vosotros vuestra vuestras vuestro vuestros y ya yo él éramos".split(" ")),e.Pipeline.registerFunction(e.es.stopWordFilter,"stopWordFilter-es")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.fi.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.fi.min.js deleted file mode 100644 index 29f5dfcea..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.fi.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Lunr languages, `Finnish` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ - -!function(i,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():e()(i.lunr)}(this,function(){return function(i){if(void 0===i)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===i.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");i.fi=function(){this.pipeline.reset(),this.pipeline.add(i.fi.trimmer,i.fi.stopWordFilter,i.fi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(i.fi.stemmer))},i.fi.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",i.fi.trimmer=i.trimmerSupport.generateTrimmer(i.fi.wordCharacters),i.Pipeline.registerFunction(i.fi.trimmer,"trimmer-fi"),i.fi.stemmer=function(){var e=i.stemmerSupport.Among,r=i.stemmerSupport.SnowballProgram,n=new function(){function i(){f=A.limit,d=f,n()||(f=A.cursor,n()||(d=A.cursor))}function n(){for(var i;;){if(i=A.cursor,A.in_grouping(W,97,246))break;if(A.cursor=i,i>=A.limit)return!0;A.cursor++}for(A.cursor=i;!A.out_grouping(W,97,246);){if(A.cursor>=A.limit)return!0;A.cursor++}return!1}function t(){return d<=A.cursor}function s(){var i,e;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(h,10)){switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:if(!A.in_grouping_b(x,97,246))return;break;case 2:if(!t())return}A.slice_del()}else A.limit_backward=e}function o(){var i,e,r;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(v,9))switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:r=A.limit-A.cursor,A.eq_s_b(1,"k")||(A.cursor=A.limit-r,A.slice_del());break;case 2:A.slice_del(),A.ket=A.cursor,A.eq_s_b(3,"kse")&&(A.bra=A.cursor,A.slice_from("ksi"));break;case 3:A.slice_del();break;case 4:A.find_among_b(p,6)&&A.slice_del();break;case 5:A.find_among_b(g,6)&&A.slice_del();break;case 6:A.find_among_b(j,2)&&A.slice_del()}else A.limit_backward=e}function l(){return A.find_among_b(q,7)}function a(){return A.eq_s_b(1,"i")&&A.in_grouping_b(L,97,246)}function u(){var i,e,r;if(A.cursor>=f)if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,i=A.find_among_b(C,30)){switch(A.bra=A.cursor,A.limit_backward=e,i){case 1:if(!A.eq_s_b(1,"a"))return;break;case 2:case 9:if(!A.eq_s_b(1,"e"))return;break;case 3:if(!A.eq_s_b(1,"i"))return;break;case 4:if(!A.eq_s_b(1,"o"))return;break;case 5:if(!A.eq_s_b(1,"ä"))return;break;case 6:if(!A.eq_s_b(1,"ö"))return;break;case 7:if(r=A.limit-A.cursor,!l()&&(A.cursor=A.limit-r,!A.eq_s_b(2,"ie"))){A.cursor=A.limit-r;break}if(A.cursor=A.limit-r,A.cursor<=A.limit_backward){A.cursor=A.limit-r;break}A.cursor--,A.bra=A.cursor;break;case 8:if(!A.in_grouping_b(W,97,246)||!A.out_grouping_b(W,97,246))return}A.slice_del(),k=!0}else A.limit_backward=e}function c(){var i,e,r;if(A.cursor>=d)if(e=A.limit_backward,A.limit_backward=d,A.ket=A.cursor,i=A.find_among_b(P,14)){if(A.bra=A.cursor,A.limit_backward=e,1==i){if(r=A.limit-A.cursor,A.eq_s_b(2,"po"))return;A.cursor=A.limit-r}A.slice_del()}else A.limit_backward=e}function m(){var i;A.cursor>=f&&(i=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,A.find_among_b(F,2)?(A.bra=A.cursor,A.limit_backward=i,A.slice_del()):A.limit_backward=i)}function w(){var i,e,r,n,t,s;if(A.cursor>=f){if(e=A.limit_backward,A.limit_backward=f,A.ket=A.cursor,A.eq_s_b(1,"t")&&(A.bra=A.cursor,r=A.limit-A.cursor,A.in_grouping_b(W,97,246)&&(A.cursor=A.limit-r,A.slice_del(),A.limit_backward=e,n=A.limit-A.cursor,A.cursor>=d&&(A.cursor=d,t=A.limit_backward,A.limit_backward=A.cursor,A.cursor=A.limit-n,A.ket=A.cursor,i=A.find_among_b(S,2))))){if(A.bra=A.cursor,A.limit_backward=t,1==i){if(s=A.limit-A.cursor,A.eq_s_b(2,"po"))return;A.cursor=A.limit-s}return void A.slice_del()}A.limit_backward=e}}function _(){var i,e,r,n;if(A.cursor>=f){for(i=A.limit_backward,A.limit_backward=f,e=A.limit-A.cursor,l()&&(A.cursor=A.limit-e,A.ket=A.cursor,A.cursor>A.limit_backward&&(A.cursor--,A.bra=A.cursor,A.slice_del())),A.cursor=A.limit-e,A.ket=A.cursor,A.in_grouping_b(y,97,228)&&(A.bra=A.cursor,A.out_grouping_b(W,97,246)&&A.slice_del()),A.cursor=A.limit-e,A.ket=A.cursor,A.eq_s_b(1,"j")&&(A.bra=A.cursor,r=A.limit-A.cursor,A.eq_s_b(1,"o")?A.slice_del():(A.cursor=A.limit-r,A.eq_s_b(1,"u")&&A.slice_del())),A.cursor=A.limit-e,A.ket=A.cursor,A.eq_s_b(1,"o")&&(A.bra=A.cursor,A.eq_s_b(1,"j")&&A.slice_del()),A.cursor=A.limit-e,A.limit_backward=i;;){if(n=A.limit-A.cursor,A.out_grouping_b(W,97,246)){A.cursor=A.limit-n;break}if(A.cursor=A.limit-n,A.cursor<=A.limit_backward)return;A.cursor--}A.ket=A.cursor,A.cursor>A.limit_backward&&(A.cursor--,A.bra=A.cursor,b=A.slice_to(),A.eq_v_b(b)&&A.slice_del())}}var k,b,d,f,h=[new e("pa",-1,1),new e("sti",-1,2),new e("kaan",-1,1),new e("han",-1,1),new e("kin",-1,1),new e("hän",-1,1),new e("kään",-1,1),new e("ko",-1,1),new e("pä",-1,1),new e("kö",-1,1)],p=[new e("lla",-1,-1),new e("na",-1,-1),new e("ssa",-1,-1),new e("ta",-1,-1),new e("lta",3,-1),new e("sta",3,-1)],g=[new e("llä",-1,-1),new e("nä",-1,-1),new e("ssä",-1,-1),new e("tä",-1,-1),new e("ltä",3,-1),new e("stä",3,-1)],j=[new e("lle",-1,-1),new e("ine",-1,-1)],v=[new e("nsa",-1,3),new e("mme",-1,3),new e("nne",-1,3),new e("ni",-1,2),new e("si",-1,1),new e("an",-1,4),new e("en",-1,6),new e("än",-1,5),new e("nsä",-1,3)],q=[new e("aa",-1,-1),new e("ee",-1,-1),new e("ii",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1),new e("ää",-1,-1),new e("öö",-1,-1)],C=[new e("a",-1,8),new e("lla",0,-1),new e("na",0,-1),new e("ssa",0,-1),new e("ta",0,-1),new e("lta",4,-1),new e("sta",4,-1),new e("tta",4,9),new e("lle",-1,-1),new e("ine",-1,-1),new e("ksi",-1,-1),new e("n",-1,7),new e("han",11,1),new e("den",11,-1,a),new e("seen",11,-1,l),new e("hen",11,2),new e("tten",11,-1,a),new e("hin",11,3),new e("siin",11,-1,a),new e("hon",11,4),new e("hän",11,5),new e("hön",11,6),new e("ä",-1,8),new e("llä",22,-1),new e("nä",22,-1),new e("ssä",22,-1),new e("tä",22,-1),new e("ltä",26,-1),new e("stä",26,-1),new e("ttä",26,9)],P=[new e("eja",-1,-1),new e("mma",-1,1),new e("imma",1,-1),new e("mpa",-1,1),new e("impa",3,-1),new e("mmi",-1,1),new e("immi",5,-1),new e("mpi",-1,1),new e("impi",7,-1),new e("ejä",-1,-1),new e("mmä",-1,1),new e("immä",10,-1),new e("mpä",-1,1),new e("impä",12,-1)],F=[new e("i",-1,-1),new e("j",-1,-1)],S=[new e("mma",-1,1),new e("imma",0,-1)],y=[17,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8],W=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],L=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],x=[17,97,24,1,0,0,0,0,0,0,0,0,0,0,0,0,8,0,32],A=new r;this.setCurrent=function(i){A.setCurrent(i)},this.getCurrent=function(){return A.getCurrent()},this.stem=function(){var e=A.cursor;return i(),k=!1,A.limit_backward=e,A.cursor=A.limit,s(),A.cursor=A.limit,o(),A.cursor=A.limit,u(),A.cursor=A.limit,c(),A.cursor=A.limit,k?(m(),A.cursor=A.limit):(A.cursor=A.limit,w(),A.cursor=A.limit),_(),!0}};return function(i){return"function"==typeof i.update?i.update(function(i){return n.setCurrent(i),n.stem(),n.getCurrent()}):(n.setCurrent(i),n.stem(),n.getCurrent())}}(),i.Pipeline.registerFunction(i.fi.stemmer,"stemmer-fi"),i.fi.stopWordFilter=i.generateStopWordFilter("ei eivät emme en et ette että he heidän heidät heihin heille heillä heiltä heissä heistä heitä hän häneen hänelle hänellä häneltä hänen hänessä hänestä hänet häntä itse ja johon joiden joihin joiksi joilla joille joilta joina joissa joista joita joka joksi jolla jolle jolta jona jonka jos jossa josta jota jotka kanssa keiden keihin keiksi keille keillä keiltä keinä keissä keistä keitä keneen keneksi kenelle kenellä keneltä kenen kenenä kenessä kenestä kenet ketkä ketkä ketä koska kuin kuka kun me meidän meidät meihin meille meillä meiltä meissä meistä meitä mihin miksi mikä mille millä miltä minkä minkä minua minulla minulle minulta minun minussa minusta minut minuun minä minä missä mistä mitkä mitä mukaan mutta ne niiden niihin niiksi niille niillä niiltä niin niin niinä niissä niistä niitä noiden noihin noiksi noilla noille noilta noin noina noissa noista noita nuo nyt näiden näihin näiksi näille näillä näiltä näinä näissä näistä näitä nämä ole olemme olen olet olette oli olimme olin olisi olisimme olisin olisit olisitte olisivat olit olitte olivat olla olleet ollut on ovat poikki se sekä sen siihen siinä siitä siksi sille sillä sillä siltä sinua sinulla sinulle sinulta sinun sinussa sinusta sinut sinuun sinä sinä sitä tai te teidän teidät teihin teille teillä teiltä teissä teistä teitä tuo tuohon tuoksi tuolla tuolle tuolta tuon tuona tuossa tuosta tuota tähän täksi tälle tällä tältä tämä tämän tänä tässä tästä tätä vaan vai vaikka yli".split(" ")),i.Pipeline.registerFunction(i.fi.stopWordFilter,"stopWordFilter-fi")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.fr.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.fr.min.js deleted file mode 100644 index 68cd0094a..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.fr.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Lunr languages, `French` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ - -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.fr=function(){this.pipeline.reset(),this.pipeline.add(e.fr.trimmer,e.fr.stopWordFilter,e.fr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.fr.stemmer))},e.fr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.fr.trimmer=e.trimmerSupport.generateTrimmer(e.fr.wordCharacters),e.Pipeline.registerFunction(e.fr.trimmer,"trimmer-fr"),e.fr.stemmer=function(){var r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,i=new function(){function e(e,r,s){return!(!W.eq_s(1,e)||(W.ket=W.cursor,!W.in_grouping(F,97,251)))&&(W.slice_from(r),W.cursor=s,!0)}function i(e,r,s){return!!W.eq_s(1,e)&&(W.ket=W.cursor,W.slice_from(r),W.cursor=s,!0)}function n(){for(var r,s;;){if(r=W.cursor,W.in_grouping(F,97,251)){if(W.bra=W.cursor,s=W.cursor,e("u","U",r))continue;if(W.cursor=s,e("i","I",r))continue;if(W.cursor=s,i("y","Y",r))continue}if(W.cursor=r,W.bra=r,!e("y","Y",r)){if(W.cursor=r,W.eq_s(1,"q")&&(W.bra=W.cursor,i("u","U",r)))continue;if(W.cursor=r,r>=W.limit)return;W.cursor++}}}function t(){for(;!W.in_grouping(F,97,251);){if(W.cursor>=W.limit)return!0;W.cursor++}for(;!W.out_grouping(F,97,251);){if(W.cursor>=W.limit)return!0;W.cursor++}return!1}function u(){var e=W.cursor;if(q=W.limit,g=q,p=q,W.in_grouping(F,97,251)&&W.in_grouping(F,97,251)&&W.cursor=W.limit){W.cursor=q;break}W.cursor++}while(!W.in_grouping(F,97,251))}q=W.cursor,W.cursor=e,t()||(g=W.cursor,t()||(p=W.cursor))}function o(){for(var e,r;;){if(r=W.cursor,W.bra=r,!(e=W.find_among(h,4)))break;switch(W.ket=W.cursor,e){case 1:W.slice_from("i");break;case 2:W.slice_from("u");break;case 3:W.slice_from("y");break;case 4:if(W.cursor>=W.limit)return;W.cursor++}}}function c(){return q<=W.cursor}function a(){return g<=W.cursor}function l(){return p<=W.cursor}function w(){var e,r;if(W.ket=W.cursor,e=W.find_among_b(C,43)){switch(W.bra=W.cursor,e){case 1:if(!l())return!1;W.slice_del();break;case 2:if(!l())return!1;W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"ic")&&(W.bra=W.cursor,l()?W.slice_del():W.slice_from("iqU"));break;case 3:if(!l())return!1;W.slice_from("log");break;case 4:if(!l())return!1;W.slice_from("u");break;case 5:if(!l())return!1;W.slice_from("ent");break;case 6:if(!c())return!1;if(W.slice_del(),W.ket=W.cursor,e=W.find_among_b(z,6))switch(W.bra=W.cursor,e){case 1:l()&&(W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"at")&&(W.bra=W.cursor,l()&&W.slice_del()));break;case 2:l()?W.slice_del():a()&&W.slice_from("eux");break;case 3:l()&&W.slice_del();break;case 4:c()&&W.slice_from("i")}break;case 7:if(!l())return!1;if(W.slice_del(),W.ket=W.cursor,e=W.find_among_b(y,3))switch(W.bra=W.cursor,e){case 1:l()?W.slice_del():W.slice_from("abl");break;case 2:l()?W.slice_del():W.slice_from("iqU");break;case 3:l()&&W.slice_del()}break;case 8:if(!l())return!1;if(W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"at")&&(W.bra=W.cursor,l()&&(W.slice_del(),W.ket=W.cursor,W.eq_s_b(2,"ic")))){W.bra=W.cursor,l()?W.slice_del():W.slice_from("iqU");break}break;case 9:W.slice_from("eau");break;case 10:if(!a())return!1;W.slice_from("al");break;case 11:if(l())W.slice_del();else{if(!a())return!1;W.slice_from("eux")}break;case 12:if(!a()||!W.out_grouping_b(F,97,251))return!1;W.slice_del();break;case 13:return c()&&W.slice_from("ant"),!1;case 14:return c()&&W.slice_from("ent"),!1;case 15:return r=W.limit-W.cursor,W.in_grouping_b(F,97,251)&&c()&&(W.cursor=W.limit-r,W.slice_del()),!1}return!0}return!1}function f(){var e,r;if(W.cursor=q){if(s=W.limit_backward,W.limit_backward=q,W.ket=W.cursor,e=W.find_among_b(P,7))switch(W.bra=W.cursor,e){case 1:if(l()){if(i=W.limit-W.cursor,!W.eq_s_b(1,"s")&&(W.cursor=W.limit-i,!W.eq_s_b(1,"t")))break;W.slice_del()}break;case 2:W.slice_from("i");break;case 3:W.slice_del();break;case 4:W.eq_s_b(2,"gu")&&W.slice_del()}W.limit_backward=s}}function b(){var e=W.limit-W.cursor;W.find_among_b(U,5)&&(W.cursor=W.limit-e,W.ket=W.cursor,W.cursor>W.limit_backward&&(W.cursor--,W.bra=W.cursor,W.slice_del()))}function d(){for(var e,r=1;W.out_grouping_b(F,97,251);)r--;if(r<=0){if(W.ket=W.cursor,e=W.limit-W.cursor,!W.eq_s_b(1,"é")&&(W.cursor=W.limit-e,!W.eq_s_b(1,"è")))return;W.bra=W.cursor,W.slice_from("e")}}function k(){if(!w()&&(W.cursor=W.limit,!f()&&(W.cursor=W.limit,!m())))return W.cursor=W.limit,void _();W.cursor=W.limit,W.ket=W.cursor,W.eq_s_b(1,"Y")?(W.bra=W.cursor,W.slice_from("i")):(W.cursor=W.limit,W.eq_s_b(1,"ç")&&(W.bra=W.cursor,W.slice_from("c")))}var p,g,q,v=[new r("col",-1,-1),new r("par",-1,-1),new r("tap",-1,-1)],h=[new r("",-1,4),new r("I",0,1),new r("U",0,2),new r("Y",0,3)],z=[new r("iqU",-1,3),new r("abl",-1,3),new r("Ièr",-1,4),new r("ièr",-1,4),new r("eus",-1,2),new r("iv",-1,1)],y=[new r("ic",-1,2),new r("abil",-1,1),new r("iv",-1,3)],C=[new r("iqUe",-1,1),new r("atrice",-1,2),new r("ance",-1,1),new r("ence",-1,5),new r("logie",-1,3),new r("able",-1,1),new r("isme",-1,1),new r("euse",-1,11),new r("iste",-1,1),new r("ive",-1,8),new r("if",-1,8),new r("usion",-1,4),new r("ation",-1,2),new r("ution",-1,4),new r("ateur",-1,2),new r("iqUes",-1,1),new r("atrices",-1,2),new r("ances",-1,1),new r("ences",-1,5),new r("logies",-1,3),new r("ables",-1,1),new r("ismes",-1,1),new r("euses",-1,11),new r("istes",-1,1),new r("ives",-1,8),new r("ifs",-1,8),new r("usions",-1,4),new r("ations",-1,2),new r("utions",-1,4),new r("ateurs",-1,2),new r("ments",-1,15),new r("ements",30,6),new r("issements",31,12),new r("ités",-1,7),new r("ment",-1,15),new r("ement",34,6),new r("issement",35,12),new r("amment",34,13),new r("emment",34,14),new r("aux",-1,10),new r("eaux",39,9),new r("eux",-1,1),new r("ité",-1,7)],x=[new r("ira",-1,1),new r("ie",-1,1),new r("isse",-1,1),new r("issante",-1,1),new r("i",-1,1),new r("irai",4,1),new r("ir",-1,1),new r("iras",-1,1),new r("ies",-1,1),new r("îmes",-1,1),new r("isses",-1,1),new r("issantes",-1,1),new r("îtes",-1,1),new r("is",-1,1),new r("irais",13,1),new r("issais",13,1),new r("irions",-1,1),new r("issions",-1,1),new r("irons",-1,1),new r("issons",-1,1),new r("issants",-1,1),new r("it",-1,1),new r("irait",21,1),new r("issait",21,1),new r("issant",-1,1),new r("iraIent",-1,1),new r("issaIent",-1,1),new r("irent",-1,1),new r("issent",-1,1),new r("iront",-1,1),new r("ît",-1,1),new r("iriez",-1,1),new r("issiez",-1,1),new r("irez",-1,1),new r("issez",-1,1)],I=[new r("a",-1,3),new r("era",0,2),new r("asse",-1,3),new r("ante",-1,3),new r("ée",-1,2),new r("ai",-1,3),new r("erai",5,2),new r("er",-1,2),new r("as",-1,3),new r("eras",8,2),new r("âmes",-1,3),new r("asses",-1,3),new r("antes",-1,3),new r("âtes",-1,3),new r("ées",-1,2),new r("ais",-1,3),new r("erais",15,2),new r("ions",-1,1),new r("erions",17,2),new r("assions",17,3),new r("erons",-1,2),new r("ants",-1,3),new r("és",-1,2),new r("ait",-1,3),new r("erait",23,2),new r("ant",-1,3),new r("aIent",-1,3),new r("eraIent",26,2),new r("èrent",-1,2),new r("assent",-1,3),new r("eront",-1,2),new r("ât",-1,3),new r("ez",-1,2),new r("iez",32,2),new r("eriez",33,2),new r("assiez",33,3),new r("erez",32,2),new r("é",-1,2)],P=[new r("e",-1,3),new r("Ière",0,2),new r("ière",0,2),new r("ion",-1,1),new r("Ier",-1,2),new r("ier",-1,2),new r("ë",-1,4)],U=[new r("ell",-1,-1),new r("eill",-1,-1),new r("enn",-1,-1),new r("onn",-1,-1),new r("ett",-1,-1)],F=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,128,130,103,8,5],S=[1,65,20,0,0,0,0,0,0,0,0,0,0,0,0,0,128],W=new s;this.setCurrent=function(e){W.setCurrent(e)},this.getCurrent=function(){return W.getCurrent()},this.stem=function(){var e=W.cursor;return n(),W.cursor=e,u(),W.limit_backward=e,W.cursor=W.limit,k(),W.cursor=W.limit,b(),W.cursor=W.limit,d(),W.cursor=W.limit_backward,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.fr.stemmer,"stemmer-fr"),e.fr.stopWordFilter=e.generateStopWordFilter("ai aie aient aies ait as au aura aurai auraient aurais aurait auras aurez auriez aurions aurons auront aux avaient avais avait avec avez aviez avions avons ayant ayez ayons c ce ceci celà ces cet cette d dans de des du elle en es est et eu eue eues eurent eus eusse eussent eusses eussiez eussions eut eux eûmes eût eûtes furent fus fusse fussent fusses fussiez fussions fut fûmes fût fûtes ici il ils j je l la le les leur leurs lui m ma mais me mes moi mon même n ne nos notre nous on ont ou par pas pour qu que quel quelle quelles quels qui s sa sans se sera serai seraient serais serait seras serez seriez serions serons seront ses soi soient sois soit sommes son sont soyez soyons suis sur t ta te tes toi ton tu un une vos votre vous y à étaient étais était étant étiez étions été étée étées étés êtes".split(" ")),e.Pipeline.registerFunction(e.fr.stopWordFilter,"stopWordFilter-fr")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.hu.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.hu.min.js deleted file mode 100644 index ed9d909f7..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.hu.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Lunr languages, `Hungarian` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ - -!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hu=function(){this.pipeline.reset(),this.pipeline.add(e.hu.trimmer,e.hu.stopWordFilter,e.hu.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hu.stemmer))},e.hu.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.hu.trimmer=e.trimmerSupport.generateTrimmer(e.hu.wordCharacters),e.Pipeline.registerFunction(e.hu.trimmer,"trimmer-hu"),e.hu.stemmer=function(){var n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,i=new function(){function e(){var e,n=L.cursor;if(d=L.limit,L.in_grouping(W,97,252))for(;;){if(e=L.cursor,L.out_grouping(W,97,252))return L.cursor=e,L.find_among(g,8)||(L.cursor=e,e=L.limit)return void(d=e);L.cursor++}if(L.cursor=n,L.out_grouping(W,97,252)){for(;!L.in_grouping(W,97,252);){if(L.cursor>=L.limit)return;L.cursor++}d=L.cursor}}function i(){return d<=L.cursor}function a(){var e;if(L.ket=L.cursor,(e=L.find_among_b(h,2))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("a");break;case 2:L.slice_from("e")}}function t(){var e=L.limit-L.cursor;return!!L.find_among_b(p,23)&&(L.cursor=L.limit-e,!0)}function s(){if(L.cursor>L.limit_backward){L.cursor--,L.ket=L.cursor;var e=L.cursor-1;L.limit_backward<=e&&e<=L.limit&&(L.cursor=e,L.bra=e,L.slice_del())}}function c(){var e;if(L.ket=L.cursor,(e=L.find_among_b(_,2))&&(L.bra=L.cursor,i())){if((1==e||2==e)&&!t())return;L.slice_del(),s()}}function o(){L.ket=L.cursor,L.find_among_b(v,44)&&(L.bra=L.cursor,i()&&(L.slice_del(),a()))}function w(){var e;if(L.ket=L.cursor,(e=L.find_among_b(z,3))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("e");break;case 2:case 3:L.slice_from("a")}}function l(){var e;if(L.ket=L.cursor,(e=L.find_among_b(y,6))&&(L.bra=L.cursor,i()))switch(e){case 1:case 2:L.slice_del();break;case 3:L.slice_from("a");break;case 4:L.slice_from("e")}}function u(){var e;if(L.ket=L.cursor,(e=L.find_among_b(j,2))&&(L.bra=L.cursor,i())){if((1==e||2==e)&&!t())return;L.slice_del(),s()}}function m(){var e;if(L.ket=L.cursor,(e=L.find_among_b(C,7))&&(L.bra=L.cursor,i()))switch(e){case 1:L.slice_from("a");break;case 2:L.slice_from("e");break;case 3:case 4:case 5:case 6:case 7:L.slice_del()}}function k(){var e;if(L.ket=L.cursor,(e=L.find_among_b(P,12))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 7:case 9:L.slice_del();break;case 2:case 5:case 8:L.slice_from("e");break;case 3:case 6:L.slice_from("a")}}function f(){var e;if(L.ket=L.cursor,(e=L.find_among_b(F,31))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 7:case 8:case 9:case 12:case 13:case 16:case 17:case 18:L.slice_del();break;case 2:case 5:case 10:case 14:case 19:L.slice_from("a");break;case 3:case 6:case 11:case 15:case 20:L.slice_from("e")}}function b(){var e;if(L.ket=L.cursor,(e=L.find_among_b(S,42))&&(L.bra=L.cursor,i()))switch(e){case 1:case 4:case 5:case 6:case 9:case 10:case 11:case 14:case 15:case 16:case 17:case 20:case 21:case 24:case 25:case 26:case 29:L.slice_del();break;case 2:case 7:case 12:case 18:case 22:case 27:L.slice_from("a");break;case 3:case 8:case 13:case 19:case 23:case 28:L.slice_from("e")}}var d,g=[new n("cs",-1,-1),new n("dzs",-1,-1),new n("gy",-1,-1),new n("ly",-1,-1),new n("ny",-1,-1),new n("sz",-1,-1),new n("ty",-1,-1),new n("zs",-1,-1)],h=[new n("á",-1,1),new n("é",-1,2)],p=[new n("bb",-1,-1),new n("cc",-1,-1),new n("dd",-1,-1),new n("ff",-1,-1),new n("gg",-1,-1),new n("jj",-1,-1),new n("kk",-1,-1),new n("ll",-1,-1),new n("mm",-1,-1),new n("nn",-1,-1),new n("pp",-1,-1),new n("rr",-1,-1),new n("ccs",-1,-1),new n("ss",-1,-1),new n("zzs",-1,-1),new n("tt",-1,-1),new n("vv",-1,-1),new n("ggy",-1,-1),new n("lly",-1,-1),new n("nny",-1,-1),new n("tty",-1,-1),new n("ssz",-1,-1),new n("zz",-1,-1)],_=[new n("al",-1,1),new n("el",-1,2)],v=[new n("ba",-1,-1),new n("ra",-1,-1),new n("be",-1,-1),new n("re",-1,-1),new n("ig",-1,-1),new n("nak",-1,-1),new n("nek",-1,-1),new n("val",-1,-1),new n("vel",-1,-1),new n("ul",-1,-1),new n("nál",-1,-1),new n("nél",-1,-1),new n("ból",-1,-1),new n("ról",-1,-1),new n("tól",-1,-1),new n("bõl",-1,-1),new n("rõl",-1,-1),new n("tõl",-1,-1),new n("ül",-1,-1),new n("n",-1,-1),new n("an",19,-1),new n("ban",20,-1),new n("en",19,-1),new n("ben",22,-1),new n("képpen",22,-1),new n("on",19,-1),new n("ön",19,-1),new n("képp",-1,-1),new n("kor",-1,-1),new n("t",-1,-1),new n("at",29,-1),new n("et",29,-1),new n("ként",29,-1),new n("anként",32,-1),new n("enként",32,-1),new n("onként",32,-1),new n("ot",29,-1),new n("ért",29,-1),new n("öt",29,-1),new n("hez",-1,-1),new n("hoz",-1,-1),new n("höz",-1,-1),new n("vá",-1,-1),new n("vé",-1,-1)],z=[new n("án",-1,2),new n("én",-1,1),new n("ánként",-1,3)],y=[new n("stul",-1,2),new n("astul",0,1),new n("ástul",0,3),new n("stül",-1,2),new n("estül",3,1),new n("éstül",3,4)],j=[new n("á",-1,1),new n("é",-1,2)],C=[new n("k",-1,7),new n("ak",0,4),new n("ek",0,6),new n("ok",0,5),new n("ák",0,1),new n("ék",0,2),new n("ök",0,3)],P=[new n("éi",-1,7),new n("áéi",0,6),new n("ééi",0,5),new n("é",-1,9),new n("ké",3,4),new n("aké",4,1),new n("eké",4,1),new n("oké",4,1),new n("áké",4,3),new n("éké",4,2),new n("öké",4,1),new n("éé",3,8)],F=[new n("a",-1,18),new n("ja",0,17),new n("d",-1,16),new n("ad",2,13),new n("ed",2,13),new n("od",2,13),new n("ád",2,14),new n("éd",2,15),new n("öd",2,13),new n("e",-1,18),new n("je",9,17),new n("nk",-1,4),new n("unk",11,1),new n("ánk",11,2),new n("énk",11,3),new n("ünk",11,1),new n("uk",-1,8),new n("juk",16,7),new n("ájuk",17,5),new n("ük",-1,8),new n("jük",19,7),new n("éjük",20,6),new n("m",-1,12),new n("am",22,9),new n("em",22,9),new n("om",22,9),new n("ám",22,10),new n("ém",22,11),new n("o",-1,18),new n("á",-1,19),new n("é",-1,20)],S=[new n("id",-1,10),new n("aid",0,9),new n("jaid",1,6),new n("eid",0,9),new n("jeid",3,6),new n("áid",0,7),new n("éid",0,8),new n("i",-1,15),new n("ai",7,14),new n("jai",8,11),new n("ei",7,14),new n("jei",10,11),new n("ái",7,12),new n("éi",7,13),new n("itek",-1,24),new n("eitek",14,21),new n("jeitek",15,20),new n("éitek",14,23),new n("ik",-1,29),new n("aik",18,26),new n("jaik",19,25),new n("eik",18,26),new n("jeik",21,25),new n("áik",18,27),new n("éik",18,28),new n("ink",-1,20),new n("aink",25,17),new n("jaink",26,16),new n("eink",25,17),new n("jeink",28,16),new n("áink",25,18),new n("éink",25,19),new n("aitok",-1,21),new n("jaitok",32,20),new n("áitok",-1,22),new n("im",-1,5),new n("aim",35,4),new n("jaim",36,1),new n("eim",35,4),new n("jeim",38,1),new n("áim",35,2),new n("éim",35,3)],W=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,1,17,52,14],L=new r;this.setCurrent=function(e){L.setCurrent(e)},this.getCurrent=function(){return L.getCurrent()},this.stem=function(){var n=L.cursor;return e(),L.limit_backward=n,L.cursor=L.limit,c(),L.cursor=L.limit,o(),L.cursor=L.limit,w(),L.cursor=L.limit,l(),L.cursor=L.limit,u(),L.cursor=L.limit,k(),L.cursor=L.limit,f(),L.cursor=L.limit,b(),L.cursor=L.limit,m(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.hu.stemmer,"stemmer-hu"),e.hu.stopWordFilter=e.generateStopWordFilter("a abban ahhoz ahogy ahol aki akik akkor alatt amely amelyek amelyekben amelyeket amelyet amelynek ami amikor amit amolyan amíg annak arra arról az azok azon azonban azt aztán azután azzal azért be belül benne bár cikk cikkek cikkeket csak de e ebben eddig egy egyes egyetlen egyik egyre egyéb egész ehhez ekkor el ellen elsõ elég elõ elõször elõtt emilyen ennek erre ez ezek ezen ezt ezzel ezért fel felé hanem hiszen hogy hogyan igen ill ill. illetve ilyen ilyenkor ismét ison itt jobban jó jól kell kellett keressünk keresztül ki kívül között közül legalább legyen lehet lehetett lenne lenni lesz lett maga magát majd majd meg mellett mely melyek mert mi mikor milyen minden mindenki mindent mindig mint mintha mit mivel miért most már más másik még míg nagy nagyobb nagyon ne nekem neki nem nincs néha néhány nélkül olyan ott pedig persze rá s saját sem semmi sok sokat sokkal szemben szerint szinte számára talán tehát teljes tovább továbbá több ugyanis utolsó után utána vagy vagyis vagyok valaki valami valamint való van vannak vele vissza viszont volna volt voltak voltam voltunk által általában át én éppen és így õ õk õket össze úgy új újabb újra".split(" ")),e.Pipeline.registerFunction(e.hu.stopWordFilter,"stopWordFilter-hu")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.it.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.it.min.js deleted file mode 100644 index 344b6a3c0..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.it.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Lunr languages, `Italian` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ - -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.it=function(){this.pipeline.reset(),this.pipeline.add(e.it.trimmer,e.it.stopWordFilter,e.it.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.it.stemmer))},e.it.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.it.trimmer=e.trimmerSupport.generateTrimmer(e.it.wordCharacters),e.Pipeline.registerFunction(e.it.trimmer,"trimmer-it"),e.it.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(e,r,n){return!(!x.eq_s(1,e)||(x.ket=x.cursor,!x.in_grouping(L,97,249)))&&(x.slice_from(r),x.cursor=n,!0)}function i(){for(var r,n,i,o,t=x.cursor;;){if(x.bra=x.cursor,r=x.find_among(h,7))switch(x.ket=x.cursor,r){case 1:x.slice_from("à");continue;case 2:x.slice_from("è");continue;case 3:x.slice_from("ì");continue;case 4:x.slice_from("ò");continue;case 5:x.slice_from("ù");continue;case 6:x.slice_from("qU");continue;case 7:if(x.cursor>=x.limit)break;x.cursor++;continue}break}for(x.cursor=t;;)for(n=x.cursor;;){if(i=x.cursor,x.in_grouping(L,97,249)){if(x.bra=x.cursor,o=x.cursor,e("u","U",i))break;if(x.cursor=o,e("i","I",i))break}if(x.cursor=i,x.cursor>=x.limit)return void(x.cursor=n);x.cursor++}}function o(e){if(x.cursor=e,!x.in_grouping(L,97,249))return!1;for(;!x.out_grouping(L,97,249);){if(x.cursor>=x.limit)return!1;x.cursor++}return!0}function t(){if(x.in_grouping(L,97,249)){var e=x.cursor;if(x.out_grouping(L,97,249)){for(;!x.in_grouping(L,97,249);){if(x.cursor>=x.limit)return o(e);x.cursor++}return!0}return o(e)}return!1}function s(){var e,r=x.cursor;if(!t()){if(x.cursor=r,!x.out_grouping(L,97,249))return;if(e=x.cursor,x.out_grouping(L,97,249)){for(;!x.in_grouping(L,97,249);){if(x.cursor>=x.limit)return x.cursor=e,void(x.in_grouping(L,97,249)&&x.cursor=x.limit)return;x.cursor++}k=x.cursor}function a(){for(;!x.in_grouping(L,97,249);){if(x.cursor>=x.limit)return!1;x.cursor++}for(;!x.out_grouping(L,97,249);){if(x.cursor>=x.limit)return!1;x.cursor++}return!0}function u(){var e=x.cursor;k=x.limit,p=k,g=k,s(),x.cursor=e,a()&&(p=x.cursor,a()&&(g=x.cursor))}function c(){for(var e;;){if(x.bra=x.cursor,!(e=x.find_among(q,3)))break;switch(x.ket=x.cursor,e){case 1:x.slice_from("i");break;case 2:x.slice_from("u");break;case 3:if(x.cursor>=x.limit)return;x.cursor++}}}function w(){return k<=x.cursor}function l(){return p<=x.cursor}function m(){return g<=x.cursor}function f(){var e;if(x.ket=x.cursor,x.find_among_b(C,37)&&(x.bra=x.cursor,(e=x.find_among_b(z,5))&&w()))switch(e){case 1:x.slice_del();break;case 2:x.slice_from("e")}}function v(){var e;if(x.ket=x.cursor,!(e=x.find_among_b(S,51)))return!1;switch(x.bra=x.cursor,e){case 1:if(!m())return!1;x.slice_del();break;case 2:if(!m())return!1;x.slice_del(),x.ket=x.cursor,x.eq_s_b(2,"ic")&&(x.bra=x.cursor,m()&&x.slice_del());break;case 3:if(!m())return!1;x.slice_from("log");break;case 4:if(!m())return!1;x.slice_from("u");break;case 5:if(!m())return!1;x.slice_from("ente");break;case 6:if(!w())return!1;x.slice_del();break;case 7:if(!l())return!1;x.slice_del(),x.ket=x.cursor,e=x.find_among_b(P,4),e&&(x.bra=x.cursor,m()&&(x.slice_del(),1==e&&(x.ket=x.cursor,x.eq_s_b(2,"at")&&(x.bra=x.cursor,m()&&x.slice_del()))));break;case 8:if(!m())return!1;x.slice_del(),x.ket=x.cursor,e=x.find_among_b(F,3),e&&(x.bra=x.cursor,1==e&&m()&&x.slice_del());break;case 9:if(!m())return!1;x.slice_del(),x.ket=x.cursor,x.eq_s_b(2,"at")&&(x.bra=x.cursor,m()&&(x.slice_del(),x.ket=x.cursor,x.eq_s_b(2,"ic")&&(x.bra=x.cursor,m()&&x.slice_del())))}return!0}function b(){var e,r;x.cursor>=k&&(r=x.limit_backward,x.limit_backward=k,x.ket=x.cursor,e=x.find_among_b(W,87),e&&(x.bra=x.cursor,1==e&&x.slice_del()),x.limit_backward=r)}function d(){var e=x.limit-x.cursor;if(x.ket=x.cursor,x.in_grouping_b(y,97,242)&&(x.bra=x.cursor,w()&&(x.slice_del(),x.ket=x.cursor,x.eq_s_b(1,"i")&&(x.bra=x.cursor,w()))))return void x.slice_del();x.cursor=x.limit-e}function _(){d(),x.ket=x.cursor,x.eq_s_b(1,"h")&&(x.bra=x.cursor,x.in_grouping_b(U,99,103)&&w()&&x.slice_del())}var g,p,k,h=[new r("",-1,7),new r("qu",0,6),new r("á",0,1),new r("é",0,2),new r("í",0,3),new r("ó",0,4),new r("ú",0,5)],q=[new r("",-1,3),new r("I",0,1),new r("U",0,2)],C=[new r("la",-1,-1),new r("cela",0,-1),new r("gliela",0,-1),new r("mela",0,-1),new r("tela",0,-1),new r("vela",0,-1),new r("le",-1,-1),new r("cele",6,-1),new r("gliele",6,-1),new r("mele",6,-1),new r("tele",6,-1),new r("vele",6,-1),new r("ne",-1,-1),new r("cene",12,-1),new r("gliene",12,-1),new r("mene",12,-1),new r("sene",12,-1),new r("tene",12,-1),new r("vene",12,-1),new r("ci",-1,-1),new r("li",-1,-1),new r("celi",20,-1),new r("glieli",20,-1),new r("meli",20,-1),new r("teli",20,-1),new r("veli",20,-1),new r("gli",20,-1),new r("mi",-1,-1),new r("si",-1,-1),new r("ti",-1,-1),new r("vi",-1,-1),new r("lo",-1,-1),new r("celo",31,-1),new r("glielo",31,-1),new r("melo",31,-1),new r("telo",31,-1),new r("velo",31,-1)],z=[new r("ando",-1,1),new r("endo",-1,1),new r("ar",-1,2),new r("er",-1,2),new r("ir",-1,2)],P=[new r("ic",-1,-1),new r("abil",-1,-1),new r("os",-1,-1),new r("iv",-1,1)],F=[new r("ic",-1,1),new r("abil",-1,1),new r("iv",-1,1)],S=[new r("ica",-1,1),new r("logia",-1,3),new r("osa",-1,1),new r("ista",-1,1),new r("iva",-1,9),new r("anza",-1,1),new r("enza",-1,5),new r("ice",-1,1),new r("atrice",7,1),new r("iche",-1,1),new r("logie",-1,3),new r("abile",-1,1),new r("ibile",-1,1),new r("usione",-1,4),new r("azione",-1,2),new r("uzione",-1,4),new r("atore",-1,2),new r("ose",-1,1),new r("ante",-1,1),new r("mente",-1,1),new r("amente",19,7),new r("iste",-1,1),new r("ive",-1,9),new r("anze",-1,1),new r("enze",-1,5),new r("ici",-1,1),new r("atrici",25,1),new r("ichi",-1,1),new r("abili",-1,1),new r("ibili",-1,1),new r("ismi",-1,1),new r("usioni",-1,4),new r("azioni",-1,2),new r("uzioni",-1,4),new r("atori",-1,2),new r("osi",-1,1),new r("anti",-1,1),new r("amenti",-1,6),new r("imenti",-1,6),new r("isti",-1,1),new r("ivi",-1,9),new r("ico",-1,1),new r("ismo",-1,1),new r("oso",-1,1),new r("amento",-1,6),new r("imento",-1,6),new r("ivo",-1,9),new r("ità",-1,8),new r("istà",-1,1),new r("istè",-1,1),new r("istì",-1,1)],W=[new r("isca",-1,1),new r("enda",-1,1),new r("ata",-1,1),new r("ita",-1,1),new r("uta",-1,1),new r("ava",-1,1),new r("eva",-1,1),new r("iva",-1,1),new r("erebbe",-1,1),new r("irebbe",-1,1),new r("isce",-1,1),new r("ende",-1,1),new r("are",-1,1),new r("ere",-1,1),new r("ire",-1,1),new r("asse",-1,1),new r("ate",-1,1),new r("avate",16,1),new r("evate",16,1),new r("ivate",16,1),new r("ete",-1,1),new r("erete",20,1),new r("irete",20,1),new r("ite",-1,1),new r("ereste",-1,1),new r("ireste",-1,1),new r("ute",-1,1),new r("erai",-1,1),new r("irai",-1,1),new r("isci",-1,1),new r("endi",-1,1),new r("erei",-1,1),new r("irei",-1,1),new r("assi",-1,1),new r("ati",-1,1),new r("iti",-1,1),new r("eresti",-1,1),new r("iresti",-1,1),new r("uti",-1,1),new r("avi",-1,1),new r("evi",-1,1),new r("ivi",-1,1),new r("isco",-1,1),new r("ando",-1,1),new r("endo",-1,1),new r("Yamo",-1,1),new r("iamo",-1,1),new r("avamo",-1,1),new r("evamo",-1,1),new r("ivamo",-1,1),new r("eremo",-1,1),new r("iremo",-1,1),new r("assimo",-1,1),new r("ammo",-1,1),new r("emmo",-1,1),new r("eremmo",54,1),new r("iremmo",54,1),new r("immo",-1,1),new r("ano",-1,1),new r("iscano",58,1),new r("avano",58,1),new r("evano",58,1),new r("ivano",58,1),new r("eranno",-1,1),new r("iranno",-1,1),new r("ono",-1,1),new r("iscono",65,1),new r("arono",65,1),new r("erono",65,1),new r("irono",65,1),new r("erebbero",-1,1),new r("irebbero",-1,1),new r("assero",-1,1),new r("essero",-1,1),new r("issero",-1,1),new r("ato",-1,1),new r("ito",-1,1),new r("uto",-1,1),new r("avo",-1,1),new r("evo",-1,1),new r("ivo",-1,1),new r("ar",-1,1),new r("ir",-1,1),new r("erà",-1,1),new r("irà",-1,1),new r("erò",-1,1),new r("irò",-1,1)],L=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2,1],y=[17,65,0,0,0,0,0,0,0,0,0,0,0,0,0,128,128,8,2],U=[17],x=new n;this.setCurrent=function(e){x.setCurrent(e)},this.getCurrent=function(){return x.getCurrent()},this.stem=function(){var e=x.cursor;return i(),x.cursor=e,u(),x.limit_backward=e,x.cursor=x.limit,f(),x.cursor=x.limit,v()||(x.cursor=x.limit,b()),x.cursor=x.limit,_(),x.cursor=x.limit_backward,c(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.it.stemmer,"stemmer-it"),e.it.stopWordFilter=e.generateStopWordFilter("a abbia abbiamo abbiano abbiate ad agl agli ai al all alla alle allo anche avemmo avendo avesse avessero avessi avessimo aveste avesti avete aveva avevamo avevano avevate avevi avevo avrai avranno avrebbe avrebbero avrei avremmo avremo avreste avresti avrete avrà avrò avuta avute avuti avuto c che chi ci coi col come con contro cui da dagl dagli dai dal dall dalla dalle dallo degl degli dei del dell della delle dello di dov dove e ebbe ebbero ebbi ed era erano eravamo eravate eri ero essendo faccia facciamo facciano facciate faccio facemmo facendo facesse facessero facessi facessimo faceste facesti faceva facevamo facevano facevate facevi facevo fai fanno farai faranno farebbe farebbero farei faremmo faremo fareste faresti farete farà farò fece fecero feci fosse fossero fossi fossimo foste fosti fu fui fummo furono gli ha hai hanno ho i il in io l la le lei li lo loro lui ma mi mia mie miei mio ne negl negli nei nel nell nella nelle nello noi non nostra nostre nostri nostro o per perché più quale quanta quante quanti quanto quella quelle quelli quello questa queste questi questo sarai saranno sarebbe sarebbero sarei saremmo saremo sareste saresti sarete sarà sarò se sei si sia siamo siano siate siete sono sta stai stando stanno starai staranno starebbe starebbero starei staremmo staremo stareste staresti starete starà starò stava stavamo stavano stavate stavi stavo stemmo stesse stessero stessi stessimo steste stesti stette stettero stetti stia stiamo stiano stiate sto su sua sue sugl sugli sui sul sull sulla sulle sullo suo suoi ti tra tu tua tue tuo tuoi tutti tutto un una uno vi voi vostra vostre vostri vostro è".split(" ")),e.Pipeline.registerFunction(e.it.stopWordFilter,"stopWordFilter-it")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.ja.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.ja.min.js deleted file mode 100644 index 5f254ebe9..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.ja.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.ja=function(){this.pipeline.reset(),this.pipeline.add(e.ja.trimmer,e.ja.stopWordFilter,e.ja.stemmer),r?this.tokenizer=e.ja.tokenizer:(e.tokenizer&&(e.tokenizer=e.ja.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.ja.tokenizer))};var t=new e.TinySegmenter;e.ja.tokenizer=function(i){var n,o,s,p,a,u,m,l,c,f;if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(t){return r?new e.Token(t.toLowerCase()):t.toLowerCase()});for(o=i.toString().toLowerCase().replace(/^\s+/,""),n=o.length-1;n>=0;n--)if(/\S/.test(o.charAt(n))){o=o.substring(0,n+1);break}for(a=[],s=o.length,c=0,l=0;c<=s;c++)if(u=o.charAt(c),m=c-l,u.match(/\s/)||c==s){if(m>0)for(p=t.segment(o.slice(l,c)).filter(function(e){return!!e}),f=l,n=0;n=C.limit)break;C.cursor++;continue}break}for(C.cursor=o,C.bra=o,C.eq_s(1,"y")?(C.ket=C.cursor,C.slice_from("Y")):C.cursor=o;;)if(e=C.cursor,C.in_grouping(q,97,232)){if(i=C.cursor,C.bra=i,C.eq_s(1,"i"))C.ket=C.cursor,C.in_grouping(q,97,232)&&(C.slice_from("I"),C.cursor=e);else if(C.cursor=i,C.eq_s(1,"y"))C.ket=C.cursor,C.slice_from("Y"),C.cursor=e;else if(n(e))break}else if(n(e))break}function n(r){return C.cursor=r,r>=C.limit||(C.cursor++,!1)}function o(){_=C.limit,d=_,t()||(_=C.cursor,_<3&&(_=3),t()||(d=C.cursor))}function t(){for(;!C.in_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}for(;!C.out_grouping(q,97,232);){if(C.cursor>=C.limit)return!0;C.cursor++}return!1}function s(){for(var r;;)if(C.bra=C.cursor,r=C.find_among(p,3))switch(C.ket=C.cursor,r){case 1:C.slice_from("y");break;case 2:C.slice_from("i");break;case 3:if(C.cursor>=C.limit)return;C.cursor++}}function u(){return _<=C.cursor}function c(){return d<=C.cursor}function a(){var r=C.limit-C.cursor;C.find_among_b(g,3)&&(C.cursor=C.limit-r,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del()))}function l(){var r;w=!1,C.ket=C.cursor,C.eq_s_b(1,"e")&&(C.bra=C.cursor,u()&&(r=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-r,C.slice_del(),w=!0,a())))}function m(){var r;u()&&(r=C.limit-C.cursor,C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-r,C.eq_s_b(3,"gem")||(C.cursor=C.limit-r,C.slice_del(),a())))}function f(){var r,e,i,n,o,t,s=C.limit-C.cursor;if(C.ket=C.cursor,r=C.find_among_b(h,5))switch(C.bra=C.cursor,r){case 1:u()&&C.slice_from("heid");break;case 2:m();break;case 3:u()&&C.out_grouping_b(j,97,232)&&C.slice_del()}if(C.cursor=C.limit-s,l(),C.cursor=C.limit-s,C.ket=C.cursor,C.eq_s_b(4,"heid")&&(C.bra=C.cursor,c()&&(e=C.limit-C.cursor,C.eq_s_b(1,"c")||(C.cursor=C.limit-e,C.slice_del(),C.ket=C.cursor,C.eq_s_b(2,"en")&&(C.bra=C.cursor,m())))),C.cursor=C.limit-s,C.ket=C.cursor,r=C.find_among_b(k,6))switch(C.bra=C.cursor,r){case 1:if(c()){if(C.slice_del(),i=C.limit-C.cursor,C.ket=C.cursor,C.eq_s_b(2,"ig")&&(C.bra=C.cursor,c()&&(n=C.limit-C.cursor,!C.eq_s_b(1,"e")))){C.cursor=C.limit-n,C.slice_del();break}C.cursor=C.limit-i,a()}break;case 2:c()&&(o=C.limit-C.cursor,C.eq_s_b(1,"e")||(C.cursor=C.limit-o,C.slice_del()));break;case 3:c()&&(C.slice_del(),l());break;case 4:c()&&C.slice_del();break;case 5:c()&&w&&C.slice_del()}C.cursor=C.limit-s,C.out_grouping_b(z,73,232)&&(t=C.limit-C.cursor,C.find_among_b(v,4)&&C.out_grouping_b(q,97,232)&&(C.cursor=C.limit-t,C.ket=C.cursor,C.cursor>C.limit_backward&&(C.cursor--,C.bra=C.cursor,C.slice_del())))}var d,_,w,b=[new e("",-1,6),new e("á",0,1),new e("ä",0,1),new e("é",0,2),new e("ë",0,2),new e("í",0,3),new e("ï",0,3),new e("ó",0,4),new e("ö",0,4),new e("ú",0,5),new e("ü",0,5)],p=[new e("",-1,3),new e("I",0,2),new e("Y",0,1)],g=[new e("dd",-1,-1),new e("kk",-1,-1),new e("tt",-1,-1)],h=[new e("ene",-1,2),new e("se",-1,3),new e("en",-1,2),new e("heden",2,1),new e("s",-1,3)],k=[new e("end",-1,1),new e("ig",-1,2),new e("ing",-1,1),new e("lijk",-1,3),new e("baar",-1,4),new e("bar",-1,5)],v=[new e("aa",-1,-1),new e("ee",-1,-1),new e("oo",-1,-1),new e("uu",-1,-1)],q=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],z=[1,0,0,17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],j=[17,67,16,1,0,0,0,0,0,0,0,0,0,0,0,0,128],C=new i;this.setCurrent=function(r){C.setCurrent(r)},this.getCurrent=function(){return C.getCurrent()},this.stem=function(){var e=C.cursor;return r(),C.cursor=e,o(),C.limit_backward=e,C.cursor=C.limit,f(),C.cursor=C.limit_backward,s(),!0}};return function(r){return"function"==typeof r.update?r.update(function(r){return n.setCurrent(r),n.stem(),n.getCurrent()}):(n.setCurrent(r),n.stem(),n.getCurrent())}}(),r.Pipeline.registerFunction(r.nl.stemmer,"stemmer-nl"),r.nl.stopWordFilter=r.generateStopWordFilter(" aan al alles als altijd andere ben bij daar dan dat de der deze die dit doch doen door dus een eens en er ge geen geweest haar had heb hebben heeft hem het hier hij hoe hun iemand iets ik in is ja je kan kon kunnen maar me meer men met mij mijn moet na naar niet niets nog nu of om omdat onder ons ook op over reeds te tegen toch toen tot u uit uw van veel voor want waren was wat werd wezen wie wil worden wordt zal ze zelf zich zij zijn zo zonder zou".split(" ")),r.Pipeline.registerFunction(r.nl.stopWordFilter,"stopWordFilter-nl")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.no.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.no.min.js deleted file mode 100644 index 92bc7e4e8..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.no.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Lunr languages, `Norwegian` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ - -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.no=function(){this.pipeline.reset(),this.pipeline.add(e.no.trimmer,e.no.stopWordFilter,e.no.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.no.stemmer))},e.no.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.no.trimmer=e.trimmerSupport.generateTrimmer(e.no.wordCharacters),e.Pipeline.registerFunction(e.no.trimmer,"trimmer-no"),e.no.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(){var e,r=w.cursor+3;if(a=w.limit,0<=r||r<=w.limit){for(s=r;;){if(e=w.cursor,w.in_grouping(d,97,248)){w.cursor=e;break}if(e>=w.limit)return;w.cursor=e+1}for(;!w.out_grouping(d,97,248);){if(w.cursor>=w.limit)return;w.cursor++}a=w.cursor,a=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(m,29),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:n=w.limit-w.cursor,w.in_grouping_b(c,98,122)?w.slice_del():(w.cursor=w.limit-n,w.eq_s_b(1,"k")&&w.out_grouping_b(d,97,248)&&w.slice_del());break;case 3:w.slice_from("er")}}function t(){var e,r=w.limit-w.cursor;w.cursor>=a&&(e=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,w.find_among_b(u,2)?(w.bra=w.cursor,w.limit_backward=e,w.cursor=w.limit-r,w.cursor>w.limit_backward&&(w.cursor--,w.bra=w.cursor,w.slice_del())):w.limit_backward=e)}function o(){var e,r;w.cursor>=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(l,11),e?(w.bra=w.cursor,w.limit_backward=r,1==e&&w.slice_del()):w.limit_backward=r)}var s,a,m=[new r("a",-1,1),new r("e",-1,1),new r("ede",1,1),new r("ande",1,1),new r("ende",1,1),new r("ane",1,1),new r("ene",1,1),new r("hetene",6,1),new r("erte",1,3),new r("en",-1,1),new r("heten",9,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",12,1),new r("s",-1,2),new r("as",14,1),new r("es",14,1),new r("edes",16,1),new r("endes",16,1),new r("enes",16,1),new r("hetenes",19,1),new r("ens",14,1),new r("hetens",21,1),new r("ers",14,1),new r("ets",14,1),new r("et",-1,1),new r("het",25,1),new r("ert",-1,3),new r("ast",-1,1)],u=[new r("dt",-1,-1),new r("vt",-1,-1)],l=[new r("leg",-1,1),new r("eleg",0,1),new r("ig",-1,1),new r("eig",2,1),new r("lig",2,1),new r("elig",4,1),new r("els",-1,1),new r("lov",-1,1),new r("elov",7,1),new r("slov",7,1),new r("hetslov",9,1)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],c=[119,125,149,1],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,i(),w.cursor=w.limit,t(),w.cursor=w.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.no.stemmer,"stemmer-no"),e.no.stopWordFilter=e.generateStopWordFilter("alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å".split(" ")),e.Pipeline.registerFunction(e.no.stopWordFilter,"stopWordFilter-no")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.pt.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.pt.min.js deleted file mode 100644 index 6c16996d6..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.pt.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Lunr languages, `Portuguese` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ - -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.pt=function(){this.pipeline.reset(),this.pipeline.add(e.pt.trimmer,e.pt.stopWordFilter,e.pt.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.pt.stemmer))},e.pt.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.pt.trimmer=e.trimmerSupport.generateTrimmer(e.pt.wordCharacters),e.Pipeline.registerFunction(e.pt.trimmer,"trimmer-pt"),e.pt.stemmer=function(){var r=e.stemmerSupport.Among,s=e.stemmerSupport.SnowballProgram,n=new function(){function e(){for(var e;;){if(z.bra=z.cursor,e=z.find_among(k,3))switch(z.ket=z.cursor,e){case 1:z.slice_from("a~");continue;case 2:z.slice_from("o~");continue;case 3:if(z.cursor>=z.limit)break;z.cursor++;continue}break}}function n(){if(z.out_grouping(y,97,250)){for(;!z.in_grouping(y,97,250);){if(z.cursor>=z.limit)return!0;z.cursor++}return!1}return!0}function i(){if(z.in_grouping(y,97,250))for(;!z.out_grouping(y,97,250);){if(z.cursor>=z.limit)return!1;z.cursor++}return g=z.cursor,!0}function o(){var e,r,s=z.cursor;if(z.in_grouping(y,97,250))if(e=z.cursor,n()){if(z.cursor=e,i())return}else g=z.cursor;if(z.cursor=s,z.out_grouping(y,97,250)){if(r=z.cursor,n()){if(z.cursor=r,!z.in_grouping(y,97,250)||z.cursor>=z.limit)return;z.cursor++}g=z.cursor}}function t(){for(;!z.in_grouping(y,97,250);){if(z.cursor>=z.limit)return!1;z.cursor++}for(;!z.out_grouping(y,97,250);){if(z.cursor>=z.limit)return!1;z.cursor++}return!0}function a(){var e=z.cursor;g=z.limit,b=g,h=g,o(),z.cursor=e,t()&&(b=z.cursor,t()&&(h=z.cursor))}function u(){for(var e;;){if(z.bra=z.cursor,e=z.find_among(q,3))switch(z.ket=z.cursor,e){case 1:z.slice_from("ã");continue;case 2:z.slice_from("õ");continue;case 3:if(z.cursor>=z.limit)break;z.cursor++;continue}break}}function w(){return g<=z.cursor}function m(){return b<=z.cursor}function c(){return h<=z.cursor}function l(){var e;if(z.ket=z.cursor,!(e=z.find_among_b(F,45)))return!1;switch(z.bra=z.cursor,e){case 1:if(!c())return!1;z.slice_del();break;case 2:if(!c())return!1;z.slice_from("log");break;case 3:if(!c())return!1;z.slice_from("u");break;case 4:if(!c())return!1;z.slice_from("ente");break;case 5:if(!m())return!1;z.slice_del(),z.ket=z.cursor,e=z.find_among_b(j,4),e&&(z.bra=z.cursor,c()&&(z.slice_del(),1==e&&(z.ket=z.cursor,z.eq_s_b(2,"at")&&(z.bra=z.cursor,c()&&z.slice_del()))));break;case 6:if(!c())return!1;z.slice_del(),z.ket=z.cursor,e=z.find_among_b(C,3),e&&(z.bra=z.cursor,1==e&&c()&&z.slice_del());break;case 7:if(!c())return!1;z.slice_del(),z.ket=z.cursor,e=z.find_among_b(P,3),e&&(z.bra=z.cursor,1==e&&c()&&z.slice_del());break;case 8:if(!c())return!1;z.slice_del(),z.ket=z.cursor,z.eq_s_b(2,"at")&&(z.bra=z.cursor,c()&&z.slice_del());break;case 9:if(!w()||!z.eq_s_b(1,"e"))return!1;z.slice_from("ir")}return!0}function f(){var e,r;if(z.cursor>=g){if(r=z.limit_backward,z.limit_backward=g,z.ket=z.cursor,e=z.find_among_b(S,120))return z.bra=z.cursor,1==e&&z.slice_del(),z.limit_backward=r,!0;z.limit_backward=r}return!1}function d(){var e;z.ket=z.cursor,(e=z.find_among_b(W,7))&&(z.bra=z.cursor,1==e&&w()&&z.slice_del())}function v(e,r){if(z.eq_s_b(1,e)){z.bra=z.cursor;var s=z.limit-z.cursor;if(z.eq_s_b(1,r))return z.cursor=z.limit-s,w()&&z.slice_del(),!1}return!0}function p(){var e;if(z.ket=z.cursor,e=z.find_among_b(L,4))switch(z.bra=z.cursor,e){case 1:w()&&(z.slice_del(),z.ket=z.cursor,z.limit-z.cursor,v("u","g")&&v("i","c"));break;case 2:z.slice_from("c")}}function _(){if(!l()&&(z.cursor=z.limit,!f()))return z.cursor=z.limit,void d();z.cursor=z.limit,z.ket=z.cursor,z.eq_s_b(1,"i")&&(z.bra=z.cursor,z.eq_s_b(1,"c")&&(z.cursor=z.limit,w()&&z.slice_del()))}var h,b,g,k=[new r("",-1,3),new r("ã",0,1),new r("õ",0,2)],q=[new r("",-1,3),new r("a~",0,1),new r("o~",0,2)],j=[new r("ic",-1,-1),new r("ad",-1,-1),new r("os",-1,-1),new r("iv",-1,1)],C=[new r("ante",-1,1),new r("avel",-1,1),new r("ível",-1,1)],P=[new r("ic",-1,1),new r("abil",-1,1),new r("iv",-1,1)],F=[new r("ica",-1,1),new r("ância",-1,1),new r("ência",-1,4),new r("ira",-1,9),new r("adora",-1,1),new r("osa",-1,1),new r("ista",-1,1),new r("iva",-1,8),new r("eza",-1,1),new r("logía",-1,2),new r("idade",-1,7),new r("ante",-1,1),new r("mente",-1,6),new r("amente",12,5),new r("ável",-1,1),new r("ível",-1,1),new r("ución",-1,3),new r("ico",-1,1),new r("ismo",-1,1),new r("oso",-1,1),new r("amento",-1,1),new r("imento",-1,1),new r("ivo",-1,8),new r("aça~o",-1,1),new r("ador",-1,1),new r("icas",-1,1),new r("ências",-1,4),new r("iras",-1,9),new r("adoras",-1,1),new r("osas",-1,1),new r("istas",-1,1),new r("ivas",-1,8),new r("ezas",-1,1),new r("logías",-1,2),new r("idades",-1,7),new r("uciones",-1,3),new r("adores",-1,1),new r("antes",-1,1),new r("aço~es",-1,1),new r("icos",-1,1),new r("ismos",-1,1),new r("osos",-1,1),new r("amentos",-1,1),new r("imentos",-1,1),new r("ivos",-1,8)],S=[new r("ada",-1,1),new r("ida",-1,1),new r("ia",-1,1),new r("aria",2,1),new r("eria",2,1),new r("iria",2,1),new r("ara",-1,1),new r("era",-1,1),new r("ira",-1,1),new r("ava",-1,1),new r("asse",-1,1),new r("esse",-1,1),new r("isse",-1,1),new r("aste",-1,1),new r("este",-1,1),new r("iste",-1,1),new r("ei",-1,1),new r("arei",16,1),new r("erei",16,1),new r("irei",16,1),new r("am",-1,1),new r("iam",20,1),new r("ariam",21,1),new r("eriam",21,1),new r("iriam",21,1),new r("aram",20,1),new r("eram",20,1),new r("iram",20,1),new r("avam",20,1),new r("em",-1,1),new r("arem",29,1),new r("erem",29,1),new r("irem",29,1),new r("assem",29,1),new r("essem",29,1),new r("issem",29,1),new r("ado",-1,1),new r("ido",-1,1),new r("ando",-1,1),new r("endo",-1,1),new r("indo",-1,1),new r("ara~o",-1,1),new r("era~o",-1,1),new r("ira~o",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("ir",-1,1),new r("as",-1,1),new r("adas",47,1),new r("idas",47,1),new r("ias",47,1),new r("arias",50,1),new r("erias",50,1),new r("irias",50,1),new r("aras",47,1),new r("eras",47,1),new r("iras",47,1),new r("avas",47,1),new r("es",-1,1),new r("ardes",58,1),new r("erdes",58,1),new r("irdes",58,1),new r("ares",58,1),new r("eres",58,1),new r("ires",58,1),new r("asses",58,1),new r("esses",58,1),new r("isses",58,1),new r("astes",58,1),new r("estes",58,1),new r("istes",58,1),new r("is",-1,1),new r("ais",71,1),new r("eis",71,1),new r("areis",73,1),new r("ereis",73,1),new r("ireis",73,1),new r("áreis",73,1),new r("éreis",73,1),new r("íreis",73,1),new r("ásseis",73,1),new r("ésseis",73,1),new r("ísseis",73,1),new r("áveis",73,1),new r("íeis",73,1),new r("aríeis",84,1),new r("eríeis",84,1),new r("iríeis",84,1),new r("ados",-1,1),new r("idos",-1,1),new r("amos",-1,1),new r("áramos",90,1),new r("éramos",90,1),new r("íramos",90,1),new r("ávamos",90,1),new r("íamos",90,1),new r("aríamos",95,1),new r("eríamos",95,1),new r("iríamos",95,1),new r("emos",-1,1),new r("aremos",99,1),new r("eremos",99,1),new r("iremos",99,1),new r("ássemos",99,1),new r("êssemos",99,1),new r("íssemos",99,1),new r("imos",-1,1),new r("armos",-1,1),new r("ermos",-1,1),new r("irmos",-1,1),new r("ámos",-1,1),new r("arás",-1,1),new r("erás",-1,1),new r("irás",-1,1),new r("eu",-1,1),new r("iu",-1,1),new r("ou",-1,1),new r("ará",-1,1),new r("erá",-1,1),new r("irá",-1,1)],W=[new r("a",-1,1),new r("i",-1,1),new r("o",-1,1),new r("os",-1,1),new r("á",-1,1),new r("í",-1,1),new r("ó",-1,1)],L=[new r("e",-1,1),new r("ç",-1,2),new r("é",-1,1),new r("ê",-1,1)],y=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,3,19,12,2],z=new s;this.setCurrent=function(e){z.setCurrent(e)},this.getCurrent=function(){return z.getCurrent()},this.stem=function(){var r=z.cursor;return e(),z.cursor=r,a(),z.limit_backward=r,z.cursor=z.limit,_(),z.cursor=z.limit,p(),z.cursor=z.limit_backward,u(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.pt.stemmer,"stemmer-pt"),e.pt.stopWordFilter=e.generateStopWordFilter("a ao aos aquela aquelas aquele aqueles aquilo as até com como da das de dela delas dele deles depois do dos e ela elas ele eles em entre era eram essa essas esse esses esta estamos estas estava estavam este esteja estejam estejamos estes esteve estive estivemos estiver estivera estiveram estiverem estivermos estivesse estivessem estivéramos estivéssemos estou está estávamos estão eu foi fomos for fora foram forem formos fosse fossem fui fôramos fôssemos haja hajam hajamos havemos hei houve houvemos houver houvera houveram houverei houverem houveremos houveria houveriam houvermos houverá houverão houveríamos houvesse houvessem houvéramos houvéssemos há hão isso isto já lhe lhes mais mas me mesmo meu meus minha minhas muito na nas nem no nos nossa nossas nosso nossos num numa não nós o os ou para pela pelas pelo pelos por qual quando que quem se seja sejam sejamos sem serei seremos seria seriam será serão seríamos seu seus somos sou sua suas são só também te tem temos tenha tenham tenhamos tenho terei teremos teria teriam terá terão teríamos teu teus teve tinha tinham tive tivemos tiver tivera tiveram tiverem tivermos tivesse tivessem tivéramos tivéssemos tu tua tuas tém tínhamos um uma você vocês vos à às éramos".split(" ")),e.Pipeline.registerFunction(e.pt.stopWordFilter,"stopWordFilter-pt")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.ro.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.ro.min.js deleted file mode 100644 index 727714018..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.ro.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Lunr languages, `Romanian` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ - -!function(e,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ro=function(){this.pipeline.reset(),this.pipeline.add(e.ro.trimmer,e.ro.stopWordFilter,e.ro.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ro.stemmer))},e.ro.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.ro.trimmer=e.trimmerSupport.generateTrimmer(e.ro.wordCharacters),e.Pipeline.registerFunction(e.ro.trimmer,"trimmer-ro"),e.ro.stemmer=function(){var i=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,n=new function(){function e(e,i){L.eq_s(1,e)&&(L.ket=L.cursor,L.in_grouping(W,97,259)&&L.slice_from(i))}function n(){for(var i,r;;){if(i=L.cursor,L.in_grouping(W,97,259)&&(r=L.cursor,L.bra=r,e("u","U"),L.cursor=r,e("i","I")),L.cursor=i,L.cursor>=L.limit)break;L.cursor++}}function t(){if(L.out_grouping(W,97,259)){for(;!L.in_grouping(W,97,259);){if(L.cursor>=L.limit)return!0;L.cursor++}return!1}return!0}function a(){if(L.in_grouping(W,97,259))for(;!L.out_grouping(W,97,259);){if(L.cursor>=L.limit)return!0;L.cursor++}return!1}function o(){var e,i,r=L.cursor;if(L.in_grouping(W,97,259)){if(e=L.cursor,!t())return void(h=L.cursor);if(L.cursor=e,!a())return void(h=L.cursor)}L.cursor=r,L.out_grouping(W,97,259)&&(i=L.cursor,t()&&(L.cursor=i,L.in_grouping(W,97,259)&&L.cursor=L.limit)return!1;L.cursor++}for(;!L.out_grouping(W,97,259);){if(L.cursor>=L.limit)return!1;L.cursor++}return!0}function c(){var e=L.cursor;h=L.limit,k=h,g=h,o(),L.cursor=e,u()&&(k=L.cursor,u()&&(g=L.cursor))}function s(){for(var e;;){if(L.bra=L.cursor,e=L.find_among(z,3))switch(L.ket=L.cursor,e){case 1:L.slice_from("i");continue;case 2:L.slice_from("u");continue;case 3:if(L.cursor>=L.limit)break;L.cursor++;continue}break}}function w(){return h<=L.cursor}function m(){return k<=L.cursor}function l(){return g<=L.cursor}function f(){var e,i;if(L.ket=L.cursor,(e=L.find_among_b(C,16))&&(L.bra=L.cursor,m()))switch(e){case 1:L.slice_del();break;case 2:L.slice_from("a");break;case 3:L.slice_from("e");break;case 4:L.slice_from("i");break;case 5:i=L.limit-L.cursor,L.eq_s_b(2,"ab")||(L.cursor=L.limit-i,L.slice_from("i"));break;case 6:L.slice_from("at");break;case 7:L.slice_from("aţi")}}function p(){var e,i=L.limit-L.cursor;if(L.ket=L.cursor,(e=L.find_among_b(P,46))&&(L.bra=L.cursor,m())){switch(e){case 1:L.slice_from("abil");break;case 2:L.slice_from("ibil");break;case 3:L.slice_from("iv");break;case 4:L.slice_from("ic");break;case 5:L.slice_from("at");break;case 6:L.slice_from("it")}return _=!0,L.cursor=L.limit-i,!0}return!1}function d(){var e,i;for(_=!1;;)if(i=L.limit-L.cursor,!p()){L.cursor=L.limit-i;break}if(L.ket=L.cursor,(e=L.find_among_b(F,62))&&(L.bra=L.cursor,l())){switch(e){case 1:L.slice_del();break;case 2:L.eq_s_b(1,"ţ")&&(L.bra=L.cursor,L.slice_from("t"));break;case 3:L.slice_from("ist")}_=!0}}function b(){var e,i,r;if(L.cursor>=h){if(i=L.limit_backward,L.limit_backward=h,L.ket=L.cursor,e=L.find_among_b(q,94))switch(L.bra=L.cursor,e){case 1:if(r=L.limit-L.cursor,!L.out_grouping_b(W,97,259)&&(L.cursor=L.limit-r,!L.eq_s_b(1,"u")))break;case 2:L.slice_del()}L.limit_backward=i}}function v(){var e;L.ket=L.cursor,(e=L.find_among_b(S,5))&&(L.bra=L.cursor,w()&&1==e&&L.slice_del())}var _,g,k,h,z=[new i("",-1,3),new i("I",0,1),new i("U",0,2)],C=[new i("ea",-1,3),new i("aţia",-1,7),new i("aua",-1,2),new i("iua",-1,4),new i("aţie",-1,7),new i("ele",-1,3),new i("ile",-1,5),new i("iile",6,4),new i("iei",-1,4),new i("atei",-1,6),new i("ii",-1,4),new i("ului",-1,1),new i("ul",-1,1),new i("elor",-1,3),new i("ilor",-1,4),new i("iilor",14,4)],P=[new i("icala",-1,4),new i("iciva",-1,4),new i("ativa",-1,5),new i("itiva",-1,6),new i("icale",-1,4),new i("aţiune",-1,5),new i("iţiune",-1,6),new i("atoare",-1,5),new i("itoare",-1,6),new i("ătoare",-1,5),new i("icitate",-1,4),new i("abilitate",-1,1),new i("ibilitate",-1,2),new i("ivitate",-1,3),new i("icive",-1,4),new i("ative",-1,5),new i("itive",-1,6),new i("icali",-1,4),new i("atori",-1,5),new i("icatori",18,4),new i("itori",-1,6),new i("ători",-1,5),new i("icitati",-1,4),new i("abilitati",-1,1),new i("ivitati",-1,3),new i("icivi",-1,4),new i("ativi",-1,5),new i("itivi",-1,6),new i("icităi",-1,4),new i("abilităi",-1,1),new i("ivităi",-1,3),new i("icităţi",-1,4),new i("abilităţi",-1,1),new i("ivităţi",-1,3),new i("ical",-1,4),new i("ator",-1,5),new i("icator",35,4),new i("itor",-1,6),new i("ător",-1,5),new i("iciv",-1,4),new i("ativ",-1,5),new i("itiv",-1,6),new i("icală",-1,4),new i("icivă",-1,4),new i("ativă",-1,5),new i("itivă",-1,6)],F=[new i("ica",-1,1),new i("abila",-1,1),new i("ibila",-1,1),new i("oasa",-1,1),new i("ata",-1,1),new i("ita",-1,1),new i("anta",-1,1),new i("ista",-1,3),new i("uta",-1,1),new i("iva",-1,1),new i("ic",-1,1),new i("ice",-1,1),new i("abile",-1,1),new i("ibile",-1,1),new i("isme",-1,3),new i("iune",-1,2),new i("oase",-1,1),new i("ate",-1,1),new i("itate",17,1),new i("ite",-1,1),new i("ante",-1,1),new i("iste",-1,3),new i("ute",-1,1),new i("ive",-1,1),new i("ici",-1,1),new i("abili",-1,1),new i("ibili",-1,1),new i("iuni",-1,2),new i("atori",-1,1),new i("osi",-1,1),new i("ati",-1,1),new i("itati",30,1),new i("iti",-1,1),new i("anti",-1,1),new i("isti",-1,3),new i("uti",-1,1),new i("işti",-1,3),new i("ivi",-1,1),new i("ităi",-1,1),new i("oşi",-1,1),new i("ităţi",-1,1),new i("abil",-1,1),new i("ibil",-1,1),new i("ism",-1,3),new i("ator",-1,1),new i("os",-1,1),new i("at",-1,1),new i("it",-1,1),new i("ant",-1,1),new i("ist",-1,3),new i("ut",-1,1),new i("iv",-1,1),new i("ică",-1,1),new i("abilă",-1,1),new i("ibilă",-1,1),new i("oasă",-1,1),new i("ată",-1,1),new i("ită",-1,1),new i("antă",-1,1),new i("istă",-1,3),new i("ută",-1,1),new i("ivă",-1,1)],q=[new i("ea",-1,1),new i("ia",-1,1),new i("esc",-1,1),new i("ăsc",-1,1),new i("ind",-1,1),new i("ând",-1,1),new i("are",-1,1),new i("ere",-1,1),new i("ire",-1,1),new i("âre",-1,1),new i("se",-1,2),new i("ase",10,1),new i("sese",10,2),new i("ise",10,1),new i("use",10,1),new i("âse",10,1),new i("eşte",-1,1),new i("ăşte",-1,1),new i("eze",-1,1),new i("ai",-1,1),new i("eai",19,1),new i("iai",19,1),new i("sei",-1,2),new i("eşti",-1,1),new i("ăşti",-1,1),new i("ui",-1,1),new i("ezi",-1,1),new i("âi",-1,1),new i("aşi",-1,1),new i("seşi",-1,2),new i("aseşi",29,1),new i("seseşi",29,2),new i("iseşi",29,1),new i("useşi",29,1),new i("âseşi",29,1),new i("işi",-1,1),new i("uşi",-1,1),new i("âşi",-1,1),new i("aţi",-1,2),new i("eaţi",38,1),new i("iaţi",38,1),new i("eţi",-1,2),new i("iţi",-1,2),new i("âţi",-1,2),new i("arăţi",-1,1),new i("serăţi",-1,2),new i("aserăţi",45,1),new i("seserăţi",45,2),new i("iserăţi",45,1),new i("userăţi",45,1),new i("âserăţi",45,1),new i("irăţi",-1,1),new i("urăţi",-1,1),new i("ârăţi",-1,1),new i("am",-1,1),new i("eam",54,1),new i("iam",54,1),new i("em",-1,2),new i("asem",57,1),new i("sesem",57,2),new i("isem",57,1),new i("usem",57,1),new i("âsem",57,1),new i("im",-1,2),new i("âm",-1,2),new i("ăm",-1,2),new i("arăm",65,1),new i("serăm",65,2),new i("aserăm",67,1),new i("seserăm",67,2),new i("iserăm",67,1),new i("userăm",67,1),new i("âserăm",67,1),new i("irăm",65,1),new i("urăm",65,1),new i("ârăm",65,1),new i("au",-1,1),new i("eau",76,1),new i("iau",76,1),new i("indu",-1,1),new i("ându",-1,1),new i("ez",-1,1),new i("ească",-1,1),new i("ară",-1,1),new i("seră",-1,2),new i("aseră",84,1),new i("seseră",84,2),new i("iseră",84,1),new i("useră",84,1),new i("âseră",84,1),new i("iră",-1,1),new i("ură",-1,1),new i("âră",-1,1),new i("ează",-1,1)],S=[new i("a",-1,1),new i("e",-1,1),new i("ie",1,1),new i("i",-1,1),new i("ă",-1,1)],W=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,2,32,0,0,4],L=new r;this.setCurrent=function(e){L.setCurrent(e)},this.getCurrent=function(){return L.getCurrent()},this.stem=function(){var e=L.cursor;return n(),L.cursor=e,c(),L.limit_backward=e,L.cursor=L.limit,f(),L.cursor=L.limit,d(),L.cursor=L.limit,_||(L.cursor=L.limit,b(),L.cursor=L.limit),v(),L.cursor=L.limit_backward,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.ro.stemmer,"stemmer-ro"),e.ro.stopWordFilter=e.generateStopWordFilter("acea aceasta această aceea acei aceia acel acela acele acelea acest acesta aceste acestea aceşti aceştia acolo acord acum ai aia aibă aici al ale alea altceva altcineva am ar are asemenea asta astea astăzi asupra au avea avem aveţi azi aş aşadar aţi bine bucur bună ca care caut ce cel ceva chiar cinci cine cineva contra cu cum cumva curând curînd când cât câte câtva câţi cînd cît cîte cîtva cîţi că căci cărei căror cărui către da dacă dar datorită dată dau de deci deja deoarece departe deşi din dinaintea dintr- dintre doi doilea două drept după dă ea ei el ele eram este eu eşti face fata fi fie fiecare fii fim fiu fiţi frumos fără graţie halbă iar ieri la le li lor lui lângă lîngă mai mea mei mele mereu meu mi mie mine mult multă mulţi mulţumesc mâine mîine mă ne nevoie nici nicăieri nimeni nimeri nimic nişte noastre noastră noi noroc nostru nouă noştri nu opt ori oricare orice oricine oricum oricând oricât oricînd oricît oriunde patra patru patrulea pe pentru peste pic poate pot prea prima primul prin puţin puţina puţină până pînă rog sa sale sau se spate spre sub sunt suntem sunteţi sută sînt sîntem sînteţi să săi său ta tale te timp tine toate toată tot totuşi toţi trei treia treilea tu tăi tău un una unde undeva unei uneia unele uneori unii unor unora unu unui unuia unul vi voastre voastră voi vostru vouă voştri vreme vreo vreun vă zece zero zi zice îi îl îmi împotriva în înainte înaintea încotro încât încît între întrucât întrucît îţi ăla ălea ăsta ăstea ăştia şapte şase şi ştiu ţi ţie".split(" ")),e.Pipeline.registerFunction(e.ro.stopWordFilter,"stopWordFilter-ro")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.ru.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.ru.min.js deleted file mode 100644 index 186cc485c..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.ru.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Lunr languages, `Russian` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ - -!function(e,n){"function"==typeof define&&define.amd?define(n):"object"==typeof exports?module.exports=n():n()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ru=function(){this.pipeline.reset(),this.pipeline.add(e.ru.trimmer,e.ru.stopWordFilter,e.ru.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ru.stemmer))},e.ru.wordCharacters="Ѐ-҄҇-ԯᴫᵸⷠ-ⷿꙀ-ꚟ︮︯",e.ru.trimmer=e.trimmerSupport.generateTrimmer(e.ru.wordCharacters),e.Pipeline.registerFunction(e.ru.trimmer,"trimmer-ru"),e.ru.stemmer=function(){var n=e.stemmerSupport.Among,r=e.stemmerSupport.SnowballProgram,t=new function(){function e(){for(;!W.in_grouping(S,1072,1103);){if(W.cursor>=W.limit)return!1;W.cursor++}return!0}function t(){for(;!W.out_grouping(S,1072,1103);){if(W.cursor>=W.limit)return!1;W.cursor++}return!0}function w(){b=W.limit,_=b,e()&&(b=W.cursor,t()&&e()&&t()&&(_=W.cursor))}function i(){return _<=W.cursor}function u(e,n){var r,t;if(W.ket=W.cursor,r=W.find_among_b(e,n)){switch(W.bra=W.cursor,r){case 1:if(t=W.limit-W.cursor,!W.eq_s_b(1,"а")&&(W.cursor=W.limit-t,!W.eq_s_b(1,"я")))return!1;case 2:W.slice_del()}return!0}return!1}function o(){return u(h,9)}function s(e,n){var r;return W.ket=W.cursor,!!(r=W.find_among_b(e,n))&&(W.bra=W.cursor,1==r&&W.slice_del(),!0)}function c(){return s(g,26)}function m(){return!!c()&&(u(C,8),!0)}function f(){return s(k,2)}function l(){return u(P,46)}function a(){s(v,36)}function p(){var e;W.ket=W.cursor,(e=W.find_among_b(F,2))&&(W.bra=W.cursor,i()&&1==e&&W.slice_del())}function d(){var e;if(W.ket=W.cursor,e=W.find_among_b(q,4))switch(W.bra=W.cursor,e){case 1:if(W.slice_del(),W.ket=W.cursor,!W.eq_s_b(1,"н"))break;W.bra=W.cursor;case 2:if(!W.eq_s_b(1,"н"))break;case 3:W.slice_del()}}var _,b,h=[new n("в",-1,1),new n("ив",0,2),new n("ыв",0,2),new n("вши",-1,1),new n("ивши",3,2),new n("ывши",3,2),new n("вшись",-1,1),new n("ившись",6,2),new n("ывшись",6,2)],g=[new n("ее",-1,1),new n("ие",-1,1),new n("ое",-1,1),new n("ые",-1,1),new n("ими",-1,1),new n("ыми",-1,1),new n("ей",-1,1),new n("ий",-1,1),new n("ой",-1,1),new n("ый",-1,1),new n("ем",-1,1),new n("им",-1,1),new n("ом",-1,1),new n("ым",-1,1),new n("его",-1,1),new n("ого",-1,1),new n("ему",-1,1),new n("ому",-1,1),new n("их",-1,1),new n("ых",-1,1),new n("ею",-1,1),new n("ою",-1,1),new n("ую",-1,1),new n("юю",-1,1),new n("ая",-1,1),new n("яя",-1,1)],C=[new n("ем",-1,1),new n("нн",-1,1),new n("вш",-1,1),new n("ивш",2,2),new n("ывш",2,2),new n("щ",-1,1),new n("ющ",5,1),new n("ующ",6,2)],k=[new n("сь",-1,1),new n("ся",-1,1)],P=[new n("ла",-1,1),new n("ила",0,2),new n("ыла",0,2),new n("на",-1,1),new n("ена",3,2),new n("ете",-1,1),new n("ите",-1,2),new n("йте",-1,1),new n("ейте",7,2),new n("уйте",7,2),new n("ли",-1,1),new n("или",10,2),new n("ыли",10,2),new n("й",-1,1),new n("ей",13,2),new n("уй",13,2),new n("л",-1,1),new n("ил",16,2),new n("ыл",16,2),new n("ем",-1,1),new n("им",-1,2),new n("ым",-1,2),new n("н",-1,1),new n("ен",22,2),new n("ло",-1,1),new n("ило",24,2),new n("ыло",24,2),new n("но",-1,1),new n("ено",27,2),new n("нно",27,1),new n("ет",-1,1),new n("ует",30,2),new n("ит",-1,2),new n("ыт",-1,2),new n("ют",-1,1),new n("уют",34,2),new n("ят",-1,2),new n("ны",-1,1),new n("ены",37,2),new n("ть",-1,1),new n("ить",39,2),new n("ыть",39,2),new n("ешь",-1,1),new n("ишь",-1,2),new n("ю",-1,2),new n("ую",44,2)],v=[new n("а",-1,1),new n("ев",-1,1),new n("ов",-1,1),new n("е",-1,1),new n("ие",3,1),new n("ье",3,1),new n("и",-1,1),new n("еи",6,1),new n("ии",6,1),new n("ами",6,1),new n("ями",6,1),new n("иями",10,1),new n("й",-1,1),new n("ей",12,1),new n("ией",13,1),new n("ий",12,1),new n("ой",12,1),new n("ам",-1,1),new n("ем",-1,1),new n("ием",18,1),new n("ом",-1,1),new n("ям",-1,1),new n("иям",21,1),new n("о",-1,1),new n("у",-1,1),new n("ах",-1,1),new n("ях",-1,1),new n("иях",26,1),new n("ы",-1,1),new n("ь",-1,1),new n("ю",-1,1),new n("ию",30,1),new n("ью",30,1),new n("я",-1,1),new n("ия",33,1),new n("ья",33,1)],F=[new n("ост",-1,1),new n("ость",-1,1)],q=[new n("ейше",-1,1),new n("н",-1,2),new n("ейш",-1,1),new n("ь",-1,3)],S=[33,65,8,232],W=new r;this.setCurrent=function(e){W.setCurrent(e)},this.getCurrent=function(){return W.getCurrent()},this.stem=function(){return w(),W.cursor=W.limit,!(W.cursor=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor++,!0}return!1},in_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e<=s&&e>=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor--,!0}return!1},out_grouping:function(t,i,s){if(this.cursors||e>3]&1<<(7&e)))return this.cursor++,!0}return!1},out_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e>s||e>3]&1<<(7&e)))return this.cursor--,!0}return!1},eq_s:function(t,i){if(this.limit-this.cursor>1),f=0,l=o0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n+_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n+_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},find_among_b:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit_backward,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o=0;m--){if(n-l==u){f=-1;break}if(f=r.charCodeAt(n-1-l)-_.s[m])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n-_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n-_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},replace_s:function(t,i,s){var e=s.length-(i-t),n=r.substring(0,t),u=r.substring(i);return r=n+s+u,this.limit+=e,this.cursor>=i?this.cursor+=e:this.cursor>t&&(this.cursor=t),e},slice_check:function(){if(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>r.length)throw"faulty slice operation"},slice_from:function(r){this.slice_check(),this.replace_s(this.bra,this.ket,r)},slice_del:function(){this.slice_from("")},insert:function(r,t,i){var s=this.replace_s(r,t,i);r<=this.bra&&(this.bra+=s),r<=this.ket&&(this.ket+=s)},slice_to:function(){return this.slice_check(),r.substring(this.bra,this.ket)},eq_v_b:function(r){return this.eq_s_b(r.length,r)}}}},r.trimmerSupport={generateTrimmer:function(r){var t=new RegExp("^[^"+r+"]+"),i=new RegExp("[^"+r+"]+$");return function(r){return"function"==typeof r.update?r.update(function(r){return r.replace(t,"").replace(i,"")}):r.replace(t,"").replace(i,"")}}}}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.sv.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.sv.min.js deleted file mode 100644 index 3e5eb6400..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.sv.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Lunr languages, `Swedish` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ - -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.sv=function(){this.pipeline.reset(),this.pipeline.add(e.sv.trimmer,e.sv.stopWordFilter,e.sv.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sv.stemmer))},e.sv.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.sv.trimmer=e.trimmerSupport.generateTrimmer(e.sv.wordCharacters),e.Pipeline.registerFunction(e.sv.trimmer,"trimmer-sv"),e.sv.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,t=new function(){function e(){var e,r=w.cursor+3;if(o=w.limit,0<=r||r<=w.limit){for(a=r;;){if(e=w.cursor,w.in_grouping(l,97,246)){w.cursor=e;break}if(w.cursor=e,w.cursor>=w.limit)return;w.cursor++}for(;!w.out_grouping(l,97,246);){if(w.cursor>=w.limit)return;w.cursor++}o=w.cursor,o=o&&(w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(u,37),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.in_grouping_b(d,98,121)&&w.slice_del()}}function i(){var e=w.limit_backward;w.cursor>=o&&(w.limit_backward=o,w.cursor=w.limit,w.find_among_b(c,7)&&(w.cursor=w.limit,w.ket=w.cursor,w.cursor>w.limit_backward&&(w.bra=--w.cursor,w.slice_del())),w.limit_backward=e)}function s(){var e,r;if(w.cursor>=o){if(r=w.limit_backward,w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(m,5))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.slice_from("lös");break;case 3:w.slice_from("full")}w.limit_backward=r}}var a,o,u=[new r("a",-1,1),new r("arna",0,1),new r("erna",0,1),new r("heterna",2,1),new r("orna",0,1),new r("ad",-1,1),new r("e",-1,1),new r("ade",6,1),new r("ande",6,1),new r("arne",6,1),new r("are",6,1),new r("aste",6,1),new r("en",-1,1),new r("anden",12,1),new r("aren",12,1),new r("heten",12,1),new r("ern",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",18,1),new r("or",-1,1),new r("s",-1,2),new r("as",21,1),new r("arnas",22,1),new r("ernas",22,1),new r("ornas",22,1),new r("es",21,1),new r("ades",26,1),new r("andes",26,1),new r("ens",21,1),new r("arens",29,1),new r("hetens",29,1),new r("erns",21,1),new r("at",-1,1),new r("andet",-1,1),new r("het",-1,1),new r("ast",-1,1)],c=[new r("dd",-1,-1),new r("gd",-1,-1),new r("nn",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1),new r("tt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("els",-1,1),new r("fullt",-1,3),new r("löst",-1,2)],l=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,24,0,32],d=[119,127,149],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,t(),w.cursor=w.limit,i(),w.cursor=w.limit,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return t.setCurrent(e),t.stem(),t.getCurrent()}):(t.setCurrent(e),t.stem(),t.getCurrent())}}(),e.Pipeline.registerFunction(e.sv.stemmer,"stemmer-sv"),e.sv.stopWordFilter=e.generateStopWordFilter("alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över".split(" ")),e.Pipeline.registerFunction(e.sv.stopWordFilter,"stopWordFilter-sv")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.tr.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.tr.min.js deleted file mode 100644 index 563f6ec1f..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.tr.min.js +++ /dev/null @@ -1,18 +0,0 @@ -/*! - * Lunr languages, `Turkish` language - * https://github.com/MihaiValentin/lunr-languages - * - * Copyright 2014, Mihai Valentin - * http://www.mozilla.org/MPL/ - */ -/*! - * based on - * Snowball JavaScript Library v0.3 - * http://code.google.com/p/urim/ - * http://snowball.tartarus.org/ - * - * Copyright 2010, Oleg Mazko - * http://www.mozilla.org/MPL/ - */ - -!function(r,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(r.lunr)}(this,function(){return function(r){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");r.tr=function(){this.pipeline.reset(),this.pipeline.add(r.tr.trimmer,r.tr.stopWordFilter,r.tr.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(r.tr.stemmer))},r.tr.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",r.tr.trimmer=r.trimmerSupport.generateTrimmer(r.tr.wordCharacters),r.Pipeline.registerFunction(r.tr.trimmer,"trimmer-tr"),r.tr.stemmer=function(){var i=r.stemmerSupport.Among,e=r.stemmerSupport.SnowballProgram,n=new function(){function r(r,i,e){for(;;){var n=Dr.limit-Dr.cursor;if(Dr.in_grouping_b(r,i,e)){Dr.cursor=Dr.limit-n;break}if(Dr.cursor=Dr.limit-n,Dr.cursor<=Dr.limit_backward)return!1;Dr.cursor--}return!0}function n(){var i,e;i=Dr.limit-Dr.cursor,r(Wr,97,305);for(var n=0;nDr.limit_backward&&(Dr.cursor--,e=Dr.limit-Dr.cursor,i()))?(Dr.cursor=Dr.limit-e,!0):(Dr.cursor=Dr.limit-n,r()?(Dr.cursor=Dr.limit-n,!1):(Dr.cursor=Dr.limit-n,!(Dr.cursor<=Dr.limit_backward)&&(Dr.cursor--,!!i()&&(Dr.cursor=Dr.limit-n,!0))))}function u(r){return t(r,function(){return Dr.in_grouping_b(Wr,97,305)})}function o(){return u(function(){return Dr.eq_s_b(1,"n")})}function s(){return u(function(){return Dr.eq_s_b(1,"s")})}function c(){return u(function(){return Dr.eq_s_b(1,"y")})}function l(){return t(function(){return Dr.in_grouping_b(Lr,105,305)},function(){return Dr.out_grouping_b(Wr,97,305)})}function a(){return Dr.find_among_b(ur,10)&&l()}function m(){return n()&&Dr.in_grouping_b(Lr,105,305)&&s()}function d(){return Dr.find_among_b(or,2)}function f(){return n()&&Dr.in_grouping_b(Lr,105,305)&&c()}function b(){return n()&&Dr.find_among_b(sr,4)}function w(){return n()&&Dr.find_among_b(cr,4)&&o()}function _(){return n()&&Dr.find_among_b(lr,2)&&c()}function k(){return n()&&Dr.find_among_b(ar,2)}function p(){return n()&&Dr.find_among_b(mr,4)}function g(){return n()&&Dr.find_among_b(dr,2)}function y(){return n()&&Dr.find_among_b(fr,4)}function z(){return n()&&Dr.find_among_b(br,2)}function v(){return n()&&Dr.find_among_b(wr,2)&&c()}function h(){return Dr.eq_s_b(2,"ki")}function q(){return n()&&Dr.find_among_b(_r,2)&&o()}function C(){return n()&&Dr.find_among_b(kr,4)&&c()}function P(){return n()&&Dr.find_among_b(pr,4)}function F(){return n()&&Dr.find_among_b(gr,4)&&c()}function S(){return Dr.find_among_b(yr,4)}function W(){return n()&&Dr.find_among_b(zr,2)}function L(){return n()&&Dr.find_among_b(vr,4)}function x(){return n()&&Dr.find_among_b(hr,8)}function A(){return Dr.find_among_b(qr,2)}function E(){return n()&&Dr.find_among_b(Cr,32)&&c()}function j(){return Dr.find_among_b(Pr,8)&&c()}function T(){return n()&&Dr.find_among_b(Fr,4)&&c()}function Z(){return Dr.eq_s_b(3,"ken")&&c()}function B(){var r=Dr.limit-Dr.cursor;return!(T()||(Dr.cursor=Dr.limit-r,E()||(Dr.cursor=Dr.limit-r,j()||(Dr.cursor=Dr.limit-r,Z()))))}function D(){if(A()){var r=Dr.limit-Dr.cursor;if(S()||(Dr.cursor=Dr.limit-r,W()||(Dr.cursor=Dr.limit-r,C()||(Dr.cursor=Dr.limit-r,P()||(Dr.cursor=Dr.limit-r,F()||(Dr.cursor=Dr.limit-r))))),T())return!1}return!0}function G(){if(W()){Dr.bra=Dr.cursor,Dr.slice_del();var r=Dr.limit-Dr.cursor;return Dr.ket=Dr.cursor,x()||(Dr.cursor=Dr.limit-r,E()||(Dr.cursor=Dr.limit-r,j()||(Dr.cursor=Dr.limit-r,T()||(Dr.cursor=Dr.limit-r)))),nr=!1,!1}return!0}function H(){if(!L())return!0;var r=Dr.limit-Dr.cursor;return!E()&&(Dr.cursor=Dr.limit-r,!j())}function I(){var r,i=Dr.limit-Dr.cursor;return!(S()||(Dr.cursor=Dr.limit-i,F()||(Dr.cursor=Dr.limit-i,P()||(Dr.cursor=Dr.limit-i,C()))))||(Dr.bra=Dr.cursor,Dr.slice_del(),r=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,T()||(Dr.cursor=Dr.limit-r),!1)}function J(){var r,i=Dr.limit-Dr.cursor;if(Dr.ket=Dr.cursor,nr=!0,B()&&(Dr.cursor=Dr.limit-i,D()&&(Dr.cursor=Dr.limit-i,G()&&(Dr.cursor=Dr.limit-i,H()&&(Dr.cursor=Dr.limit-i,I()))))){if(Dr.cursor=Dr.limit-i,!x())return;Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,r=Dr.limit-Dr.cursor,S()||(Dr.cursor=Dr.limit-r,W()||(Dr.cursor=Dr.limit-r,C()||(Dr.cursor=Dr.limit-r,P()||(Dr.cursor=Dr.limit-r,F()||(Dr.cursor=Dr.limit-r))))),T()||(Dr.cursor=Dr.limit-r)}Dr.bra=Dr.cursor,Dr.slice_del()}function K(){var r,i,e,n;if(Dr.ket=Dr.cursor,h()){if(r=Dr.limit-Dr.cursor,p())return Dr.bra=Dr.cursor,Dr.slice_del(),i=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,W()?(Dr.bra=Dr.cursor,Dr.slice_del(),K()):(Dr.cursor=Dr.limit-i,a()&&(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K()))),!0;if(Dr.cursor=Dr.limit-r,w()){if(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,e=Dr.limit-Dr.cursor,d())Dr.bra=Dr.cursor,Dr.slice_del();else{if(Dr.cursor=Dr.limit-e,Dr.ket=Dr.cursor,!a()&&(Dr.cursor=Dr.limit-e,!m()&&(Dr.cursor=Dr.limit-e,!K())))return!0;Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K())}return!0}if(Dr.cursor=Dr.limit-r,g()){if(n=Dr.limit-Dr.cursor,d())Dr.bra=Dr.cursor,Dr.slice_del();else if(Dr.cursor=Dr.limit-n,m())Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K());else if(Dr.cursor=Dr.limit-n,!K())return!1;return!0}}return!1}function M(r){if(Dr.ket=Dr.cursor,!g()&&(Dr.cursor=Dr.limit-r,!k()))return!1;var i=Dr.limit-Dr.cursor;if(d())Dr.bra=Dr.cursor,Dr.slice_del();else if(Dr.cursor=Dr.limit-i,m())Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K());else if(Dr.cursor=Dr.limit-i,!K())return!1;return!0}function N(r){if(Dr.ket=Dr.cursor,!z()&&(Dr.cursor=Dr.limit-r,!b()))return!1;var i=Dr.limit-Dr.cursor;return!(!m()&&(Dr.cursor=Dr.limit-i,!d()))&&(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K()),!0)}function O(){var r,i=Dr.limit-Dr.cursor;return Dr.ket=Dr.cursor,!(!w()&&(Dr.cursor=Dr.limit-i,!v()))&&(Dr.bra=Dr.cursor,Dr.slice_del(),r=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,!(!W()||(Dr.bra=Dr.cursor,Dr.slice_del(),!K()))||(Dr.cursor=Dr.limit-r,Dr.ket=Dr.cursor,!(a()||(Dr.cursor=Dr.limit-r,m()||(Dr.cursor=Dr.limit-r,K())))||(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K()),!0)))}function Q(){var r,i,e=Dr.limit-Dr.cursor;if(Dr.ket=Dr.cursor,!p()&&(Dr.cursor=Dr.limit-e,!f()&&(Dr.cursor=Dr.limit-e,!_())))return!1;if(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,r=Dr.limit-Dr.cursor,a())Dr.bra=Dr.cursor,Dr.slice_del(),i=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,W()||(Dr.cursor=Dr.limit-i);else if(Dr.cursor=Dr.limit-r,!W())return!0;return Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,K(),!0}function R(){var r,i,e=Dr.limit-Dr.cursor;if(Dr.ket=Dr.cursor,W())return Dr.bra=Dr.cursor,Dr.slice_del(),void K();if(Dr.cursor=Dr.limit-e,Dr.ket=Dr.cursor,q())if(Dr.bra=Dr.cursor,Dr.slice_del(),r=Dr.limit-Dr.cursor,Dr.ket=Dr.cursor,d())Dr.bra=Dr.cursor,Dr.slice_del();else{if(Dr.cursor=Dr.limit-r,Dr.ket=Dr.cursor,!a()&&(Dr.cursor=Dr.limit-r,!m())){if(Dr.cursor=Dr.limit-r,Dr.ket=Dr.cursor,!W())return;if(Dr.bra=Dr.cursor,Dr.slice_del(),!K())return}Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K())}else if(Dr.cursor=Dr.limit-e,!M(e)&&(Dr.cursor=Dr.limit-e,!N(e))){if(Dr.cursor=Dr.limit-e,Dr.ket=Dr.cursor,y())return Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,i=Dr.limit-Dr.cursor,void(a()?(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K())):(Dr.cursor=Dr.limit-i,W()?(Dr.bra=Dr.cursor,Dr.slice_del(),K()):(Dr.cursor=Dr.limit-i,K())));if(Dr.cursor=Dr.limit-e,!O()){if(Dr.cursor=Dr.limit-e,d())return Dr.bra=Dr.cursor,void Dr.slice_del();Dr.cursor=Dr.limit-e,K()||(Dr.cursor=Dr.limit-e,Q()||(Dr.cursor=Dr.limit-e,Dr.ket=Dr.cursor,(a()||(Dr.cursor=Dr.limit-e,m()))&&(Dr.bra=Dr.cursor,Dr.slice_del(),Dr.ket=Dr.cursor,W()&&(Dr.bra=Dr.cursor,Dr.slice_del(),K()))))}}}function U(){var r;if(Dr.ket=Dr.cursor,r=Dr.find_among_b(Sr,4))switch(Dr.bra=Dr.cursor,r){case 1:Dr.slice_from("p");break;case 2:Dr.slice_from("ç");break;case 3:Dr.slice_from("t");break;case 4:Dr.slice_from("k")}}function V(){for(;;){var r=Dr.limit-Dr.cursor;if(Dr.in_grouping_b(Wr,97,305)){Dr.cursor=Dr.limit-r;break}if(Dr.cursor=Dr.limit-r,Dr.cursor<=Dr.limit_backward)return!1;Dr.cursor--}return!0}function X(r,i,e){if(Dr.cursor=Dr.limit-r,V()){var n=Dr.limit-Dr.cursor;if(!Dr.eq_s_b(1,i)&&(Dr.cursor=Dr.limit-n,!Dr.eq_s_b(1,e)))return!0;Dr.cursor=Dr.limit-r;var t=Dr.cursor;return Dr.insert(Dr.cursor,Dr.cursor,e),Dr.cursor=t,!1}return!0}function Y(){var r=Dr.limit-Dr.cursor;(Dr.eq_s_b(1,"d")||(Dr.cursor=Dr.limit-r,Dr.eq_s_b(1,"g")))&&X(r,"a","ı")&&X(r,"e","i")&&X(r,"o","u")&&X(r,"ö","ü")}function $(){for(var r,i=Dr.cursor,e=2;;){for(r=Dr.cursor;!Dr.in_grouping(Wr,97,305);){if(Dr.cursor>=Dr.limit)return Dr.cursor=r,!(e>0)&&(Dr.cursor=i,!0);Dr.cursor++}e--}}function rr(r,i,e){for(;!Dr.eq_s(i,e);){if(Dr.cursor>=Dr.limit)return!0;Dr.cursor++}return(tr=i)!=Dr.limit||(Dr.cursor=r,!1)}function ir(){var r=Dr.cursor;return!rr(r,2,"ad")||(Dr.cursor=r,!rr(r,5,"soyad"))}function er(){var r=Dr.cursor;return!ir()&&(Dr.limit_backward=r,Dr.cursor=Dr.limit,Y(),Dr.cursor=Dr.limit,U(),!0)}var nr,tr,ur=[new i("m",-1,-1),new i("n",-1,-1),new i("miz",-1,-1),new i("niz",-1,-1),new i("muz",-1,-1),new i("nuz",-1,-1),new i("müz",-1,-1),new i("nüz",-1,-1),new i("mız",-1,-1),new i("nız",-1,-1)],or=[new i("leri",-1,-1),new i("ları",-1,-1)],sr=[new i("ni",-1,-1),new i("nu",-1,-1),new i("nü",-1,-1),new i("nı",-1,-1)],cr=[new i("in",-1,-1),new i("un",-1,-1),new i("ün",-1,-1),new i("ın",-1,-1)],lr=[new i("a",-1,-1),new i("e",-1,-1)],ar=[new i("na",-1,-1),new i("ne",-1,-1)],mr=[new i("da",-1,-1),new i("ta",-1,-1),new i("de",-1,-1),new i("te",-1,-1)],dr=[new i("nda",-1,-1),new i("nde",-1,-1)],fr=[new i("dan",-1,-1),new i("tan",-1,-1),new i("den",-1,-1),new i("ten",-1,-1)],br=[new i("ndan",-1,-1),new i("nden",-1,-1)],wr=[new i("la",-1,-1),new i("le",-1,-1)],_r=[new i("ca",-1,-1),new i("ce",-1,-1)],kr=[new i("im",-1,-1),new i("um",-1,-1),new i("üm",-1,-1),new i("ım",-1,-1)],pr=[new i("sin",-1,-1),new i("sun",-1,-1),new i("sün",-1,-1),new i("sın",-1,-1)],gr=[new i("iz",-1,-1),new i("uz",-1,-1),new i("üz",-1,-1),new i("ız",-1,-1)],yr=[new i("siniz",-1,-1),new i("sunuz",-1,-1),new i("sünüz",-1,-1),new i("sınız",-1,-1)],zr=[new i("lar",-1,-1),new i("ler",-1,-1)],vr=[new i("niz",-1,-1),new i("nuz",-1,-1),new i("nüz",-1,-1),new i("nız",-1,-1)],hr=[new i("dir",-1,-1),new i("tir",-1,-1),new i("dur",-1,-1),new i("tur",-1,-1),new i("dür",-1,-1),new i("tür",-1,-1),new i("dır",-1,-1),new i("tır",-1,-1)],qr=[new i("casına",-1,-1),new i("cesine",-1,-1)],Cr=[new i("di",-1,-1),new i("ti",-1,-1),new i("dik",-1,-1),new i("tik",-1,-1),new i("duk",-1,-1),new i("tuk",-1,-1),new i("dük",-1,-1),new i("tük",-1,-1),new i("dık",-1,-1),new i("tık",-1,-1),new i("dim",-1,-1),new i("tim",-1,-1),new i("dum",-1,-1),new i("tum",-1,-1),new i("düm",-1,-1),new i("tüm",-1,-1),new i("dım",-1,-1),new i("tım",-1,-1),new i("din",-1,-1),new i("tin",-1,-1),new i("dun",-1,-1),new i("tun",-1,-1),new i("dün",-1,-1),new i("tün",-1,-1),new i("dın",-1,-1),new i("tın",-1,-1),new i("du",-1,-1),new i("tu",-1,-1),new i("dü",-1,-1),new i("tü",-1,-1),new i("dı",-1,-1),new i("tı",-1,-1)],Pr=[new i("sa",-1,-1),new i("se",-1,-1),new i("sak",-1,-1),new i("sek",-1,-1),new i("sam",-1,-1),new i("sem",-1,-1),new i("san",-1,-1),new i("sen",-1,-1)],Fr=[new i("miş",-1,-1),new i("muş",-1,-1),new i("müş",-1,-1),new i("mış",-1,-1)],Sr=[new i("b",-1,1),new i("c",-1,2),new i("d",-1,3),new i("ğ",-1,4)],Wr=[17,65,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,32,8,0,0,0,0,0,0,1],Lr=[1,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,0,0,0,1],xr=[1,64,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],Ar=[17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,130],Er=[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],jr=[17],Tr=[65],Zr=[65],Br=[["a",xr,97,305],["e",Ar,101,252],["ı",Er,97,305],["i",jr,101,105],["o",Tr,111,117],["ö",Zr,246,252],["u",Tr,111,117]],Dr=new e;this.setCurrent=function(r){Dr.setCurrent(r)},this.getCurrent=function(){return Dr.getCurrent()},this.stem=function(){return!!($()&&(Dr.limit_backward=Dr.cursor,Dr.cursor=Dr.limit,J(),Dr.cursor=Dr.limit,nr&&(R(),Dr.cursor=Dr.limit_backward,er())))}};return function(r){return"function"==typeof r.update?r.update(function(r){return n.setCurrent(r),n.stem(),n.getCurrent()}):(n.setCurrent(r),n.stem(),n.getCurrent())}}(),r.Pipeline.registerFunction(r.tr.stemmer,"stemmer-tr"),r.tr.stopWordFilter=r.generateStopWordFilter("acaba altmış altı ama ancak arada aslında ayrıca bana bazı belki ben benden beni benim beri beş bile bin bir biri birkaç birkez birçok birşey birşeyi biz bizden bize bizi bizim bu buna bunda bundan bunlar bunları bunların bunu bunun burada böyle böylece da daha dahi de defa değil diye diğer doksan dokuz dolayı dolayısıyla dört edecek eden ederek edilecek ediliyor edilmesi ediyor elli en etmesi etti ettiği ettiğini eğer gibi göre halen hangi hatta hem henüz hep hepsi her herhangi herkesin hiç hiçbir iki ile ilgili ise itibaren itibariyle için işte kadar karşın katrilyon kendi kendilerine kendini kendisi kendisine kendisini kez ki kim kimden kime kimi kimse kırk milyar milyon mu mü mı nasıl ne neden nedenle nerde nerede nereye niye niçin o olan olarak oldu olduklarını olduğu olduğunu olmadı olmadığı olmak olması olmayan olmaz olsa olsun olup olur olursa oluyor on ona ondan onlar onlardan onları onların onu onun otuz oysa pek rağmen sadece sanki sekiz seksen sen senden seni senin siz sizden sizi sizin tarafından trilyon tüm var vardı ve veya ya yani yapacak yapmak yaptı yaptıkları yaptığı yaptığını yapılan yapılması yapıyor yedi yerine yetmiş yine yirmi yoksa yüz zaten çok çünkü öyle üzere üç şey şeyden şeyi şeyler şu şuna şunda şundan şunları şunu şöyle".split(" ")),r.Pipeline.registerFunction(r.tr.stopWordFilter,"stopWordFilter-tr")}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/min/lunr.vi.min.js b/docs/v3/v2/assets/javascripts/lunr/min/lunr.vi.min.js deleted file mode 100644 index 22aed28c4..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/min/lunr.vi.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.vi=function(){this.pipeline.reset(),this.pipeline.add(e.vi.stopWordFilter,e.vi.trimmer)},e.vi.wordCharacters="[A-Za-ẓ̀͐́͑̉̃̓ÂâÊêÔôĂ-ăĐ-đƠ-ơƯ-ư]",e.vi.trimmer=e.trimmerSupport.generateTrimmer(e.vi.wordCharacters),e.Pipeline.registerFunction(e.vi.trimmer,"trimmer-vi"),e.vi.stopWordFilter=e.generateStopWordFilter("là cái nhưng mà".split(" "))}}); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/lunr/tinyseg.min.js b/docs/v3/v2/assets/javascripts/lunr/tinyseg.min.js deleted file mode 100644 index 302befbb3..000000000 --- a/docs/v3/v2/assets/javascripts/lunr/tinyseg.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(_,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(_.lunr)}(this,(function(){return function(_){function t(){var _={"[一二三四五六七八九十百千万億兆]":"M","[一-龠々〆ヵヶ]":"H","[ぁ-ん]":"I","[ァ-ヴーア-ン゙ー]":"K","[a-zA-Za-zA-Z]":"A","[0-90-9]":"N"};for(var t in this.chartype_=[],_){var H=new RegExp(t);this.chartype_.push([H,_[t]])}return this.BIAS__=-332,this.BC1__={HH:6,II:2461,KH:406,OH:-1378},this.BC2__={AA:-3267,AI:2744,AN:-878,HH:-4070,HM:-1711,HN:4012,HO:3761,IA:1327,IH:-1184,II:-1332,IK:1721,IO:5492,KI:3831,KK:-8741,MH:-3132,MK:3334,OO:-2920},this.BC3__={HH:996,HI:626,HK:-721,HN:-1307,HO:-836,IH:-301,KK:2762,MK:1079,MM:4034,OA:-1652,OH:266},this.BP1__={BB:295,OB:304,OO:-125,UB:352},this.BP2__={BO:60,OO:-1762},this.BQ1__={BHH:1150,BHM:1521,BII:-1158,BIM:886,BMH:1208,BNH:449,BOH:-91,BOO:-2597,OHI:451,OIH:-296,OKA:1851,OKH:-1020,OKK:904,OOO:2965},this.BQ2__={BHH:118,BHI:-1159,BHM:466,BIH:-919,BKK:-1720,BKO:864,OHH:-1139,OHM:-181,OIH:153,UHI:-1146},this.BQ3__={BHH:-792,BHI:2664,BII:-299,BKI:419,BMH:937,BMM:8335,BNN:998,BOH:775,OHH:2174,OHM:439,OII:280,OKH:1798,OKI:-793,OKO:-2242,OMH:-2402,OOO:11699},this.BQ4__={BHH:-3895,BIH:3761,BII:-4654,BIK:1348,BKK:-1806,BMI:-3385,BOO:-12396,OAH:926,OHH:266,OHK:-2036,ONN:-973},this.BW1__={",と":660,",同":727,"B1あ":1404,"B1同":542,"、と":660,"、同":727,"」と":1682,"あっ":1505,"いう":1743,"いっ":-2055,"いる":672,"うし":-4817,"うん":665,"から":3472,"がら":600,"こう":-790,"こと":2083,"こん":-1262,"さら":-4143,"さん":4573,"した":2641,"して":1104,"すで":-3399,"そこ":1977,"それ":-871,"たち":1122,"ため":601,"った":3463,"つい":-802,"てい":805,"てき":1249,"でき":1127,"です":3445,"では":844,"とい":-4915,"とみ":1922,"どこ":3887,"ない":5713,"なっ":3015,"など":7379,"なん":-1113,"にし":2468,"には":1498,"にも":1671,"に対":-912,"の一":-501,"の中":741,"ませ":2448,"まで":1711,"まま":2600,"まる":-2155,"やむ":-1947,"よっ":-2565,"れた":2369,"れで":-913,"をし":1860,"を見":731,"亡く":-1886,"京都":2558,"取り":-2784,"大き":-2604,"大阪":1497,"平方":-2314,"引き":-1336,"日本":-195,"本当":-2423,"毎日":-2113,"目指":-724,"B1あ":1404,"B1同":542,"」と":1682},this.BW2__={"..":-11822,11:-669,"――":-5730,"−−":-13175,"いう":-1609,"うか":2490,"かし":-1350,"かも":-602,"から":-7194,"かれ":4612,"がい":853,"がら":-3198,"きた":1941,"くな":-1597,"こと":-8392,"この":-4193,"させ":4533,"され":13168,"さん":-3977,"しい":-1819,"しか":-545,"した":5078,"して":972,"しな":939,"その":-3744,"たい":-1253,"たた":-662,"ただ":-3857,"たち":-786,"たと":1224,"たは":-939,"った":4589,"って":1647,"っと":-2094,"てい":6144,"てき":3640,"てく":2551,"ては":-3110,"ても":-3065,"でい":2666,"でき":-1528,"でし":-3828,"です":-4761,"でも":-4203,"とい":1890,"とこ":-1746,"とと":-2279,"との":720,"とみ":5168,"とも":-3941,"ない":-2488,"なが":-1313,"など":-6509,"なの":2614,"なん":3099,"にお":-1615,"にし":2748,"にな":2454,"によ":-7236,"に対":-14943,"に従":-4688,"に関":-11388,"のか":2093,"ので":-7059,"のに":-6041,"のの":-6125,"はい":1073,"はが":-1033,"はず":-2532,"ばれ":1813,"まし":-1316,"まで":-6621,"まれ":5409,"めて":-3153,"もい":2230,"もの":-10713,"らか":-944,"らし":-1611,"らに":-1897,"りし":651,"りま":1620,"れた":4270,"れて":849,"れば":4114,"ろう":6067,"われ":7901,"を通":-11877,"んだ":728,"んな":-4115,"一人":602,"一方":-1375,"一日":970,"一部":-1051,"上が":-4479,"会社":-1116,"出て":2163,"分の":-7758,"同党":970,"同日":-913,"大阪":-2471,"委員":-1250,"少な":-1050,"年度":-8669,"年間":-1626,"府県":-2363,"手権":-1982,"新聞":-4066,"日新":-722,"日本":-7068,"日米":3372,"曜日":-601,"朝鮮":-2355,"本人":-2697,"東京":-1543,"然と":-1384,"社会":-1276,"立て":-990,"第に":-1612,"米国":-4268,"11":-669},this.BW3__={"あた":-2194,"あり":719,"ある":3846,"い.":-1185,"い。":-1185,"いい":5308,"いえ":2079,"いく":3029,"いた":2056,"いっ":1883,"いる":5600,"いわ":1527,"うち":1117,"うと":4798,"えと":1454,"か.":2857,"か。":2857,"かけ":-743,"かっ":-4098,"かに":-669,"から":6520,"かり":-2670,"が,":1816,"が、":1816,"がき":-4855,"がけ":-1127,"がっ":-913,"がら":-4977,"がり":-2064,"きた":1645,"けど":1374,"こと":7397,"この":1542,"ころ":-2757,"さい":-714,"さを":976,"し,":1557,"し、":1557,"しい":-3714,"した":3562,"して":1449,"しな":2608,"しま":1200,"す.":-1310,"す。":-1310,"する":6521,"ず,":3426,"ず、":3426,"ずに":841,"そう":428,"た.":8875,"た。":8875,"たい":-594,"たの":812,"たり":-1183,"たる":-853,"だ.":4098,"だ。":4098,"だっ":1004,"った":-4748,"って":300,"てい":6240,"てお":855,"ても":302,"です":1437,"でに":-1482,"では":2295,"とう":-1387,"とし":2266,"との":541,"とも":-3543,"どう":4664,"ない":1796,"なく":-903,"など":2135,"に,":-1021,"に、":-1021,"にし":1771,"にな":1906,"には":2644,"の,":-724,"の、":-724,"の子":-1e3,"は,":1337,"は、":1337,"べき":2181,"まし":1113,"ます":6943,"まっ":-1549,"まで":6154,"まれ":-793,"らし":1479,"られ":6820,"るる":3818,"れ,":854,"れ、":854,"れた":1850,"れて":1375,"れば":-3246,"れる":1091,"われ":-605,"んだ":606,"んで":798,"カ月":990,"会議":860,"入り":1232,"大会":2217,"始め":1681,"市":965,"新聞":-5055,"日,":974,"日、":974,"社会":2024,"カ月":990},this.TC1__={AAA:1093,HHH:1029,HHM:580,HII:998,HOH:-390,HOM:-331,IHI:1169,IOH:-142,IOI:-1015,IOM:467,MMH:187,OOI:-1832},this.TC2__={HHO:2088,HII:-1023,HMM:-1154,IHI:-1965,KKH:703,OII:-2649},this.TC3__={AAA:-294,HHH:346,HHI:-341,HII:-1088,HIK:731,HOH:-1486,IHH:128,IHI:-3041,IHO:-1935,IIH:-825,IIM:-1035,IOI:-542,KHH:-1216,KKA:491,KKH:-1217,KOK:-1009,MHH:-2694,MHM:-457,MHO:123,MMH:-471,NNH:-1689,NNO:662,OHO:-3393},this.TC4__={HHH:-203,HHI:1344,HHK:365,HHM:-122,HHN:182,HHO:669,HIH:804,HII:679,HOH:446,IHH:695,IHO:-2324,IIH:321,III:1497,IIO:656,IOO:54,KAK:4845,KKA:3386,KKK:3065,MHH:-405,MHI:201,MMH:-241,MMM:661,MOM:841},this.TQ1__={BHHH:-227,BHHI:316,BHIH:-132,BIHH:60,BIII:1595,BNHH:-744,BOHH:225,BOOO:-908,OAKK:482,OHHH:281,OHIH:249,OIHI:200,OIIH:-68},this.TQ2__={BIHH:-1401,BIII:-1033,BKAK:-543,BOOO:-5591},this.TQ3__={BHHH:478,BHHM:-1073,BHIH:222,BHII:-504,BIIH:-116,BIII:-105,BMHI:-863,BMHM:-464,BOMH:620,OHHH:346,OHHI:1729,OHII:997,OHMH:481,OIHH:623,OIIH:1344,OKAK:2792,OKHH:587,OKKA:679,OOHH:110,OOII:-685},this.TQ4__={BHHH:-721,BHHM:-3604,BHII:-966,BIIH:-607,BIII:-2181,OAAA:-2763,OAKK:180,OHHH:-294,OHHI:2446,OHHO:480,OHIH:-1573,OIHH:1935,OIHI:-493,OIIH:626,OIII:-4007,OKAK:-8156},this.TW1__={"につい":-4681,"東京都":2026},this.TW2__={"ある程":-2049,"いった":-1256,"ころが":-2434,"しょう":3873,"その後":-4430,"だって":-1049,"ていた":1833,"として":-4657,"ともに":-4517,"もので":1882,"一気に":-792,"初めて":-1512,"同時に":-8097,"大きな":-1255,"対して":-2721,"社会党":-3216},this.TW3__={"いただ":-1734,"してい":1314,"として":-4314,"につい":-5483,"にとっ":-5989,"に当た":-6247,"ので,":-727,"ので、":-727,"のもの":-600,"れから":-3752,"十二月":-2287},this.TW4__={"いう.":8576,"いう。":8576,"からな":-2348,"してい":2958,"たが,":1516,"たが、":1516,"ている":1538,"という":1349,"ました":5543,"ません":1097,"ようと":-4258,"よると":5865},this.UC1__={A:484,K:93,M:645,O:-505},this.UC2__={A:819,H:1059,I:409,M:3987,N:5775,O:646},this.UC3__={A:-1370,I:2311},this.UC4__={A:-2643,H:1809,I:-1032,K:-3450,M:3565,N:3876,O:6646},this.UC5__={H:313,I:-1238,K:-799,M:539,O:-831},this.UC6__={H:-506,I:-253,K:87,M:247,O:-387},this.UP1__={O:-214},this.UP2__={B:69,O:935},this.UP3__={B:189},this.UQ1__={BH:21,BI:-12,BK:-99,BN:142,BO:-56,OH:-95,OI:477,OK:410,OO:-2422},this.UQ2__={BH:216,BI:113,OK:1759},this.UQ3__={BA:-479,BH:42,BI:1913,BK:-7198,BM:3160,BN:6427,BO:14761,OI:-827,ON:-3212},this.UW1__={",":156,"、":156,"「":-463,"あ":-941,"う":-127,"が":-553,"き":121,"こ":505,"で":-201,"と":-547,"ど":-123,"に":-789,"の":-185,"は":-847,"も":-466,"や":-470,"よ":182,"ら":-292,"り":208,"れ":169,"を":-446,"ん":-137,"・":-135,"主":-402,"京":-268,"区":-912,"午":871,"国":-460,"大":561,"委":729,"市":-411,"日":-141,"理":361,"生":-408,"県":-386,"都":-718,"「":-463,"・":-135},this.UW2__={",":-829,"、":-829,"〇":892,"「":-645,"」":3145,"あ":-538,"い":505,"う":134,"お":-502,"か":1454,"が":-856,"く":-412,"こ":1141,"さ":878,"ざ":540,"し":1529,"す":-675,"せ":300,"そ":-1011,"た":188,"だ":1837,"つ":-949,"て":-291,"で":-268,"と":-981,"ど":1273,"な":1063,"に":-1764,"の":130,"は":-409,"ひ":-1273,"べ":1261,"ま":600,"も":-1263,"や":-402,"よ":1639,"り":-579,"る":-694,"れ":571,"を":-2516,"ん":2095,"ア":-587,"カ":306,"キ":568,"ッ":831,"三":-758,"不":-2150,"世":-302,"中":-968,"主":-861,"事":492,"人":-123,"会":978,"保":362,"入":548,"初":-3025,"副":-1566,"北":-3414,"区":-422,"大":-1769,"天":-865,"太":-483,"子":-1519,"学":760,"実":1023,"小":-2009,"市":-813,"年":-1060,"強":1067,"手":-1519,"揺":-1033,"政":1522,"文":-1355,"新":-1682,"日":-1815,"明":-1462,"最":-630,"朝":-1843,"本":-1650,"東":-931,"果":-665,"次":-2378,"民":-180,"気":-1740,"理":752,"発":529,"目":-1584,"相":-242,"県":-1165,"立":-763,"第":810,"米":509,"自":-1353,"行":838,"西":-744,"見":-3874,"調":1010,"議":1198,"込":3041,"開":1758,"間":-1257,"「":-645,"」":3145,"ッ":831,"ア":-587,"カ":306,"キ":568},this.UW3__={",":4889,1:-800,"−":-1723,"、":4889,"々":-2311,"〇":5827,"」":2670,"〓":-3573,"あ":-2696,"い":1006,"う":2342,"え":1983,"お":-4864,"か":-1163,"が":3271,"く":1004,"け":388,"げ":401,"こ":-3552,"ご":-3116,"さ":-1058,"し":-395,"す":584,"せ":3685,"そ":-5228,"た":842,"ち":-521,"っ":-1444,"つ":-1081,"て":6167,"で":2318,"と":1691,"ど":-899,"な":-2788,"に":2745,"の":4056,"は":4555,"ひ":-2171,"ふ":-1798,"へ":1199,"ほ":-5516,"ま":-4384,"み":-120,"め":1205,"も":2323,"や":-788,"よ":-202,"ら":727,"り":649,"る":5905,"れ":2773,"わ":-1207,"を":6620,"ん":-518,"ア":551,"グ":1319,"ス":874,"ッ":-1350,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278,"・":-3794,"一":-1619,"下":-1759,"世":-2087,"両":3815,"中":653,"主":-758,"予":-1193,"二":974,"人":2742,"今":792,"他":1889,"以":-1368,"低":811,"何":4265,"作":-361,"保":-2439,"元":4858,"党":3593,"全":1574,"公":-3030,"六":755,"共":-1880,"円":5807,"再":3095,"分":457,"初":2475,"別":1129,"前":2286,"副":4437,"力":365,"動":-949,"務":-1872,"化":1327,"北":-1038,"区":4646,"千":-2309,"午":-783,"協":-1006,"口":483,"右":1233,"各":3588,"合":-241,"同":3906,"和":-837,"員":4513,"国":642,"型":1389,"場":1219,"外":-241,"妻":2016,"学":-1356,"安":-423,"実":-1008,"家":1078,"小":-513,"少":-3102,"州":1155,"市":3197,"平":-1804,"年":2416,"広":-1030,"府":1605,"度":1452,"建":-2352,"当":-3885,"得":1905,"思":-1291,"性":1822,"戸":-488,"指":-3973,"政":-2013,"教":-1479,"数":3222,"文":-1489,"新":1764,"日":2099,"旧":5792,"昨":-661,"時":-1248,"曜":-951,"最":-937,"月":4125,"期":360,"李":3094,"村":364,"東":-805,"核":5156,"森":2438,"業":484,"氏":2613,"民":-1694,"決":-1073,"法":1868,"海":-495,"無":979,"物":461,"特":-3850,"生":-273,"用":914,"町":1215,"的":7313,"直":-1835,"省":792,"県":6293,"知":-1528,"私":4231,"税":401,"立":-960,"第":1201,"米":7767,"系":3066,"約":3663,"級":1384,"統":-4229,"総":1163,"線":1255,"者":6457,"能":725,"自":-2869,"英":785,"見":1044,"調":-562,"財":-733,"費":1777,"車":1835,"軍":1375,"込":-1504,"通":-1136,"選":-681,"郎":1026,"郡":4404,"部":1200,"金":2163,"長":421,"開":-1432,"間":1302,"関":-1282,"雨":2009,"電":-1045,"非":2066,"駅":1620,"1":-800,"」":2670,"・":-3794,"ッ":-1350,"ア":551,"グ":1319,"ス":874,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278},this.UW4__={",":3930,".":3508,"―":-4841,"、":3930,"。":3508,"〇":4999,"「":1895,"」":3798,"〓":-5156,"あ":4752,"い":-3435,"う":-640,"え":-2514,"お":2405,"か":530,"が":6006,"き":-4482,"ぎ":-3821,"く":-3788,"け":-4376,"げ":-4734,"こ":2255,"ご":1979,"さ":2864,"し":-843,"じ":-2506,"す":-731,"ず":1251,"せ":181,"そ":4091,"た":5034,"だ":5408,"ち":-3654,"っ":-5882,"つ":-1659,"て":3994,"で":7410,"と":4547,"な":5433,"に":6499,"ぬ":1853,"ね":1413,"の":7396,"は":8578,"ば":1940,"ひ":4249,"び":-4134,"ふ":1345,"へ":6665,"べ":-744,"ほ":1464,"ま":1051,"み":-2082,"む":-882,"め":-5046,"も":4169,"ゃ":-2666,"や":2795,"ょ":-1544,"よ":3351,"ら":-2922,"り":-9726,"る":-14896,"れ":-2613,"ろ":-4570,"わ":-1783,"を":13150,"ん":-2352,"カ":2145,"コ":1789,"セ":1287,"ッ":-724,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637,"・":-4371,"ー":-11870,"一":-2069,"中":2210,"予":782,"事":-190,"井":-1768,"人":1036,"以":544,"会":950,"体":-1286,"作":530,"側":4292,"先":601,"党":-2006,"共":-1212,"内":584,"円":788,"初":1347,"前":1623,"副":3879,"力":-302,"動":-740,"務":-2715,"化":776,"区":4517,"協":1013,"参":1555,"合":-1834,"和":-681,"員":-910,"器":-851,"回":1500,"国":-619,"園":-1200,"地":866,"場":-1410,"塁":-2094,"士":-1413,"多":1067,"大":571,"子":-4802,"学":-1397,"定":-1057,"寺":-809,"小":1910,"屋":-1328,"山":-1500,"島":-2056,"川":-2667,"市":2771,"年":374,"庁":-4556,"後":456,"性":553,"感":916,"所":-1566,"支":856,"改":787,"政":2182,"教":704,"文":522,"方":-856,"日":1798,"時":1829,"最":845,"月":-9066,"木":-485,"来":-442,"校":-360,"業":-1043,"氏":5388,"民":-2716,"気":-910,"沢":-939,"済":-543,"物":-735,"率":672,"球":-1267,"生":-1286,"産":-1101,"田":-2900,"町":1826,"的":2586,"目":922,"省":-3485,"県":2997,"空":-867,"立":-2112,"第":788,"米":2937,"系":786,"約":2171,"経":1146,"統":-1169,"総":940,"線":-994,"署":749,"者":2145,"能":-730,"般":-852,"行":-792,"規":792,"警":-1184,"議":-244,"谷":-1e3,"賞":730,"車":-1481,"軍":1158,"輪":-1433,"込":-3370,"近":929,"道":-1291,"選":2596,"郎":-4866,"都":1192,"野":-1100,"銀":-2213,"長":357,"間":-2344,"院":-2297,"際":-2604,"電":-878,"領":-1659,"題":-792,"館":-1984,"首":1749,"高":2120,"「":1895,"」":3798,"・":-4371,"ッ":-724,"ー":-11870,"カ":2145,"コ":1789,"セ":1287,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637},this.UW5__={",":465,".":-299,1:-514,E2:-32768,"]":-2762,"、":465,"。":-299,"「":363,"あ":1655,"い":331,"う":-503,"え":1199,"お":527,"か":647,"が":-421,"き":1624,"ぎ":1971,"く":312,"げ":-983,"さ":-1537,"し":-1371,"す":-852,"だ":-1186,"ち":1093,"っ":52,"つ":921,"て":-18,"で":-850,"と":-127,"ど":1682,"な":-787,"に":-1224,"の":-635,"は":-578,"べ":1001,"み":502,"め":865,"ゃ":3350,"ょ":854,"り":-208,"る":429,"れ":504,"わ":419,"を":-1264,"ん":327,"イ":241,"ル":451,"ン":-343,"中":-871,"京":722,"会":-1153,"党":-654,"務":3519,"区":-901,"告":848,"員":2104,"大":-1296,"学":-548,"定":1785,"嵐":-1304,"市":-2991,"席":921,"年":1763,"思":872,"所":-814,"挙":1618,"新":-1682,"日":218,"月":-4353,"査":932,"格":1356,"機":-1508,"氏":-1347,"田":240,"町":-3912,"的":-3149,"相":1319,"省":-1052,"県":-4003,"研":-997,"社":-278,"空":-813,"統":1955,"者":-2233,"表":663,"語":-1073,"議":1219,"選":-1018,"郎":-368,"長":786,"間":1191,"題":2368,"館":-689,"1":-514,"E2":-32768,"「":363,"イ":241,"ル":451,"ン":-343},this.UW6__={",":227,".":808,1:-270,E1:306,"、":227,"。":808,"あ":-307,"う":189,"か":241,"が":-73,"く":-121,"こ":-200,"じ":1782,"す":383,"た":-428,"っ":573,"て":-1014,"で":101,"と":-105,"な":-253,"に":-149,"の":-417,"は":-236,"も":-206,"り":187,"る":-135,"を":195,"ル":-673,"ン":-496,"一":-277,"中":201,"件":-800,"会":624,"前":302,"区":1792,"員":-1212,"委":798,"学":-960,"市":887,"広":-695,"後":535,"業":-697,"相":753,"社":-507,"福":974,"空":-822,"者":1811,"連":463,"郎":1082,"1":-270,"E1":306,"ル":-673,"ン":-496},this}t.prototype.ctype_=function(_){for(var t in this.chartype_)if(_.match(this.chartype_[t][0]))return this.chartype_[t][1];return"O"},t.prototype.ts_=function(_){return _||0},t.prototype.segment=function(_){if(null==_||null==_||""==_)return[];var t=[],H=["B3","B2","B1"],s=["O","O","O"],h=_.split("");for(K=0;K0&&(t.push(i),i="",N="B"),I=O,O=B,B=N,i+=H[K]}return t.push(i),t},_.TinySegmenter=t}})); \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/vendor.77e55a48.min.js b/docs/v3/v2/assets/javascripts/vendor.77e55a48.min.js deleted file mode 100644 index 4b293bef1..000000000 --- a/docs/v3/v2/assets/javascripts/vendor.77e55a48.min.js +++ /dev/null @@ -1,30 +0,0 @@ -(window.webpackJsonp=window.webpackJsonp||[]).push([[1],[,function(t,e,n){"use strict";function r(t){return"function"==typeof(null==t?void 0:t.lift)}function i(t){return e=>{if(r(e))return e.lift((function(e){try{return t(e,this)}catch(t){this.error(t)}}));throw new TypeError("Unable to lift unknown Observable type")}}n.d(e,"a",(function(){return r})),n.d(e,"b",(function(){return i}))},,function(t,e,n){"use strict";n.d(e,"a",(function(){return i}));var r=n(25);class i extends r.b{constructor(t,e,n,r,i){super(t),this.onUnsubscribe=i,e&&(this._next=function(t){try{e(t)}catch(t){this.error(t)}}),n&&(this._error=function(t){try{n(t)}catch(t){this.destination.error(t)}this.unsubscribe()}),r&&(this._complete=function(){try{r()}catch(t){this.destination.error(t)}this.unsubscribe()})}unsubscribe(){var t;!this.closed&&(null===(t=this.onUnsubscribe)||void 0===t||t.call(this)),super.unsubscribe()}}},,,function(t,e,n){"use strict";n.d(e,"a",(function(){return a}));var r=n(25),i=n(7),o=n(13),s=n(33),c=n(17),u=n(29);class a{constructor(t){t&&(this._subscribe=t)}lift(t){const e=new a;return e.source=this,e.operator=t,e}subscribe(t,e,n){const o=(s=t)&&s instanceof r.b||function(t){return t&&"function"==typeof t.next&&"function"==typeof t.error&&"function"==typeof t.complete}(s)&&Object(i.c)(s)?t:new r.a(t,e,n);var s;const{operator:u,source:a}=this;return o.add(u?u.call(o,a):a||c.a.useDeprecatedSynchronousErrorHandling?this._subscribe(o):this._trySubscribe(o)),o}_trySubscribe(t){try{return this._subscribe(t)}catch(e){if(c.a.useDeprecatedSynchronousErrorHandling)throw e;!function(t){for(;t;){const{closed:e,destination:n,isStopped:i}=t;if(e||i)return!1;t=n&&n instanceof r.b?n:null}return!0}(t)?Object(u.a)(e):t.error(e)}}forEach(t,e){return new(e=l(e))((e,n)=>{let r;r=this.subscribe(e=>{try{t(e)}catch(t){n(t),null==r||r.unsubscribe()}},n,e)})}_subscribe(t){var e;return null===(e=this.source)||void 0===e?void 0:e.subscribe(t)}[o.a](){return this}pipe(...t){return t.length?Object(s.b)(t)(this):this}toPromise(t){return new(t=l(t))((t,e)=>{let n;this.subscribe(t=>n=t,t=>e(t),()=>t(n))})}}function l(t){var e;return null!==(e=null!=t?t:c.a.Promise)&&void 0!==e?e:Promise}a.create=t=>new a(t)},function(t,e,n){"use strict";n.d(e,"b",(function(){return u})),n.d(e,"a",(function(){return a})),n.d(e,"c",(function(){return l}));var r=n(21),i=n(37);const o=Object(i.a)(t=>function(e){t(this),this.message=e?`${e.length} errors occurred during unsubscription:\n${e.map((t,e)=>`${e+1}) ${t.toString()}`).join("\n ")}`:"",this.name="UnsubscriptionError",this.errors=e});var s,c=n(16);class u{constructor(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._teardowns=null}unsubscribe(){let t;if(!this.closed){this.closed=!0;const{_parentage:e}=this;if(Array.isArray(e))for(const t of e)t.remove(this);else null==e||e.remove(this);const{initialTeardown:n}=this;if(Object(r.a)(n))try{n()}catch(e){t=e instanceof o?e.errors:[e]}const{_teardowns:i}=this;if(i){this._teardowns=null;for(const e of i)try{f(e)}catch(e){t=null!=t?t:[],e instanceof o?t=[...t,...e.errors]:t.push(e)}}if(t)throw new o(t)}}add(t){var e;if(t&&t!==this)if(this.closed)f(t);else{if(t instanceof u){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._teardowns=null!==(e=this._teardowns)&&void 0!==e?e:[]).push(t)}}_hasParent(t){const{_parentage:e}=this;return e===t||Array.isArray(e)&&e.includes(t)}_addParent(t){const{_parentage:e}=this;this._parentage=Array.isArray(e)?(e.push(t),e):e?[e,t]:t}_removeParent(t){const{_parentage:e}=this;e===t?this._parentage=null:Array.isArray(e)&&Object(c.a)(e,t)}remove(t){const{_teardowns:e}=this;e&&Object(c.a)(e,t),t instanceof u&&t._removeParent(this)}}u.EMPTY=((s=new u).closed=!0,s);const a=u.EMPTY;function l(t){return t instanceof u||t&&"closed"in t&&"function"==typeof t.remove&&"function"==typeof t.add&&"function"==typeof t.unsubscribe}function f(t){"function"==typeof t?t():t.unsubscribe()}},,function(t,e,n){"use strict";n.d(e,"a",(function(){return m}));var r=n(40),i=n(29);const o="function"==typeof Symbol&&Symbol.iterator?Symbol.iterator:"@@iterator";var s=n(13);var c=n(30);function u(t){return!!t&&"function"!=typeof t.subscribe&&"function"==typeof t.then}function a(t,e,n,r){return new(n||(n=Promise))((function(i,o){function s(t){try{u(r.next(t))}catch(t){o(t)}}function c(t){try{u(r.throw(t))}catch(t){o(t)}}function u(t){var e;t.done?i(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(s,c)}u((r=r.apply(t,e||[])).next())}))}Object.create;function l(t){var e="function"==typeof Symbol&&Symbol.iterator,n=e&&t[e],r=0;if(n)return n.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&r>=t.length&&(t=void 0),{value:t&&t[r++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}function f(t){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var e,n=t[Symbol.asyncIterator];return n?n.call(t):(t=l(t),e={},r("next"),r("throw"),r("return"),e[Symbol.asyncIterator]=function(){return this},e);function r(n){e[n]=t[n]&&function(e){return new Promise((function(r,i){(function(t,e,n,r){Promise.resolve(r).then((function(e){t({value:e,done:n})}),e)})(r,i,(e=t[n](e)).done,e.value)}))}}}Object.create;function d(t){return e=>{(function(t,e){var n,r,i,o;return a(this,void 0,void 0,(function*(){try{for(n=f(t);!(r=yield n.next()).done;){const t=r.value;e.next(t)}}catch(t){i={error:t}}finally{try{r&&!r.done&&(o=n.return)&&(yield o.call(n))}finally{if(i)throw i.error}}e.complete()}))})(t,e).catch(t=>e.error(t))}}var h=n(6),b=n(7);var p=n(31);function v(t,e){if(null!=t){if(function(t){return t&&"function"==typeof t[s.a]}(t))return function(t,e){return new h.a(n=>{const r=new b.b;return r.add(e.schedule(()=>{const i=t[s.a]();r.add(i.subscribe({next(t){r.add(e.schedule(()=>n.next(t)))},error(t){r.add(e.schedule(()=>n.error(t)))},complete(){r.add(e.schedule(()=>n.complete()))}}))})),r})}(t,e);if(u(t))return function(t,e){return new h.a(n=>{const r=new b.b;return r.add(e.schedule(()=>t.then(t=>{r.add(e.schedule(()=>{n.next(t),r.add(e.schedule(()=>n.complete()))}))},t=>{r.add(e.schedule(()=>n.error(t)))}))),r})}(t,e);if(Object(c.a)(t))return Object(p.a)(t,e);if(function(t){return t&&"function"==typeof t[o]}(t)||"string"==typeof t)return function(t,e){if(!t)throw new Error("Iterable cannot be null");return new h.a(n=>{const r=new b.b;let i;return r.add(()=>{i&&"function"==typeof i.return&&i.return()}),r.add(e.schedule(()=>{i=t[o](),r.add(e.schedule((function(){if(n.closed)return;let t,e;try{const n=i.next();t=n.value,e=n.done}catch(t){return void n.error(t)}e?n.complete():(n.next(t),this.schedule())})))})),r})}(t,e);if(Symbol&&Symbol.asyncIterator&&"function"==typeof t[Symbol.asyncIterator])return function(t,e){if(!t)throw new Error("Iterable cannot be null");return new h.a(n=>{const r=new b.b;return r.add(e.schedule(()=>{const i=t[Symbol.asyncIterator]();r.add(e.schedule((function(){i.next().then(t=>{t.done?n.complete():(n.next(t.value),this.schedule())})})))})),r})}(t,e)}throw new TypeError((null!==t&&typeof t||t)+" is not observable")}function m(t,e){return e?v(t,e):t instanceof h.a?t:new h.a(function(t){if(t&&"function"==typeof t[s.a])return l=t,t=>{const e=l[s.a]();if("function"!=typeof e.subscribe)throw new TypeError("Provided object does not correctly implement Symbol.observable");return e.subscribe(t)};if(Object(c.a)(t))return Object(r.a)(t);if(u(t))return a=t,t=>(a.then(e=>{t.closed||(t.next(e),t.complete())},e=>t.error(e)).then(null,i.a),t);if(t&&"function"==typeof t[o])return n=t,t=>{const e=n[o]();for(;;){let n;try{n=e.next()}catch(e){return void t.error(e)}if(n.done){t.complete();break}if(t.next(n.value),t.closed)break}return"function"==typeof e.return&&t.add(()=>{e.return&&e.return()}),t};if(Symbol&&Symbol.asyncIterator&&t&&"function"==typeof t[Symbol.asyncIterator])return d(t);{const n=null!==(e=t)&&"object"==typeof e?"an invalid object":`'${t}'`;throw new TypeError(`You provided ${n} where a stream was expected. You can provide an Observable, Promise, Array, AsyncIterable, or Iterable.`)}var e; -/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -***************************************************************************** */var n;var a;var l}(t))}},,function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var r=n(1),i=n(3);function o(t,e){return Object(r.b)((n,r)=>{let o=0;n.subscribe(new i.a(r,n=>{r.next(t.call(e,n,o++))}))})}},function(t,e,n){"use strict";function r(t){return t}n.d(e,"a",(function(){return r}))},function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));const r="function"==typeof Symbol&&Symbol.observable||"@@observable"},function(t,e,n){"use strict";function r(t){return t&&"function"==typeof t.schedule}n.d(e,"a",(function(){return r}))},,function(t,e,n){"use strict";function r(t,e){if(t){const n=t.indexOf(e);0<=n&&t.splice(n,1)}}n.d(e,"a",(function(){return r}))},function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));const r={onUnhandledError:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1}},function(t,e,n){"use strict";function r(){}n.d(e,"a",(function(){return r}))},,function(t,e,n){"use strict";n.d(e,"a",(function(){return s}));var r=n(9),i=n(1),o=n(3);function s(t,e){return Object(i.b)((n,i)=>{let s=null,c=0,u=!1;const a=()=>u&&!s&&i.complete();n.subscribe(new o.a(i,n=>{null==s||s.unsubscribe();let u=0,l=c++;Object(r.a)(t(n,l)).subscribe(s=new o.a(i,t=>i.next(e?e(n,t,l,u++):t),void 0,()=>{s=null,a()}))},void 0,()=>{u=!0,a()}))})}},function(t,e,n){"use strict";function r(t){return"function"==typeof t}n.d(e,"a",(function(){return r}))},function(t,e,n){"use strict";n.d(e,"a",(function(){return s}));var r=n(6),i=n(40),o=n(31);function s(t,e){return e?Object(o.a)(t,e):new r.a(Object(i.a)(t))}},,,function(t,e,n){"use strict";n.d(e,"b",(function(){return u})),n.d(e,"a",(function(){return a}));var r=n(21),i=n(7),o=n(17),s=n(29),c=n(18);class u extends i.b{constructor(t){super(),this.isStopped=!1,t?(this.destination=t,Object(i.c)(t)&&t.add(this)):this.destination=f}static create(t,e,n){return new a(t,e,n)}next(t){this.isStopped||this._next(t)}error(t){this.isStopped||(this.isStopped=!0,this._error(t))}complete(){this.isStopped||(this.isStopped=!0,this._complete())}unsubscribe(){this.closed||(this.isStopped=!0,super.unsubscribe())}_next(t){this.destination.next(t)}_error(t){this.destination.error(t),this.unsubscribe()}_complete(){this.destination.complete(),this.unsubscribe()}}class a extends u{constructor(t,e,n){if(super(),this.destination=f,(t||e||n)&&t!==f){let i;if(Object(r.a)(t))i=t;else if(t){let r;({next:i,error:e,complete:n}=t),this&&o.a.useDeprecatedNextContext?(r=Object.create(t),r.unsubscribe=()=>this.unsubscribe()):r=t,i=null==i?void 0:i.bind(r),e=null==e?void 0:e.bind(r),n=null==n?void 0:n.bind(r)}this.destination={next:i||c.a,error:e||l,complete:n||c.a}}}}function l(t){if(o.a.useDeprecatedSynchronousErrorHandling)throw t;Object(s.a)(t)}const f={closed:!0,next:c.a,error:l,complete:c.a}},function(t,e,n){"use strict";n.d(e,"a",(function(){return i}));var r=n(6);const i=new r.a(t=>t.complete())},,function(t,e,n){"use strict";n.d(e,"a",(function(){return c}));var r=n(11),i=n(9),o=n(1),s=n(3);function c(t,e,n=1/0){return"function"==typeof e?o=>o.pipe(c((n,o)=>Object(i.a)(t(n,o)).pipe(Object(r.a)((t,r)=>e(n,t,o,r))),n)):("number"==typeof e&&(n=e),Object(o.b)((e,r)=>{let o=!1,c=0,u=0,a=[];const l=()=>o&&!c&&r.complete(),f=e=>{c++,r.add(Object(i.a)(t(e,u++)).subscribe(new s.a(r,t=>r.next(t),void 0,()=>{c--,a.length&&(()=>{for(;c0;)f(a.shift())})(),l()})))};let d;return d=e.subscribe(new s.a(r,t=>c{o=!0,l(),null==d||d.unsubscribe()})),()=>{a=null}}))}},function(t,e,n){"use strict";n.d(e,"a",(function(){return i}));var r=n(17);function i(t){setTimeout(()=>{const{onUnhandledError:e}=r.a;if(!e)throw t;e(t)})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));const r=t=>t&&"number"==typeof t.length&&"function"!=typeof t},function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var r=n(6),i=n(7);function o(t,e){return new r.a(n=>{const r=new i.b;let o=0;return r.add(e.schedule((function(){o!==t.length?(n.next(t[o++]),n.closed||r.add(this.schedule())):n.complete()}))),r})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return u}));var r=n(6),i=n(7),o=n(37);const s=Object(o.a)(t=>function(){t(this),this.message="object unsubscribed"});var c=n(16);class u extends r.a{constructor(){super(),this.observers=[],this.closed=!1,this.isStopped=!1,this.hasError=!1,this.thrownError=null}lift(t){const e=new a(this,this);return e.operator=t,e}_throwIfClosed(){if(this.closed)throw new s}next(t){if(this._throwIfClosed(),!this.isStopped){const e=this.observers.slice();for(const n of e)n.next(t)}}error(t){if(this._throwIfClosed(),!this.isStopped){this.hasError=this.isStopped=!0,this.thrownError=t;const{observers:e}=this;for(;e.length;)e.shift().error(t)}}complete(){if(this._throwIfClosed(),!this.isStopped){this.isStopped=!0;const{observers:t}=this;for(;t.length;)t.shift().complete()}}unsubscribe(){this.isStopped=this.closed=!0,this.observers=null}_trySubscribe(t){return this._throwIfClosed(),super._trySubscribe(t)}_subscribe(t){return this._throwIfClosed(),this._checkFinalizedStatuses(t),this._innerSubscribe(t)}_innerSubscribe(t){const{hasError:e,isStopped:n,observers:r}=this;return e||n?i.a:(r.push(t),new i.b(()=>Object(c.a)(this.observers,t)))}_checkFinalizedStatuses(t){const{hasError:e,thrownError:n,isStopped:r}=this;e?t.error(n):r&&t.complete()}asObservable(){const t=new r.a;return t.source=this,t}}u.create=(t,e)=>new a(t,e);class a extends u{constructor(t,e){super(),this.destination=t,this.source=e}next(t){var e,n;null===(n=null===(e=this.destination)||void 0===e?void 0:e.next)||void 0===n||n.call(e,t)}error(t){var e,n;null===(n=null===(e=this.destination)||void 0===e?void 0:e.error)||void 0===n||n.call(e,t)}complete(){var t,e;null===(e=null===(t=this.destination)||void 0===t?void 0:t.complete)||void 0===e||e.call(t)}_subscribe(t){var e,n;return null!==(n=null===(e=this.source)||void 0===e?void 0:e.subscribe(t))&&void 0!==n?n:i.a}}},function(t,e,n){"use strict";n.d(e,"a",(function(){return i})),n.d(e,"b",(function(){return o}));var r=n(12);function i(...t){return o(t)}function o(t){return 0===t.length?r.a:1===t.length?t[0]:function(e){return t.reduce((t,e)=>e(t),e)}}},,,function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var r=n(1),i=n(3);function o(t,e){return t=null!=t?t:s,Object(r.b)((n,r)=>{let o,s=!0;n.subscribe(new i.a(r,n=>{(s&&(o=n,1)||!t(o,o=e?e(n):n))&&r.next(n),s=!1}))})}function s(t,e){return t===e}},function(t,e,n){"use strict";function r(t){const e=t(t=>{Error.call(t),t.name=t.constructor.name,t.stack=(new Error).stack});return e.prototype=Object.create(Error.prototype),e.prototype.constructor=e,e}n.d(e,"a",(function(){return r}))},function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));const r={now:()=>(r.delegate||Date).now(),delegate:void 0}},function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var r=n(11);const{isArray:i}=Array;function o(t){return Object(r.a)(e=>function(t,e){return i(e)?t(...e):t(e)}(t,e))}},function(t,e,n){"use strict";n.d(e,"a",(function(){return r}));const r=t=>e=>{for(let n=0,r=t.length;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=i()(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=i()(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),document.activeElement.blur(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":o(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}(),u=n(1),a=n.n(u),l=n(2),f=n.n(l),d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},h=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===d(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=f()(t,"click",(function(t){return e.onClick(t)}))}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new c({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return p("action",t)}},{key:"defaultTarget",value:function(t){var e=p("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return p("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach((function(t){n=n&&!!document.queryCommandSupported(t)})),n}}]),e}(a.a);function p(t,e){var n="data-clipboard-"+t;if(e.hasAttribute(n))return e.getAttribute(n)}e.default=b}]).default},t.exports=r()},function(t,e,n){"use strict";n.d(e,"a",(function(){return s}));var r=n(14),i=n(22),o=n(31);function s(...t){let e=t[t.length-1];return Object(r.a)(e)?(t.pop(),Object(o.a)(t,e)):Object(i.a)(t)}},function(t,e,n){"use strict";n.d(e,"a",(function(){return b}));var r=n(6),i=n(14);const{isArray:o}=Array,{getPrototypeOf:s,prototype:c,keys:u}=Object;function a(t){if(1===t.length){const n=t[0];if(o(n))return{args:n,keys:null};if((e=n)&&"object"==typeof e&&s(e)===c){const t=u(n);return{args:t.map(t=>n[t]),keys:t}}}var e;return{args:t,keys:null}}var l=n(25),f=n(9),d=n(12),h=n(39);function b(...t){let e=void 0,n=void 0;Object(i.a)(t[t.length-1])&&(n=t.pop()),"function"==typeof t[t.length-1]&&(e=t.pop());const{args:o,keys:s}=a(t),c=new r.a(function(t,e,n=d.a){return r=>{v(e,()=>{const{length:i}=t,o=new Array(i);let s=i;const c=t.map(()=>!1);let u=!0;for(let a=0;a{Object(f.a)(t[a],e).subscribe(new p(r,t=>{o[a]=t,u&&(c[a]=!0,u=!c.every(d.a)),u||r.next(n(o.slice()))},()=>0==--s))},r)}},r)}}(o,n,s?t=>{const e={};for(let n=0;n0},t.prototype.connect_=function(){r&&!this.connected_&&(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),c?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},t.prototype.disconnect_=function(){r&&this.connected_&&(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},t.prototype.onTransitionEnd_=function(t){var e=t.propertyName,n=void 0===e?"":e;s.some((function(t){return!!~n.indexOf(t)}))&&this.refresh()},t.getInstance=function(){return this.instance_||(this.instance_=new t),this.instance_},t.instance_=null,t}(),a=function(t,e){for(var n=0,r=Object.keys(e);n0},t}(),_="undefined"!=typeof WeakMap?new WeakMap:new n,E=function t(e){if(!(this instanceof t))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var n=u.getInstance(),r=new g(e,n,this);_.set(this,r)};["observe","unobserve","disconnect"].forEach((function(t){E.prototype[t]=function(){var e;return(e=_.get(this))[t].apply(e,arguments)}}));var O=void 0!==i.ResizeObserver?i.ResizeObserver:E;e.a=O}).call(this,n(70))},function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var r=n(6),i=n(9);function o(t){return new r.a(e=>{let n;try{n=t()}catch(t){return void e.error(t)}return Object(i.a)(n).subscribe(e)})}},function(t,e,n){"use strict"; -/*! - * escape-html - * Copyright(c) 2012-2013 TJ Holowaychuk - * Copyright(c) 2015 Andreas Lubbe - * Copyright(c) 2015 Tiancheng "Timothy" Gu - * MIT Licensed - */var r=/["'&<>]/;t.exports=function(t){var e,n=""+t,i=r.exec(n);if(!i)return n;var o="",s=0,c=0;for(s=i.index;s{e.subscribe(n),n.add(t)})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return c}));var r=n(21),i=n(1),o=n(3),s=n(12);function c(t,e,n){const c=Object(r.a)(t)||e||n?{next:t,error:e,complete:n}:t;return c?Object(i.b)((t,e)=>{t.subscribe(new o.a(e,t=>{var n;null===(n=c.next)||void 0===n||n.call(c,t),e.next(t)},t=>{var n;null===(n=c.error)||void 0===n||n.call(c,t),e.error(t)},()=>{var t;null===(t=c.complete)||void 0===t||t.call(c),e.complete()}))}):s.a}},function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var r=n(1),i=n(3);function o(t,e){const n=arguments.length>=2;return Object(r.b)((r,o)=>{let s=n,c=e,u=0;r.subscribe(new i.a(o,e=>{const n=u++;o.next(c=s?t(c,e,n):(s=!0,e))}))})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var r=n(1),i=n(3);function o(t,e=0){return Object(r.b)((n,r)=>{n.subscribe(new i.a(r,n=>r.add(t.schedule(()=>r.next(n),e)),n=>r.add(t.schedule(()=>r.error(n),e)),()=>r.add(t.schedule(()=>r.complete(),e))))})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return a}));var r=n(43),i=n(7);const o={schedule(t){let e=requestAnimationFrame,n=cancelAnimationFrame;const{delegate:r}=o;r&&(e=r.requestAnimationFrame,n=r.cancelAnimationFrame);const s=e(e=>{n=void 0,t(e)});return new i.b(()=>null==n?void 0:n(s))},requestAnimationFrame(...t){const{delegate:e}=o;return((null==e?void 0:e.requestAnimationFrame)||requestAnimationFrame)(...t)},cancelAnimationFrame(...t){const{delegate:e}=o;return((null==e?void 0:e.cancelAnimationFrame)||cancelAnimationFrame)(...t)},delegate:void 0};class s extends r.a{constructor(t,e){super(t,e),this.scheduler=t,this.work=e}requestAsyncId(t,e,n=0){return null!==n&&n>0?super.requestAsyncId(t,e,n):(t.actions.push(this),t.scheduled||(t.scheduled=o.requestAnimationFrame(()=>t.flush(void 0))))}recycleAsyncId(t,e,n=0){if(null!=n&&n>0||null==n&&this.delay>0)return super.recycleAsyncId(t,e,n);0===t.actions.length&&(o.cancelAnimationFrame(e),t.scheduled=void 0)}}var c=n(44);class u extends c.a{flush(t){this.active=!0,this.scheduled=void 0;const{actions:e}=this;let n,r=-1;t=t||e.shift();let i=e.length;do{if(n=t.execute(t.state,t.delay))break}while(++r{let l;c++,o?l=o.subscribe(a):(o=new r.a(t,e,i),l=o.subscribe(a),s=u.subscribe({next(t){o.next(t)},error(t){const e=o;s=void 0,o=void 0,e.error(t)},complete(){s=void 0,o.complete()}}),s.closed&&(s=void 0)),a.add(()=>{c--,l.unsubscribe(),n&&0===c&&s&&(s.unsubscribe(),s=void 0,o=void 0)})}}(o))}},function(t,e,n){"use strict";n.d(e,"a",(function(){return i}));var r=n(36);function i(t,e){return Object(r.a)((n,r)=>e?e(n[t],r[t]):n[t]===r[t])}},function(t,e,n){"use strict";n.d(e,"a",(function(){return u}));var r=n(1),i=n(3),o=n(9),s=n(12),c=n(18);function u(...t){let e;return"function"==typeof t[t.length-1]&&(e=t.pop()),Object(r.b)((n,r)=>{const u=t.length,a=new Array(u);let l=t.map(()=>!1),f=!1;n.subscribe(new i.a(r,t=>{if(f){const n=[t,...a];r.next(e?e(...n):n)}}));for(let e=0;e{a[e]=t,f||l[e]||(l[e]=!0,(f=l.every(s.a))&&(l=null))},void 0,c.a))}})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return s}));var r=n(1),i=n(3),o=n(16);function s(t,e=null){return e=null!=e?e:t,Object(r.b)((n,r)=>{let s=[],c=0;n.subscribe(new i.a(r,n=>{let i=null;c++%e==0&&s.push([]);for(const e of s)e.push(n),t<=e.length&&(i=null!=i?i:[],i.push(e));if(i)for(const t of i)Object(o.a)(s,t),r.next(t)},void 0,()=>{for(const t of s)r.next(t);r.complete()},()=>{s=null}))})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return c}));var r=n(41);var i=n(14),o=n(22);function s(...t){let e;return Object(i.a)(t[t.length-1])&&(e=t.pop()),Object(r.a)(1)(Object(o.a)(t,e))}function c(...t){const e=t[t.length-1];return Object(i.a)(e)?(t.pop(),n=>s(t,n,e)):e=>s(t,e)}},function(t,e,n){"use strict";n.d(e,"a",(function(){return a}));var r=n(6),i=n(28),o=n(30),s=n(21),c=n(39),u=n(22);function a(t,e,n,l){return Object(s.a)(n)&&(l=n,n=void 0),l?a(t,e,n).pipe(Object(c.a)(l)):new r.a(r=>{const s=(...t)=>r.next(t.length>1?t:t[0]);return(c=t)&&"function"==typeof c.addEventListener&&"function"==typeof c.removeEventListener?(t.addEventListener(e,s,n),()=>t.removeEventListener(e,s,n)):function(t){return t&&"function"==typeof t.on&&"function"==typeof t.off}(t)?(t.on(e,s),()=>t.off(e,s)):function(t){return t&&"function"==typeof t.addListener&&"function"==typeof t.removeListener}(t)?(t.addListener(e,s),()=>t.removeListener(e,s)):Object(o.a)(t)?Object(i.a)(t=>a(t,e,n))(Object(u.a)(t)).subscribe(r):void r.error(new TypeError("Invalid event target"));var c})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var r=n(1),i=n(3);function o(t){return Object(r.b)((e,n)=>{e.subscribe(new i.a(n,()=>n.next(t)))})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var r=n(6),i=n(18);const o=new r.a(i.a)},function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var r=n(1),i=n(3);function o(t,e){return Object(r.b)((n,r)=>{let o=0;n.subscribe(new i.a(r,n=>t.call(e,n,o++)&&r.next(n)))})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return i}));var r=n(32);class i extends r.a{constructor(t){super(),this._value=t}get value(){return this.getValue()}_subscribe(t){const e=super._subscribe(t);return!e.closed&&t.next(this._value),e}getValue(){const{hasError:t,thrownError:e,_value:n}=this;if(t)throw e;return this._throwIfClosed(),n}next(t){super.next(this._value=t)}}},function(t,e,n){"use strict";n.d(e,"a",(function(){return s}));var r=n(26),i=n(1),o=n(3);function s(t){return t<=0?()=>r.a:Object(i.b)((e,n)=>{let r=0;e.subscribe(new o.a(n,e=>{++r<=t&&(n.next(e),t<=r&&n.complete())}))})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return c}));var r=n(1),i=n(3),o=n(9);const s={leading:!0,trailing:!1};function c(t,{leading:e,trailing:n}=s){return Object(r.b)((r,s)=>{let c=!1,u=null,a=null;const l=()=>{null==a||a.unsubscribe(),a=null,n&&d()},f=e=>a=Object(o.a)(t(e)).subscribe(new i.a(s,l,void 0,l)),d=()=>{c&&(s.next(u),f(u)),c=!1,u=null};r.subscribe(new i.a(s,t=>{c=!0,u=t,!a&&(e?d():f(t))}))})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return i}));var r=n(20);function i(t,e){return e?Object(r.a)(()=>t,e):Object(r.a)(()=>t)}},function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var r=n(1),i=n(3);function o(t){return Object(r.b)((e,n)=>{let r=!1,o=null;e.subscribe(new i.a(n,t=>{r=!0,o=t}));const s=()=>{if(r){r=!1;const t=o;o=null,n.next(t)}};t.subscribe(new i.a(n,s,void 0,s))})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var r=n(1),i=n(3);function o(t){return Object(r.b)((e,n)=>{let r=0;e.subscribe(new i.a(n,e=>t===r?n.next(e):r++))})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return s}));var r=n(9),i=n(3),o=n(1);function s(t){return Object(o.b)((e,n)=>{let o,c=null,u=!1;c=e.subscribe(new i.a(n,void 0,i=>{o=Object(r.a)(t(i,s(t)(e))),c?(c.unsubscribe(),c=null,o.subscribe(n)):u=!0})),u&&(c.unsubscribe(),c=null,o.subscribe(n))})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return s}));var r=n(42),i=n(1),o=n(3);function s(t,e=r.a){return Object(i.b)((n,r)=>{let i=!1,s=null,c=null;const u=()=>{i=!1;const t=s;s=null,r.next(t)};n.subscribe(new o.a(r,n=>{null==c||c.unsubscribe(),i=!0,s=n,r.add(c=e.schedule(()=>{c=null,u()},t))},void 0,()=>{i&&u(),r.complete()}))})}},function(t,e,n){"use strict";n.d(e,"a",(function(){return i}));var r=n(28);function i(t,e){return"function"==typeof e?Object(r.a)(t,e,1):Object(r.a)(t,1)}},function(t,e,n){"use strict";n.d(e,"a",(function(){return o}));var r=n(62),i=n(26);function o(t,e=i.a,n=i.a){return Object(r.a)(()=>t()?e:n)}},function(t,e,n){"use strict";n.d(e,"a",(function(){return f}));var r=n(6),i=n(7),o=n(1),s=n(3);function c(){return Object(o.b)((t,e)=>{let n=null;t._refCount++;const r=new s.a(e,void 0,void 0,void 0,()=>{if(!t||t._refCount<=0||0<--t._refCount)return void(n=null);const r=t._connection,i=n;n=null,!r||i&&r!==i||r.unsubscribe(),e.unsubscribe()});t.subscribe(r),r.closed||(n=t.connect())})}class u extends r.a{constructor(t,e){super(),this.source=t,this.subjectFactory=e,this._subject=null,this._refCount=0,this._connection=null}_subscribe(t){return this.getSubject().subscribe(t)}getSubject(){const t=this._subject;return t&&!t.isStopped||(this._subject=this.subjectFactory()),this._subject}_teardown(){this._refCount=0;const{_connection:t}=this;this._subject=this._connection=null,null==t||t.unsubscribe()}connect(){let t=this._connection;if(!t){t=this._connection=new i.b;const e=this.getSubject();t.add(this.source.subscribe(new s.a(e,void 0,t=>{this._teardown(),e.error(t)},()=>{this._teardown(),e.complete()},()=>this._teardown()))),t.closed&&(this._connection=null,t=i.b.EMPTY)}return t}refCount(){return c()(this)}}var a=n(32);function l(){return new a.a}function f(){return t=>c()(function(t,e){const n="function"==typeof t?t:()=>t;return"function"==typeof e?Object(o.b)((t,r)=>{const i=n();e(i).subscribe(r).add(t.subscribe(i))}):t=>{const e=new u(t,n);return Object(o.a)(t)&&(e.lift=t.lift),e.source=t,e.subjectFactory=n,e}}(l)(t))}},function(t,e,n){"use strict";n.d(e,"a",(function(){return u}));var r=n(6),i=n(7),o=n(9);var s=n(1);function c(...t){return Object(s.b)((e,n)=>{(function(...t){let e=void 0;return"function"==typeof t[t.length-1]&&(e=t.pop()),new r.a(n=>{const r=t.map(()=>[]),s=t.map(()=>!1),c=new i.b,u=()=>{if(r.every(t=>t.length>0)){let t=r.map(t=>t.shift());if(e)try{t=e(...t)}catch(t){return void n.error(t)}n.next(t),r.some((t,e)=>0===t.length&&s[e])&&n.complete()}};for(let e=0;!n.closed&&e{r[e].push(t),u()},error:t=>n.error(t),complete:()=>{s[e]=!0,0===r[e].length&&n.complete()}}))}return c})})(e,...t).subscribe(n)})}function u(...t){return c(...t)}},function(t,e,n){"use strict";n.d(e,"a",(function(){return a}));var r=n(14),i=n(41),o=n(22);const{isArray:s}=Array;var c=n(9),u=n(26);function a(...t){let e=1/0,n=void 0;return Object(r.a)(t[t.length-1])&&(n=t.pop()),"number"==typeof t[t.length-1]&&(e=t.pop()),(t=function(t){return 1===t.length&&s(t[0])?t[0]:t}(t)).length?1===t.length?Object(c.a)(t[0]):Object(i.a)(e)(Object(o.a)(t,n)):u.a}},function(t,e,n){"use strict";n.d(e,"a",(function(){return s}));var r=n(42);var i=n(1),o=n(3);function s(t,e=r.a){return Object(i.b)((n,r)=>{const i=(s=t)instanceof Date&&!isNaN(s);var s;let c=!1,u=0,a=i?[]:null;const l=()=>c&&!u&&!(null==a?void 0:a.length)&&r.complete();return i&&(u++,r.add(e.schedule(()=>{if(u--,a){const t=a;a=null;for(const e of t)r.next(e)}l()},+t-e.now()))),n.subscribe(new o.a(r,n=>{i?a?a.push(n):r.next(n):(u++,r.add(e.schedule(()=>{u--,r.next(n),l()},t)))},void 0,()=>{c=!0,l()})),()=>{a=null}})}}]]); -//# sourceMappingURL=vendor.77e55a48.min.js.map \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/vendor.77e55a48.min.js.map b/docs/v3/v2/assets/javascripts/vendor.77e55a48.min.js.map deleted file mode 100644 index 3fb4244ae..000000000 --- a/docs/v3/v2/assets/javascripts/vendor.77e55a48.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///./node_modules/rxjs/dist/esm/internal/util/lift.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/OperatorSubscriber.js","webpack:///./node_modules/rxjs/dist/esm/internal/Observable.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/UnsubscriptionError.js","webpack:///./node_modules/rxjs/dist/esm/internal/Subscription.js","webpack:///./node_modules/rxjs/dist/esm/internal/symbol/iterator.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/isPromise.js","webpack:///./node_modules/tslib/tslib.es6.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/subscribeToAsyncIterable.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduled/scheduled.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/isInteropObservable.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduled/scheduleObservable.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduled/schedulePromise.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/isIterable.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduled/scheduleIterable.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduled/scheduleAsyncIterable.js","webpack:///./node_modules/rxjs/dist/esm/internal/observable/from.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/subscribeToObservable.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/subscribeToPromise.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/subscribeToIterable.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/isObject.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/map.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/identity.js","webpack:///./node_modules/rxjs/dist/esm/internal/symbol/observable.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/isScheduler.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/arrRemove.js","webpack:///./node_modules/rxjs/dist/esm/internal/config.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/noop.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/switchMap.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/isFunction.js","webpack:///./node_modules/rxjs/dist/esm/internal/observable/fromArray.js","webpack:///./node_modules/rxjs/dist/esm/internal/Subscriber.js","webpack:///./node_modules/rxjs/dist/esm/internal/observable/empty.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/mergeMap.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/reportUnhandledError.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/isArrayLike.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduled/scheduleArray.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/ObjectUnsubscribedError.js","webpack:///./node_modules/rxjs/dist/esm/internal/Subject.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/pipe.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/distinctUntilChanged.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/createErrorClass.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduler/dateTimestampProvider.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/mapOneOrManyArgs.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/subscribeToArray.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/mergeAll.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduler/async.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduler/Action.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduler/intervalProvider.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduler/AsyncAction.js","webpack:///./node_modules/rxjs/dist/esm/internal/Scheduler.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduler/AsyncScheduler.js","webpack:///./node_modules/clipboard/dist/clipboard.js","webpack:///./node_modules/rxjs/dist/esm/internal/observable/of.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/argsArgArrayOrObject.js","webpack:///./node_modules/rxjs/dist/esm/internal/observable/combineLatest.js","webpack:///./node_modules/rxjs/dist/esm/internal/ReplaySubject.js","webpack:///./node_modules/resize-observer-polyfill/dist/ResizeObserver.es.js","webpack:///./node_modules/rxjs/dist/esm/internal/observable/defer.js","webpack:///./node_modules/escape-html/index.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/finalize.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/tap.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/scan.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/observeOn.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduler/animationFrameProvider.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduler/AnimationFrameAction.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduler/AnimationFrameScheduler.js","webpack:///./node_modules/rxjs/dist/esm/internal/scheduler/animationFrame.js","webpack:///./node_modules/focus-visible/dist/focus-visible.js","webpack:///(webpack)/buildin/global.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/shareReplay.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/distinctUntilKeyChanged.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/withLatestFrom.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/bufferCount.js","webpack:///./node_modules/rxjs/dist/esm/internal/observable/concat.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/concatAll.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/startWith.js","webpack:///./node_modules/rxjs/dist/esm/internal/observable/fromEvent.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/mapTo.js","webpack:///./node_modules/rxjs/dist/esm/internal/observable/never.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/filter.js","webpack:///./node_modules/rxjs/dist/esm/internal/BehaviorSubject.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/take.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/throttle.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/switchMapTo.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/sample.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/skip.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/catchError.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/debounceTime.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/concatMap.js","webpack:///./node_modules/rxjs/dist/esm/internal/observable/iif.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/refCount.js","webpack:///./node_modules/rxjs/dist/esm/internal/observable/ConnectableObservable.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/share.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/multicast.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/zipWith.js","webpack:///./node_modules/rxjs/dist/esm/internal/observable/zip.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/argsOrArgArray.js","webpack:///./node_modules/rxjs/dist/esm/internal/observable/merge.js","webpack:///./node_modules/rxjs/dist/esm/internal/operators/delay.js","webpack:///./node_modules/rxjs/dist/esm/internal/util/isDate.js"],"names":["hasLift","source","lift","operate","init","liftedSource","this","err","error","TypeError","OperatorSubscriber","destination","onNext","onError","onComplete","onUnsubscribe","super","_next","value","_error","unsubscribe","_complete","_a","closed","call","Observable","subscribe","_subscribe","operator","observable","observerOrNext","complete","subscriber","next","isObserver","add","useDeprecatedSynchronousErrorHandling","_trySubscribe","sink","isStopped","canReportError","promiseCtor","getPromiseCtor","resolve","reject","subscription","operations","length","x","Promise","create","UnsubscriptionError","createErrorClass","_super","errors","message","map","i","toString","join","name","empty","initialTeardown","_parentage","_teardowns","Array","isArray","parent","remove","isFunction","e","teardown","execTeardown","push","_hasParent","_addParent","includes","arrRemove","_removeParent","EMPTY","EMPTY_SUBSCRIPTION","isSubscription","Symbol","iterator","isPromise","then","__awaiter","thisArg","_arguments","P","generator","fulfilled","step","rejected","result","done","apply","Object","__values","o","s","m","__asyncValues","asyncIterator","verb","n","v","d","settle","subscribeToAsyncIterable","asyncIterable","asyncIterable_1","asyncIterable_1_1","e_1","e_1_1","return","process","catch","scheduled","input","scheduler","isInteropObservable","sub","Subscription","schedule","scheduleObservable","schedulePromise","isArrayLike","scheduleArray","isIterable","Error","scheduleIterable","scheduleAsyncIterable","from","obj","obs","subscribeToArray","promise","reportUnhandledError","iterable","item","subscribeTo","project","index","identity","isScheduler","arr","indexOf","splice","config","onUnhandledError","undefined","useDeprecatedNextContext","noop","switchMap","resultSelector","innerSubscriber","isComplete","checkComplete","innerIndex","outerIndex","innerValue","fromArray","Subscriber","EMPTY_OBSERVER","SafeSubscriber","context","bind","defaultErrorHandler","mergeMap","concurrent","Infinity","pipe","a","b","ii","active","buffer","doInnerSub","shift","tryInnerSub","outerSubs","setTimeout","ObjectUnsubscribedError","observers","hasError","thrownError","subject","_throwIfClosed","copy","slice","observer","_checkFinalizedStatuses","_innerSubscribe","_b","fns","pipeFromArray","reduce","prev","fn","distinctUntilChanged","compare","keySelector","defaultCompare","first","createImpl","ctorFunc","instance","constructor","stack","prototype","dateTimestampProvider","now","delegate","Date","mapOneOrManyArgs","args","callOrApply","array","len","mergeAll","asyncScheduler","work","state","delay","intervalProvider","setInterval","handle","clearInterval","pending","id","recycleAsyncId","requestAsyncId","_id","flush","_scheduler","_execute","_delay","errored","errorValue","actions","Scheduler","SchedulerAction","action","execute","factory","modules","installedModules","__webpack_require__","moduleId","exports","module","l","c","getter","defineProperty","enumerable","get","r","toStringTag","t","mode","__esModule","ns","key","object","property","hasOwnProperty","p","element","selectedText","nodeName","focus","isReadOnly","hasAttribute","setAttribute","select","setSelectionRange","removeAttribute","selection","window","getSelection","range","document","createRange","selectNodeContents","removeAllRanges","addRange","E","on","callback","ctx","once","self","listener","off","arguments","_","emit","data","evtArr","evts","liveEvents","TinyEmitter","is","target","type","string","node","addEventListener","destroy","removeEventListener","listenNode","nodeList","forEach","listenNodeList","selector","body","listenSelector","HTMLElement","nodeType","String","closest","_delegate","useCapture","listenerFn","delegateTarget","elements","querySelectorAll","Element","matches","proto","matchesSelector","mozMatchesSelector","msMatchesSelector","oMatchesSelector","webkitMatchesSelector","parentNode","__webpack_exports__","src_select","select_default","_typeof","_createClass","defineProperties","props","descriptor","configurable","writable","Constructor","protoProps","staticProps","clipboard_action","ClipboardAction","options","_classCallCheck","resolveOptions","initSelection","container","emitter","text","trigger","selectFake","selectTarget","_this","isRTL","documentElement","getAttribute","removeFake","fakeHandlerCallback","fakeHandler","fakeElem","createElement","style","fontSize","border","padding","margin","position","yPosition","pageYOffset","scrollTop","top","appendChild","copyText","removeChild","succeeded","execCommand","handleResult","clearSelection","activeElement","blur","set","_action","_target","tiny_emitter","tiny_emitter_default","listen","listen_default","clipboard_typeof","clipboard_createClass","clipboard_Clipboard","_Emitter","Clipboard","clipboard_classCallCheck","ReferenceError","_possibleConstructorReturn","__proto__","getPrototypeOf","listenClick","subClass","superClass","setPrototypeOf","_inherits","defaultAction","defaultTarget","defaultText","_this2","onClick","currentTarget","clipboardAction","getAttributeValue","querySelector","support","queryCommandSupported","suffix","attribute","of","pop","objectProto","keys","getKeys","argsArgArrayOrObject","combineLatest","observables","valueTransform","maybeSchedule","values","hasValues","waitingForFirstValues","every","combineLatestInit","shouldComplete","ReplaySubject","bufferSize","windowTime","timestampProvider","infiniteTimeWindow","Math","max","trimBuffer","adjustedBufferSize","last","MapShim","Map","getIndex","some","entry","class_1","__entries__","delete","entries","has","clear","_i","isBrowser","global$1","global","Function","requestAnimationFrame$1","requestAnimationFrame","transitionKeys","mutationObserverSupported","MutationObserver","ResizeObserverController","connected_","mutationEventsAdded_","mutationsObserver_","observers_","onTransitionEnd_","refresh","leadingCall","trailingCall","lastCallTime","resolvePending","proxy","timeoutCallback","timeStamp","throttle","addObserver","connect_","removeObserver","disconnect_","updateObservers_","activeObservers","filter","gatherActive","hasActive","broadcastActive","observe","attributes","childList","characterData","subtree","disconnect","propertyName","getInstance","instance_","defineConfigurable","getWindowOf","ownerDocument","defaultView","emptyRect","createRectInit","toFloat","parseFloat","getBordersSize","styles","positions","size","getHTMLElementContentRect","clientWidth","clientHeight","getComputedStyle","paddings","positions_1","getPaddings","horizPad","left","right","vertPad","bottom","width","height","boxSizing","round","isDocumentElement","vertScrollbar","horizScrollbar","abs","isSVGGraphicsElement","SVGGraphicsElement","SVGElement","getBBox","getContentRect","bbox","getSVGContentRect","y","ResizeObservation","broadcastWidth","broadcastHeight","contentRect_","isActive","rect","broadcastRect","ResizeObserverEntry","rectInit","Constr","contentRect","DOMRectReadOnly","ResizeObserverSPI","controller","callbackCtx","activeObservations_","observations_","callback_","controller_","callbackCtx_","observations","unobserve","clearActive","observation","WeakMap","ResizeObserver","method","defer","observableFactory","matchHtmlRegExp","escape","str","match","exec","html","lastIndex","charCodeAt","substring","finalize","tap","tapObserver","scan","accumulator","seed","hasSeed","hasState","observeOn","animationFrameProvider","request","cancel","cancelAnimationFrame","timestamp","AsyncAction","AsyncScheduler","count","animationFrameScheduler","applyFocusVisiblePolyfill","scope","hadKeyboardEvent","hadFocusVisibleRecently","hadFocusVisibleRecentlyTimeout","inputTypesWhitelist","search","url","tel","email","password","number","date","month","week","time","datetime","isValidFocusTarget","el","classList","addFocusVisibleClass","contains","onPointerDown","addInitialPointerMoveListeners","onInitialPointerMove","toLowerCase","metaKey","altKey","ctrlKey","visibilityState","tagName","readOnly","isContentEditable","clearTimeout","Node","DOCUMENT_FRAGMENT_NODE","host","DOCUMENT_NODE","event","CustomEvent","createEvent","initCustomEvent","dispatchEvent","g","shareReplay","configOrBufferSize","refCount","useRefCount","innerSub","dest","shareReplayOperator","distinctUntilKeyChanged","withLatestFrom","inputs","otherValues","hasValue","ready","otherSource","bufferCount","startBufferEvery","buffers","toEmit","concat","concatAll","startWith","fromEvent","eventName","handler","sourceObj","isJQueryStyleEventEmitter","addListener","removeListener","isNodeStyleEventEmitter","mapTo","NEVER","predicate","BehaviorSubject","_value","getValue","take","seen","defaultThrottleConfig","leading","trailing","durationSelector","sendValue","throttled","throttlingDone","send","switchMapTo","innerObservable","sample","notifier","lastValue","skip","catchError","handledResult","syncUnsub","debounceTime","dueTime","debounceSubscription","emitLastValue","concatMap","iif","condition","trueResult","falseResult","connection","_refCount","refCounter","sharedConnection","_connection","conn","connect","subjectFactory","_subject","getSubject","_teardown","shareSubjectFactory","Subject","share","subjectOrSubjectFactory","connectable","multicast","sources","completed","tryEmit","zip","zipWith","otherInputs","merge","argsOrArgArray","isAbsoluteDelay","isNaN","absoluteTimeValues"],"mappings":"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","file":"assets/javascripts/vendor.77e55a48.min.js","sourcesContent":["export function hasLift(source) {\n return typeof (source === null || source === void 0 ? void 0 : source.lift) === 'function';\n}\nexport function operate(init) {\n return (source) => {\n if (hasLift(source)) {\n return source.lift(function (liftedSource) {\n try {\n return init(liftedSource, this);\n }\n catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n//# sourceMappingURL=lift.js.map","import { Subscriber } from '../Subscriber';\nexport class OperatorSubscriber extends Subscriber {\n constructor(destination, onNext, onError, onComplete, onUnsubscribe) {\n super(destination);\n this.onUnsubscribe = onUnsubscribe;\n if (onNext) {\n this._next = function (value) {\n try {\n onNext(value);\n }\n catch (err) {\n this.error(err);\n }\n };\n }\n if (onError) {\n this._error = function (err) {\n try {\n onError(err);\n }\n catch (err) {\n this.destination.error(err);\n }\n this.unsubscribe();\n };\n }\n if (onComplete) {\n this._complete = function () {\n try {\n onComplete();\n }\n catch (err) {\n this.destination.error(err);\n }\n this.unsubscribe();\n };\n }\n }\n unsubscribe() {\n var _a;\n !this.closed && ((_a = this.onUnsubscribe) === null || _a === void 0 ? void 0 : _a.call(this));\n super.unsubscribe();\n }\n}\n//# sourceMappingURL=OperatorSubscriber.js.map","import { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription } from './Subscription';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nexport class Observable {\n constructor(subscribe) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n lift(operator) {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n subscribe(observerOrNext, error, complete) {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n const { operator, source } = this;\n subscriber.add(operator\n ? operator.call(subscriber, source)\n : source || config.useDeprecatedSynchronousErrorHandling\n ? this._subscribe(subscriber)\n : this._trySubscribe(subscriber));\n return subscriber;\n }\n _trySubscribe(sink) {\n try {\n return this._subscribe(sink);\n }\n catch (err) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n throw err;\n }\n else {\n canReportError(sink) ? sink.error(err) : reportUnhandledError(err);\n }\n }\n }\n forEach(next, promiseCtor) {\n promiseCtor = getPromiseCtor(promiseCtor);\n return new promiseCtor((resolve, reject) => {\n let subscription;\n subscription = this.subscribe((value) => {\n try {\n next(value);\n }\n catch (err) {\n reject(err);\n subscription === null || subscription === void 0 ? void 0 : subscription.unsubscribe();\n }\n }, reject, resolve);\n });\n }\n _subscribe(subscriber) {\n var _a;\n return (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber);\n }\n [Symbol_observable]() {\n return this;\n }\n pipe(...operations) {\n return operations.length ? pipeFromArray(operations)(this) : this;\n }\n toPromise(promiseCtor) {\n promiseCtor = getPromiseCtor(promiseCtor);\n return new promiseCtor((resolve, reject) => {\n let value;\n this.subscribe((x) => (value = x), (err) => reject(err), () => resolve(value));\n });\n }\n}\nObservable.create = (subscribe) => {\n return new Observable(subscribe);\n};\nfunction getPromiseCtor(promiseCtor) {\n var _a;\n return (_a = promiseCtor !== null && promiseCtor !== void 0 ? promiseCtor : config.Promise) !== null && _a !== void 0 ? _a : Promise;\n}\nexport function canReportError(subscriber) {\n while (subscriber) {\n const { closed, destination, isStopped } = subscriber;\n if (closed || isStopped) {\n return false;\n }\n subscriber = destination && destination instanceof Subscriber ? destination : null;\n }\n return true;\n}\nfunction isObserver(value) {\n return value && typeof value.next === 'function' && typeof value.error === 'function' && typeof value.complete === 'function';\n}\nfunction isSubscriber(value) {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n//# sourceMappingURL=Observable.js.map","import { createErrorClass } from './createErrorClass';\nexport const UnsubscriptionError = createErrorClass((_super) => function UnsubscriptionError(errors) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n});\n//# sourceMappingURL=UnsubscriptionError.js.map","import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { arrRemove } from './util/arrRemove';\nexport class Subscription {\n constructor(initialTeardown) {\n this.initialTeardown = initialTeardown;\n this.closed = false;\n this._parentage = null;\n this._teardowns = null;\n }\n unsubscribe() {\n let errors;\n if (!this.closed) {\n this.closed = true;\n const { _parentage } = this;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n }\n else {\n _parentage === null || _parentage === void 0 ? void 0 : _parentage.remove(this);\n }\n const { initialTeardown } = this;\n if (isFunction(initialTeardown)) {\n try {\n initialTeardown();\n }\n catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n const { _teardowns } = this;\n if (_teardowns) {\n this._teardowns = null;\n for (const teardown of _teardowns) {\n try {\n execTeardown(teardown);\n }\n catch (err) {\n errors = errors !== null && errors !== void 0 ? errors : [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n }\n else {\n errors.push(err);\n }\n }\n }\n }\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n add(teardown) {\n var _a;\n if (teardown && teardown !== this) {\n if (this.closed) {\n execTeardown(teardown);\n }\n else {\n if (teardown instanceof Subscription) {\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._teardowns = (_a = this._teardowns) !== null && _a !== void 0 ? _a : []).push(teardown);\n }\n }\n }\n _hasParent(parent) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n _addParent(parent) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n _removeParent(parent) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n }\n else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n remove(teardown) {\n const { _teardowns } = this;\n _teardowns && arrRemove(_teardowns, teardown);\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\nSubscription.EMPTY = (function (empty) {\n empty.closed = true;\n return empty;\n})(new Subscription());\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\nexport function isSubscription(value) {\n return (value instanceof Subscription ||\n (value &&\n 'closed' in value &&\n typeof value.remove === 'function' &&\n typeof value.add === 'function' &&\n typeof value.unsubscribe === 'function'));\n}\nfunction execTeardown(teardown) {\n if (typeof teardown === 'function') {\n teardown();\n }\n else {\n teardown.unsubscribe();\n }\n}\n//# sourceMappingURL=Subscription.js.map","export function getSymbolIterator() {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator';\n }\n return Symbol.iterator;\n}\nexport const iterator = getSymbolIterator();\nexport const $$iterator = iterator;\n//# sourceMappingURL=iterator.js.map","export function isPromise(value) {\n return !!value && typeof value.subscribe !== 'function' && typeof value.then === 'function';\n}\n//# sourceMappingURL=isPromise.js.map","/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n};\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, privateMap) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to get private field on non-instance\");\r\n }\r\n return privateMap.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, privateMap, value) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to set private field on non-instance\");\r\n }\r\n privateMap.set(receiver, value);\r\n return value;\r\n}\r\n","import { __asyncValues, __awaiter } from \"tslib\";\nexport function subscribeToAsyncIterable(asyncIterable) {\n return (subscriber) => {\n process(asyncIterable, subscriber).catch(err => subscriber.error(err));\n };\n}\nfunction process(asyncIterable, subscriber) {\n var asyncIterable_1, asyncIterable_1_1;\n var e_1, _a;\n return __awaiter(this, void 0, void 0, function* () {\n try {\n for (asyncIterable_1 = __asyncValues(asyncIterable); asyncIterable_1_1 = yield asyncIterable_1.next(), !asyncIterable_1_1.done;) {\n const value = asyncIterable_1_1.value;\n subscriber.next(value);\n }\n }\n catch (e_1_1) { e_1 = { error: e_1_1 }; }\n finally {\n try {\n if (asyncIterable_1_1 && !asyncIterable_1_1.done && (_a = asyncIterable_1.return)) yield _a.call(asyncIterable_1);\n }\n finally { if (e_1) throw e_1.error; }\n }\n subscriber.complete();\n });\n}\n//# sourceMappingURL=subscribeToAsyncIterable.js.map","import { scheduleObservable } from './scheduleObservable';\nimport { schedulePromise } from './schedulePromise';\nimport { scheduleArray } from './scheduleArray';\nimport { scheduleIterable } from './scheduleIterable';\nimport { isInteropObservable } from '../util/isInteropObservable';\nimport { isPromise } from '../util/isPromise';\nimport { isArrayLike } from '../util/isArrayLike';\nimport { isIterable } from '../util/isIterable';\nimport { scheduleAsyncIterable } from './scheduleAsyncIterable';\nexport function scheduled(input, scheduler) {\n if (input != null) {\n if (isInteropObservable(input)) {\n return scheduleObservable(input, scheduler);\n }\n else if (isPromise(input)) {\n return schedulePromise(input, scheduler);\n }\n else if (isArrayLike(input)) {\n return scheduleArray(input, scheduler);\n }\n else if (isIterable(input) || typeof input === 'string') {\n return scheduleIterable(input, scheduler);\n }\n else if (Symbol && Symbol.asyncIterator && typeof input[Symbol.asyncIterator] === 'function') {\n return scheduleAsyncIterable(input, scheduler);\n }\n }\n throw new TypeError((input !== null && typeof input || input) + ' is not observable');\n}\n//# sourceMappingURL=scheduled.js.map","import { observable as Symbol_observable } from '../symbol/observable';\nexport function isInteropObservable(input) {\n return input && typeof input[Symbol_observable] === 'function';\n}\n//# sourceMappingURL=isInteropObservable.js.map","import { Observable } from '../Observable';\nimport { Subscription } from '../Subscription';\nimport { observable as Symbol_observable } from '../symbol/observable';\nexport function scheduleObservable(input, scheduler) {\n return new Observable(subscriber => {\n const sub = new Subscription();\n sub.add(scheduler.schedule(() => {\n const observable = input[Symbol_observable]();\n sub.add(observable.subscribe({\n next(value) { sub.add(scheduler.schedule(() => subscriber.next(value))); },\n error(err) { sub.add(scheduler.schedule(() => subscriber.error(err))); },\n complete() { sub.add(scheduler.schedule(() => subscriber.complete())); },\n }));\n }));\n return sub;\n });\n}\n//# sourceMappingURL=scheduleObservable.js.map","import { Observable } from '../Observable';\nimport { Subscription } from '../Subscription';\nexport function schedulePromise(input, scheduler) {\n return new Observable(subscriber => {\n const sub = new Subscription();\n sub.add(scheduler.schedule(() => input.then(value => {\n sub.add(scheduler.schedule(() => {\n subscriber.next(value);\n sub.add(scheduler.schedule(() => subscriber.complete()));\n }));\n }, err => {\n sub.add(scheduler.schedule(() => subscriber.error(err)));\n })));\n return sub;\n });\n}\n//# sourceMappingURL=schedulePromise.js.map","import { iterator as Symbol_iterator } from '../symbol/iterator';\nexport function isIterable(input) {\n return input && typeof input[Symbol_iterator] === 'function';\n}\n//# sourceMappingURL=isIterable.js.map","import { Observable } from '../Observable';\nimport { Subscription } from '../Subscription';\nimport { iterator as Symbol_iterator } from '../symbol/iterator';\nexport function scheduleIterable(input, scheduler) {\n if (!input) {\n throw new Error('Iterable cannot be null');\n }\n return new Observable(subscriber => {\n const sub = new Subscription();\n let iterator;\n sub.add(() => {\n if (iterator && typeof iterator.return === 'function') {\n iterator.return();\n }\n });\n sub.add(scheduler.schedule(() => {\n iterator = input[Symbol_iterator]();\n sub.add(scheduler.schedule(function () {\n if (subscriber.closed) {\n return;\n }\n let value;\n let done;\n try {\n const result = iterator.next();\n value = result.value;\n done = result.done;\n }\n catch (err) {\n subscriber.error(err);\n return;\n }\n if (done) {\n subscriber.complete();\n }\n else {\n subscriber.next(value);\n this.schedule();\n }\n }));\n }));\n return sub;\n });\n}\n//# sourceMappingURL=scheduleIterable.js.map","import { Observable } from '../Observable';\nimport { Subscription } from '../Subscription';\nexport function scheduleAsyncIterable(input, scheduler) {\n if (!input) {\n throw new Error('Iterable cannot be null');\n }\n return new Observable(subscriber => {\n const sub = new Subscription();\n sub.add(scheduler.schedule(() => {\n const iterator = input[Symbol.asyncIterator]();\n sub.add(scheduler.schedule(function () {\n iterator.next().then(result => {\n if (result.done) {\n subscriber.complete();\n }\n else {\n subscriber.next(result.value);\n this.schedule();\n }\n });\n }));\n }));\n return sub;\n });\n}\n//# sourceMappingURL=scheduleAsyncIterable.js.map","import { subscribeToArray } from '../util/subscribeToArray';\nimport { subscribeToPromise } from '../util/subscribeToPromise';\nimport { subscribeToIterable } from '../util/subscribeToIterable';\nimport { subscribeToObservable } from '../util/subscribeToObservable';\nimport { isArrayLike } from '../util/isArrayLike';\nimport { isPromise } from '../util/isPromise';\nimport { isObject } from '../util/isObject';\nimport { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { subscribeToAsyncIterable } from '../util/subscribeToAsyncIterable';\nimport { Observable } from '../Observable';\nimport { scheduled } from '../scheduled/scheduled';\nexport function from(input, scheduler) {\n if (!scheduler) {\n if (input instanceof Observable) {\n return input;\n }\n return new Observable(subscribeTo(input));\n }\n else {\n return scheduled(input, scheduler);\n }\n}\nfunction subscribeTo(result) {\n if (result && typeof result[Symbol_observable] === 'function') {\n return subscribeToObservable(result);\n }\n else if (isArrayLike(result)) {\n return subscribeToArray(result);\n }\n else if (isPromise(result)) {\n return subscribeToPromise(result);\n }\n else if (result && typeof result[Symbol_iterator] === 'function') {\n return subscribeToIterable(result);\n }\n else if (Symbol && Symbol.asyncIterator &&\n !!result && typeof result[Symbol.asyncIterator] === 'function') {\n return subscribeToAsyncIterable(result);\n }\n else {\n const value = isObject(result) ? 'an invalid object' : `'${result}'`;\n const msg = `You provided ${value} where a stream was expected.`\n + ' You can provide an Observable, Promise, Array, AsyncIterable, or Iterable.';\n throw new TypeError(msg);\n }\n}\n;\n//# sourceMappingURL=from.js.map","import { observable as Symbol_observable } from '../symbol/observable';\nexport const subscribeToObservable = (obj) => (subscriber) => {\n const obs = obj[Symbol_observable]();\n if (typeof obs.subscribe !== 'function') {\n throw new TypeError('Provided object does not correctly implement Symbol.observable');\n }\n else {\n return obs.subscribe(subscriber);\n }\n};\n//# sourceMappingURL=subscribeToObservable.js.map","import { reportUnhandledError } from './reportUnhandledError';\nexport const subscribeToPromise = (promise) => (subscriber) => {\n promise.then((value) => {\n if (!subscriber.closed) {\n subscriber.next(value);\n subscriber.complete();\n }\n }, (err) => subscriber.error(err))\n .then(null, reportUnhandledError);\n return subscriber;\n};\n//# sourceMappingURL=subscribeToPromise.js.map","import { iterator as Symbol_iterator } from '../symbol/iterator';\nexport const subscribeToIterable = (iterable) => (subscriber) => {\n const iterator = iterable[Symbol_iterator]();\n do {\n let item;\n try {\n item = iterator.next();\n }\n catch (err) {\n subscriber.error(err);\n return;\n }\n if (item.done) {\n subscriber.complete();\n break;\n }\n subscriber.next(item.value);\n if (subscriber.closed) {\n break;\n }\n } while (true);\n if (typeof iterator.return === 'function') {\n subscriber.add(() => {\n if (iterator.return) {\n iterator.return();\n }\n });\n }\n return subscriber;\n};\n//# sourceMappingURL=subscribeToIterable.js.map","export function isObject(x) {\n return x !== null && typeof x === 'object';\n}\n//# sourceMappingURL=isObject.js.map","import { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function map(project, thisArg) {\n return operate((source, subscriber) => {\n let index = 0;\n source.subscribe(new OperatorSubscriber(subscriber, (value) => {\n subscriber.next(project.call(thisArg, value, index++));\n }));\n });\n}\n//# sourceMappingURL=map.js.map","export function identity(x) {\n return x;\n}\n//# sourceMappingURL=identity.js.map","export const observable = (() => typeof Symbol === 'function' && Symbol.observable || '@@observable')();\n//# sourceMappingURL=observable.js.map","export function isScheduler(value) {\n return value && typeof value.schedule === 'function';\n}\n//# sourceMappingURL=isScheduler.js.map","export function arrRemove(arr, item) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n//# sourceMappingURL=arrRemove.js.map","export const config = {\n onUnhandledError: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n//# sourceMappingURL=config.js.map","export function noop() { }\n//# sourceMappingURL=noop.js.map","import { from } from '../observable/from';\nimport { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function switchMap(project, resultSelector) {\n return operate((source, subscriber) => {\n let innerSubscriber = null;\n let index = 0;\n let isComplete = false;\n const checkComplete = () => isComplete && !innerSubscriber && subscriber.complete();\n source.subscribe(new OperatorSubscriber(subscriber, (value) => {\n innerSubscriber === null || innerSubscriber === void 0 ? void 0 : innerSubscriber.unsubscribe();\n let innerIndex = 0;\n let outerIndex = index++;\n from(project(value, outerIndex)).subscribe((innerSubscriber = new OperatorSubscriber(subscriber, (innerValue) => subscriber.next(resultSelector ? resultSelector(value, innerValue, outerIndex, innerIndex++) : innerValue), undefined, () => {\n innerSubscriber = null;\n checkComplete();\n })));\n }, undefined, () => {\n isComplete = true;\n checkComplete();\n }));\n });\n}\n//# sourceMappingURL=switchMap.js.map","export function isFunction(x) {\n return typeof x === 'function';\n}\n//# sourceMappingURL=isFunction.js.map","import { Observable } from '../Observable';\nimport { subscribeToArray } from '../util/subscribeToArray';\nimport { scheduleArray } from '../scheduled/scheduleArray';\nexport function fromArray(input, scheduler) {\n if (!scheduler) {\n return new Observable(subscribeToArray(input));\n }\n else {\n return scheduleArray(input, scheduler);\n }\n}\n//# sourceMappingURL=fromArray.js.map","import { isFunction } from './util/isFunction';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nexport class Subscriber extends Subscription {\n constructor(destination) {\n super();\n this.isStopped = false;\n if (destination) {\n this.destination = destination;\n if (isSubscription(destination)) {\n destination.add(this);\n }\n }\n else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n static create(next, error, complete) {\n return new SafeSubscriber(next, error, complete);\n }\n next(value) {\n if (!this.isStopped) {\n this._next(value);\n }\n }\n error(err) {\n if (!this.isStopped) {\n this.isStopped = true;\n this._error(err);\n }\n }\n complete() {\n if (!this.isStopped) {\n this.isStopped = true;\n this._complete();\n }\n }\n unsubscribe() {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n }\n }\n _next(value) {\n this.destination.next(value);\n }\n _error(err) {\n this.destination.error(err);\n this.unsubscribe();\n }\n _complete() {\n this.destination.complete();\n this.unsubscribe();\n }\n}\nexport class SafeSubscriber extends Subscriber {\n constructor(observerOrNext, error, complete) {\n super();\n this.destination = EMPTY_OBSERVER;\n if ((observerOrNext || error || complete) && observerOrNext !== EMPTY_OBSERVER) {\n let next;\n if (isFunction(observerOrNext)) {\n next = observerOrNext;\n }\n else if (observerOrNext) {\n ({ next, error, complete } = observerOrNext);\n let context;\n if (this && config.useDeprecatedNextContext) {\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n }\n else {\n context = observerOrNext;\n }\n next = next === null || next === void 0 ? void 0 : next.bind(context);\n error = error === null || error === void 0 ? void 0 : error.bind(context);\n complete = complete === null || complete === void 0 ? void 0 : complete.bind(context);\n }\n this.destination = {\n next: next || noop,\n error: error || defaultErrorHandler,\n complete: complete || noop,\n };\n }\n }\n}\nfunction defaultErrorHandler(err) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n throw err;\n }\n reportUnhandledError(err);\n}\nexport const EMPTY_OBSERVER = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n//# sourceMappingURL=Subscriber.js.map","import { Observable } from '../Observable';\nexport const EMPTY = new Observable(subscriber => subscriber.complete());\nexport function empty(scheduler) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\nfunction emptyScheduled(scheduler) {\n return new Observable(subscriber => scheduler.schedule(() => subscriber.complete()));\n}\n//# sourceMappingURL=empty.js.map","import { map } from './map';\nimport { from } from '../observable/from';\nimport { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function mergeMap(project, resultSelector, concurrent = Infinity) {\n if (typeof resultSelector === 'function') {\n return (source) => source.pipe(mergeMap((a, i) => from(project(a, i)).pipe(map((b, ii) => resultSelector(a, b, i, ii))), concurrent));\n }\n else if (typeof resultSelector === 'number') {\n concurrent = resultSelector;\n }\n return operate((source, subscriber) => {\n let isComplete = false;\n let active = 0;\n let index = 0;\n let buffer = [];\n const checkComplete = () => isComplete && !active && subscriber.complete();\n const tryInnerSub = () => {\n while (active < concurrent && buffer.length > 0) {\n doInnerSub(buffer.shift());\n }\n };\n const doInnerSub = (value) => {\n active++;\n subscriber.add(from(project(value, index++)).subscribe(new OperatorSubscriber(subscriber, (innerValue) => subscriber.next(innerValue), undefined, () => {\n active--;\n buffer.length && tryInnerSub();\n checkComplete();\n })));\n };\n let outerSubs;\n outerSubs = source.subscribe(new OperatorSubscriber(subscriber, (value) => (active < concurrent ? doInnerSub(value) : buffer.push(value)), undefined, () => {\n isComplete = true;\n checkComplete();\n outerSubs === null || outerSubs === void 0 ? void 0 : outerSubs.unsubscribe();\n }));\n return () => {\n buffer = null;\n };\n });\n}\nexport const flatMap = mergeMap;\n//# sourceMappingURL=mergeMap.js.map","import { config } from '../config';\nexport function reportUnhandledError(err) {\n setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n onUnhandledError(err);\n }\n else {\n throw err;\n }\n });\n}\n//# sourceMappingURL=reportUnhandledError.js.map","export const isArrayLike = ((x) => x && typeof x.length === 'number' && typeof x !== 'function');\n//# sourceMappingURL=isArrayLike.js.map","import { Observable } from '../Observable';\nimport { Subscription } from '../Subscription';\nexport function scheduleArray(input, scheduler) {\n return new Observable(subscriber => {\n const sub = new Subscription();\n let i = 0;\n sub.add(scheduler.schedule(function () {\n if (i === input.length) {\n subscriber.complete();\n return;\n }\n subscriber.next(input[i++]);\n if (!subscriber.closed) {\n sub.add(this.schedule());\n }\n }));\n return sub;\n });\n}\n//# sourceMappingURL=scheduleArray.js.map","import { createErrorClass } from './createErrorClass';\nexport const ObjectUnsubscribedError = createErrorClass((_super) => function ObjectUnsubscribedError() {\n _super(this);\n this.message = 'object unsubscribed';\n});\n//# sourceMappingURL=ObjectUnsubscribedError.js.map","import { Observable } from './Observable';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nexport class Subject extends Observable {\n constructor() {\n super();\n this.observers = [];\n this.closed = false;\n this.isStopped = false;\n this.hasError = false;\n this.thrownError = null;\n }\n lift(operator) {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator;\n return subject;\n }\n _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n next(value) {\n this._throwIfClosed();\n if (!this.isStopped) {\n const copy = this.observers.slice();\n for (const observer of copy) {\n observer.next(value);\n }\n }\n }\n error(err) {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift().error(err);\n }\n }\n }\n complete() {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift().complete();\n }\n }\n }\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = null;\n }\n _trySubscribe(subscriber) {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n _subscribe(subscriber) {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n _innerSubscribe(subscriber) {\n const { hasError, isStopped, observers } = this;\n return hasError || isStopped\n ? EMPTY_SUBSCRIPTION\n : (observers.push(subscriber), new Subscription(() => arrRemove(this.observers, subscriber)));\n }\n _checkFinalizedStatuses(subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n }\n else if (isStopped) {\n subscriber.complete();\n }\n }\n asObservable() {\n const observable = new Observable();\n observable.source = this;\n return observable;\n }\n}\nSubject.create = (destination, source) => {\n return new AnonymousSubject(destination, source);\n};\nexport class AnonymousSubject extends Subject {\n constructor(destination, source) {\n super();\n this.destination = destination;\n this.source = source;\n }\n next(value) {\n var _a, _b;\n (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.next) === null || _b === void 0 ? void 0 : _b.call(_a, value);\n }\n error(err) {\n var _a, _b;\n (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.call(_a, err);\n }\n complete() {\n var _a, _b;\n (_b = (_a = this.destination) === null || _a === void 0 ? void 0 : _a.complete) === null || _b === void 0 ? void 0 : _b.call(_a);\n }\n _subscribe(subscriber) {\n var _a, _b;\n return (_b = (_a = this.source) === null || _a === void 0 ? void 0 : _a.subscribe(subscriber)) !== null && _b !== void 0 ? _b : EMPTY_SUBSCRIPTION;\n }\n}\n//# sourceMappingURL=Subject.js.map","import { identity } from './identity';\nexport function pipe(...fns) {\n return pipeFromArray(fns);\n}\nexport function pipeFromArray(fns) {\n if (fns.length === 0) {\n return identity;\n }\n if (fns.length === 1) {\n return fns[0];\n }\n return function piped(input) {\n return fns.reduce((prev, fn) => fn(prev), input);\n };\n}\n//# sourceMappingURL=pipe.js.map","import { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function distinctUntilChanged(compare, keySelector) {\n compare = compare !== null && compare !== void 0 ? compare : defaultCompare;\n return operate((source, subscriber) => {\n let prev;\n let first = true;\n source.subscribe(new OperatorSubscriber(subscriber, (value) => {\n ((first && ((prev = value), 1)) || !compare(prev, (prev = keySelector ? keySelector(value) : value))) &&\n subscriber.next(value);\n first = false;\n }));\n });\n}\nfunction defaultCompare(a, b) {\n return a === b;\n}\n//# sourceMappingURL=distinctUntilChanged.js.map","export function createErrorClass(createImpl) {\n const _super = (instance) => {\n Error.call(instance);\n instance.name = instance.constructor.name;\n instance.stack = new Error().stack;\n };\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n//# sourceMappingURL=createErrorClass.js.map","export const dateTimestampProvider = {\n now() {\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n//# sourceMappingURL=dateTimestampProvider.js.map","import { map } from \"../operators/map\";\nconst { isArray } = Array;\nfunction callOrApply(fn, args) {\n return isArray(args) ? fn(...args) : fn(args);\n}\nexport function mapOneOrManyArgs(fn) {\n return map(args => callOrApply(fn, args));\n}\n//# sourceMappingURL=mapOneOrManyArgs.js.map","export const subscribeToArray = (array) => (subscriber) => {\n for (let i = 0, len = array.length; i < len && !subscriber.closed; i++) {\n subscriber.next(array[i]);\n }\n subscriber.complete();\n};\n//# sourceMappingURL=subscribeToArray.js.map","import { mergeMap } from './mergeMap';\nimport { identity } from '../util/identity';\nexport function mergeAll(concurrent = Infinity) {\n return mergeMap(identity, concurrent);\n}\n//# sourceMappingURL=mergeAll.js.map","import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\nexport const async = asyncScheduler;\n//# sourceMappingURL=async.js.map","import { Subscription } from '../Subscription';\nexport class Action extends Subscription {\n constructor(scheduler, work) {\n super();\n }\n schedule(state, delay = 0) {\n return this;\n }\n}\n//# sourceMappingURL=Action.js.map","export const intervalProvider = {\n setInterval(...args) {\n const { delegate } = intervalProvider;\n return ((delegate === null || delegate === void 0 ? void 0 : delegate.setInterval) || setInterval)(...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return ((delegate === null || delegate === void 0 ? void 0 : delegate.clearInterval) || clearInterval)(handle);\n },\n delegate: undefined,\n};\n//# sourceMappingURL=intervalProvider.js.map","import { Action } from './Action';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nexport class AsyncAction extends Action {\n constructor(scheduler, work) {\n super(scheduler, work);\n this.scheduler = scheduler;\n this.work = work;\n this.pending = false;\n }\n schedule(state, delay = 0) {\n if (this.closed) {\n return this;\n }\n this.state = state;\n const id = this.id;\n const scheduler = this.scheduler;\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n this.pending = true;\n this.delay = delay;\n this.id = this.id || this.requestAsyncId(scheduler, this.id, delay);\n return this;\n }\n requestAsyncId(scheduler, _id, delay = 0) {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n recycleAsyncId(_scheduler, id, delay = 0) {\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n intervalProvider.clearInterval(id);\n return undefined;\n }\n execute(state, delay) {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n }\n else if (this.pending === false && this.id != null) {\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n _execute(state, _delay) {\n let errored = false;\n let errorValue = undefined;\n try {\n this.work(state);\n }\n catch (e) {\n errored = true;\n errorValue = (!!e && e) || new Error(e);\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n this.work = this.state = this.scheduler = null;\n this.pending = false;\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n this.delay = null;\n super.unsubscribe();\n }\n }\n}\n//# sourceMappingURL=AsyncAction.js.map","import { dateTimestampProvider } from \"./scheduler/dateTimestampProvider\";\nexport class Scheduler {\n constructor(SchedulerAction, now = Scheduler.now) {\n this.SchedulerAction = SchedulerAction;\n this.now = now;\n }\n schedule(work, delay = 0, state) {\n return new this.SchedulerAction(this, work).schedule(state, delay);\n }\n}\nScheduler.now = dateTimestampProvider.now;\n//# sourceMappingURL=Scheduler.js.map","import { Scheduler } from '../Scheduler';\nexport class AsyncScheduler extends Scheduler {\n constructor(SchedulerAction, now = Scheduler.now) {\n super(SchedulerAction, now);\n this.actions = [];\n this.active = false;\n this.scheduled = undefined;\n }\n flush(action) {\n const { actions } = this;\n if (this.active) {\n actions.push(action);\n return;\n }\n let error;\n this.active = true;\n do {\n if (error = action.execute(action.state, action.delay)) {\n break;\n }\n } while (action = actions.shift());\n this.active = false;\n if (error) {\n while (action = actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n//# sourceMappingURL=AsyncScheduler.js.map","/*!\n * clipboard.js v2.0.6\n * https://clipboardjs.com/\n * \n * Licensed MIT © Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// define __esModule on exports\n/******/ \t__webpack_require__.r = function(exports) {\n/******/ \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n/******/ \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n/******/ \t\t}\n/******/ \t\tObject.defineProperty(exports, '__esModule', { value: true });\n/******/ \t};\n/******/\n/******/ \t// create a fake namespace object\n/******/ \t// mode & 1: value is a module id, require it\n/******/ \t// mode & 2: merge all properties of value into the ns\n/******/ \t// mode & 4: return value when already ns object\n/******/ \t// mode & 8|1: behave like require\n/******/ \t__webpack_require__.t = function(value, mode) {\n/******/ \t\tif(mode & 1) value = __webpack_require__(value);\n/******/ \t\tif(mode & 8) return value;\n/******/ \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n/******/ \t\tvar ns = Object.create(null);\n/******/ \t\t__webpack_require__.r(ns);\n/******/ \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n/******/ \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n/******/ \t\treturn ns;\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 6);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar is = __webpack_require__(3);\nvar delegate = __webpack_require__(4);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar closest = __webpack_require__(5);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n/* 5 */\n/***/ (function(module, exports) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n/* 6 */\n/***/ (function(module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n__webpack_require__.r(__webpack_exports__);\n\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(0);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n\n// CONCATENATED MODULE: ./src/clipboard-action.js\nvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\n\n\n/**\n * Inner class which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n */\n\nvar clipboard_action_ClipboardAction = function () {\n /**\n * @param {Object} options\n */\n function ClipboardAction(options) {\n _classCallCheck(this, ClipboardAction);\n\n this.resolveOptions(options);\n this.initSelection();\n }\n\n /**\n * Defines base properties passed from constructor.\n * @param {Object} options\n */\n\n\n _createClass(ClipboardAction, [{\n key: 'resolveOptions',\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n this.action = options.action;\n this.container = options.container;\n this.emitter = options.emitter;\n this.target = options.target;\n this.text = options.text;\n this.trigger = options.trigger;\n\n this.selectedText = '';\n }\n\n /**\n * Decides which selection strategy is going to be applied based\n * on the existence of `text` and `target` properties.\n */\n\n }, {\n key: 'initSelection',\n value: function initSelection() {\n if (this.text) {\n this.selectFake();\n } else if (this.target) {\n this.selectTarget();\n }\n }\n\n /**\n * Creates a fake textarea element, sets its value from `text` property,\n * and makes a selection on it.\n */\n\n }, {\n key: 'selectFake',\n value: function selectFake() {\n var _this = this;\n\n var isRTL = document.documentElement.getAttribute('dir') == 'rtl';\n\n this.removeFake();\n\n this.fakeHandlerCallback = function () {\n return _this.removeFake();\n };\n this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;\n\n this.fakeElem = document.createElement('textarea');\n // Prevent zooming on iOS\n this.fakeElem.style.fontSize = '12pt';\n // Reset box model\n this.fakeElem.style.border = '0';\n this.fakeElem.style.padding = '0';\n this.fakeElem.style.margin = '0';\n // Move element out of screen horizontally\n this.fakeElem.style.position = 'absolute';\n this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px';\n // Move element to the same position vertically\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n this.fakeElem.style.top = yPosition + 'px';\n\n this.fakeElem.setAttribute('readonly', '');\n this.fakeElem.value = this.text;\n\n this.container.appendChild(this.fakeElem);\n\n this.selectedText = select_default()(this.fakeElem);\n this.copyText();\n }\n\n /**\n * Only removes the fake element after another click event, that way\n * a user can hit `Ctrl+C` to copy because selection still exists.\n */\n\n }, {\n key: 'removeFake',\n value: function removeFake() {\n if (this.fakeHandler) {\n this.container.removeEventListener('click', this.fakeHandlerCallback);\n this.fakeHandler = null;\n this.fakeHandlerCallback = null;\n }\n\n if (this.fakeElem) {\n this.container.removeChild(this.fakeElem);\n this.fakeElem = null;\n }\n }\n\n /**\n * Selects the content from element passed on `target` property.\n */\n\n }, {\n key: 'selectTarget',\n value: function selectTarget() {\n this.selectedText = select_default()(this.target);\n this.copyText();\n }\n\n /**\n * Executes the copy operation based on the current selection.\n */\n\n }, {\n key: 'copyText',\n value: function copyText() {\n var succeeded = void 0;\n\n try {\n succeeded = document.execCommand(this.action);\n } catch (err) {\n succeeded = false;\n }\n\n this.handleResult(succeeded);\n }\n\n /**\n * Fires an event based on the copy operation result.\n * @param {Boolean} succeeded\n */\n\n }, {\n key: 'handleResult',\n value: function handleResult(succeeded) {\n this.emitter.emit(succeeded ? 'success' : 'error', {\n action: this.action,\n text: this.selectedText,\n trigger: this.trigger,\n clearSelection: this.clearSelection.bind(this)\n });\n }\n\n /**\n * Moves focus away from `target` and back to the trigger, removes current selection.\n */\n\n }, {\n key: 'clearSelection',\n value: function clearSelection() {\n if (this.trigger) {\n this.trigger.focus();\n }\n document.activeElement.blur();\n window.getSelection().removeAllRanges();\n }\n\n /**\n * Sets the `action` to be performed which can be either 'copy' or 'cut'.\n * @param {String} action\n */\n\n }, {\n key: 'destroy',\n\n\n /**\n * Destroy lifecycle.\n */\n value: function destroy() {\n this.removeFake();\n }\n }, {\n key: 'action',\n set: function set() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy';\n\n this._action = action;\n\n if (this._action !== 'copy' && this._action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n }\n }\n\n /**\n * Gets the `action` property.\n * @return {String}\n */\n ,\n get: function get() {\n return this._action;\n }\n\n /**\n * Sets the `target` property using an element\n * that will be have its content copied.\n * @param {Element} target\n */\n\n }, {\n key: 'target',\n set: function set(target) {\n if (target !== undefined) {\n if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && target.nodeType === 1) {\n if (this.action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n\n this._target = target;\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n }\n }\n\n /**\n * Gets the `target` property.\n * @return {String|HTMLElement}\n */\n ,\n get: function get() {\n return this._target;\n }\n }]);\n\n return ClipboardAction;\n}();\n\n/* harmony default export */ var clipboard_action = (clipboard_action_ClipboardAction);\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(1);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(2);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n\n// CONCATENATED MODULE: ./src/clipboard.js\nvar clipboard_typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\nvar clipboard_createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();\n\nfunction clipboard_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return call && (typeof call === \"object\" || typeof call === \"function\") ? call : self; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function, not \" + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }\n\n\n\n\n\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\nvar clipboard_Clipboard = function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n clipboard_classCallCheck(this, Clipboard);\n\n var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this));\n\n _this.resolveOptions(options);\n _this.listenClick(trigger);\n return _this;\n }\n\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n clipboard_createClass(Clipboard, [{\n key: 'resolveOptions',\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: 'listenClick',\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: 'onClick',\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n\n if (this.clipboardAction) {\n this.clipboardAction = null;\n }\n\n this.clipboardAction = new clipboard_action({\n action: this.action(trigger),\n target: this.target(trigger),\n text: this.text(trigger),\n container: this.container,\n trigger: trigger,\n emitter: this\n });\n }\n\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: 'defaultAction',\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: 'defaultTarget',\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: 'defaultText',\n\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: 'destroy',\n value: function destroy() {\n this.listener.destroy();\n\n if (this.clipboardAction) {\n this.clipboardAction.destroy();\n this.clipboardAction = null;\n }\n }\n }], [{\n key: 'isSupported',\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n\n return support;\n }\n }]);\n\n return Clipboard;\n}(tiny_emitter_default.a);\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\n\nfunction getAttributeValue(suffix, element) {\n var attribute = 'data-clipboard-' + suffix;\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n\n/* harmony default export */ var clipboard = __webpack_exports__[\"default\"] = (clipboard_Clipboard);\n\n/***/ })\n/******/ ])[\"default\"];\n});","import { isScheduler } from '../util/isScheduler';\nimport { fromArray } from './fromArray';\nimport { scheduleArray } from '../scheduled/scheduleArray';\nexport function of(...args) {\n let scheduler = args[args.length - 1];\n if (isScheduler(scheduler)) {\n args.pop();\n return scheduleArray(args, scheduler);\n }\n else {\n return fromArray(args);\n }\n}\n//# sourceMappingURL=of.js.map","const { isArray } = Array;\nconst { getPrototypeOf, prototype: objectProto, keys: getKeys } = Object;\nexport function argsArgArrayOrObject(args) {\n if (args.length === 1) {\n const first = args[0];\n if (isArray(first)) {\n return { args: first, keys: null };\n }\n if (isPOJO(first)) {\n const keys = getKeys(first);\n return {\n args: keys.map((key) => first[key]),\n keys,\n };\n }\n }\n return { args: args, keys: null };\n}\nfunction isPOJO(obj) {\n return obj && typeof obj === 'object' && getPrototypeOf(obj) === objectProto;\n}\n//# sourceMappingURL=argsArgArrayOrObject.js.map","import { Observable } from '../Observable';\nimport { isScheduler } from '../util/isScheduler';\nimport { argsArgArrayOrObject } from '../util/argsArgArrayOrObject';\nimport { Subscriber } from '../Subscriber';\nimport { from } from './from';\nimport { identity } from '../util/identity';\nimport { mapOneOrManyArgs } from '../util/mapOneOrManyArgs';\nexport function combineLatest(...args) {\n let resultSelector = undefined;\n let scheduler = undefined;\n if (isScheduler(args[args.length - 1])) {\n scheduler = args.pop();\n }\n if (typeof args[args.length - 1] === 'function') {\n resultSelector = args.pop();\n }\n const { args: observables, keys } = argsArgArrayOrObject(args);\n const result = new Observable(combineLatestInit(observables, scheduler, keys\n ?\n (args) => {\n const value = {};\n for (let i = 0; i < args.length; i++) {\n value[keys[i]] = args[i];\n }\n return value;\n }\n :\n identity));\n if (resultSelector) {\n return result.pipe(mapOneOrManyArgs(resultSelector));\n }\n return result;\n}\nclass CombineLatestSubscriber extends Subscriber {\n constructor(destination, _next, shouldComplete) {\n super(destination);\n this._next = _next;\n this.shouldComplete = shouldComplete;\n }\n _complete() {\n if (this.shouldComplete()) {\n super._complete();\n }\n else {\n this.unsubscribe();\n }\n }\n}\nexport function combineLatestInit(observables, scheduler = undefined, valueTransform = identity) {\n return (subscriber) => {\n const primarySubscribe = () => {\n const { length } = observables;\n const values = new Array(length);\n let active = length;\n const hasValues = observables.map(() => false);\n let waitingForFirstValues = true;\n const emit = () => subscriber.next(valueTransform(values.slice()));\n for (let i = 0; i < length; i++) {\n const subscribe = () => {\n const source = from(observables[i], scheduler);\n source.subscribe(new CombineLatestSubscriber(subscriber, (value) => {\n values[i] = value;\n if (waitingForFirstValues) {\n hasValues[i] = true;\n waitingForFirstValues = !hasValues.every(identity);\n }\n if (!waitingForFirstValues) {\n emit();\n }\n }, () => --active === 0));\n };\n maybeSchedule(scheduler, subscribe, subscriber);\n }\n };\n maybeSchedule(scheduler, primarySubscribe, subscriber);\n };\n}\nfunction maybeSchedule(scheduler, execute, subscription) {\n if (scheduler) {\n subscription.add(scheduler.schedule(execute));\n }\n else {\n execute();\n }\n}\n//# sourceMappingURL=combineLatest.js.map","import { Subject } from './Subject';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\nexport class ReplaySubject extends Subject {\n constructor(bufferSize = Infinity, windowTime = Infinity, timestampProvider = dateTimestampProvider) {\n super();\n this.bufferSize = bufferSize;\n this.windowTime = windowTime;\n this.timestampProvider = timestampProvider;\n this.buffer = [];\n this.infiniteTimeWindow = true;\n this.infiniteTimeWindow = windowTime === Infinity;\n this.bufferSize = Math.max(1, bufferSize);\n this.windowTime = Math.max(1, windowTime);\n }\n next(value) {\n const { isStopped, buffer, infiniteTimeWindow, timestampProvider, windowTime } = this;\n if (!isStopped) {\n buffer.push(value);\n !infiniteTimeWindow && buffer.push(timestampProvider.now() + windowTime);\n }\n this.trimBuffer();\n super.next(value);\n }\n _subscribe(subscriber) {\n this._throwIfClosed();\n this.trimBuffer();\n const subscription = this._innerSubscribe(subscriber);\n const { infiniteTimeWindow, buffer } = this;\n const copy = buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i]);\n }\n this._checkFinalizedStatuses(subscriber);\n return subscription;\n }\n trimBuffer() {\n const { bufferSize, timestampProvider, buffer, infiniteTimeWindow } = this;\n const adjustedBufferSize = (infiniteTimeWindow ? 1 : 2) * bufferSize;\n bufferSize < Infinity && adjustedBufferSize < buffer.length && buffer.splice(0, buffer.length - adjustedBufferSize);\n if (!infiniteTimeWindow) {\n const now = timestampProvider.now();\n let last = 0;\n for (let i = 1; i < buffer.length && buffer[i] <= now; i += 2) {\n last = i;\n }\n last && buffer.splice(0, last + 1);\n }\n }\n}\n//# sourceMappingURL=ReplaySubject.js.map","/**\r\n * A collection of shims that provide minimal functionality of the ES6 collections.\r\n *\r\n * These implementations are not meant to be used outside of the ResizeObserver\r\n * modules as they cover only a limited range of use cases.\r\n */\r\n/* eslint-disable require-jsdoc, valid-jsdoc */\r\nvar MapShim = (function () {\r\n if (typeof Map !== 'undefined') {\r\n return Map;\r\n }\r\n /**\r\n * Returns index in provided array that matches the specified key.\r\n *\r\n * @param {Array} arr\r\n * @param {*} key\r\n * @returns {number}\r\n */\r\n function getIndex(arr, key) {\r\n var result = -1;\r\n arr.some(function (entry, index) {\r\n if (entry[0] === key) {\r\n result = index;\r\n return true;\r\n }\r\n return false;\r\n });\r\n return result;\r\n }\r\n return /** @class */ (function () {\r\n function class_1() {\r\n this.__entries__ = [];\r\n }\r\n Object.defineProperty(class_1.prototype, \"size\", {\r\n /**\r\n * @returns {boolean}\r\n */\r\n get: function () {\r\n return this.__entries__.length;\r\n },\r\n enumerable: true,\r\n configurable: true\r\n });\r\n /**\r\n * @param {*} key\r\n * @returns {*}\r\n */\r\n class_1.prototype.get = function (key) {\r\n var index = getIndex(this.__entries__, key);\r\n var entry = this.__entries__[index];\r\n return entry && entry[1];\r\n };\r\n /**\r\n * @param {*} key\r\n * @param {*} value\r\n * @returns {void}\r\n */\r\n class_1.prototype.set = function (key, value) {\r\n var index = getIndex(this.__entries__, key);\r\n if (~index) {\r\n this.__entries__[index][1] = value;\r\n }\r\n else {\r\n this.__entries__.push([key, value]);\r\n }\r\n };\r\n /**\r\n * @param {*} key\r\n * @returns {void}\r\n */\r\n class_1.prototype.delete = function (key) {\r\n var entries = this.__entries__;\r\n var index = getIndex(entries, key);\r\n if (~index) {\r\n entries.splice(index, 1);\r\n }\r\n };\r\n /**\r\n * @param {*} key\r\n * @returns {void}\r\n */\r\n class_1.prototype.has = function (key) {\r\n return !!~getIndex(this.__entries__, key);\r\n };\r\n /**\r\n * @returns {void}\r\n */\r\n class_1.prototype.clear = function () {\r\n this.__entries__.splice(0);\r\n };\r\n /**\r\n * @param {Function} callback\r\n * @param {*} [ctx=null]\r\n * @returns {void}\r\n */\r\n class_1.prototype.forEach = function (callback, ctx) {\r\n if (ctx === void 0) { ctx = null; }\r\n for (var _i = 0, _a = this.__entries__; _i < _a.length; _i++) {\r\n var entry = _a[_i];\r\n callback.call(ctx, entry[1], entry[0]);\r\n }\r\n };\r\n return class_1;\r\n }());\r\n})();\n\n/**\r\n * Detects whether window and document objects are available in current environment.\r\n */\r\nvar isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined' && window.document === document;\n\n// Returns global object of a current environment.\r\nvar global$1 = (function () {\r\n if (typeof global !== 'undefined' && global.Math === Math) {\r\n return global;\r\n }\r\n if (typeof self !== 'undefined' && self.Math === Math) {\r\n return self;\r\n }\r\n if (typeof window !== 'undefined' && window.Math === Math) {\r\n return window;\r\n }\r\n // eslint-disable-next-line no-new-func\r\n return Function('return this')();\r\n})();\n\n/**\r\n * A shim for the requestAnimationFrame which falls back to the setTimeout if\r\n * first one is not supported.\r\n *\r\n * @returns {number} Requests' identifier.\r\n */\r\nvar requestAnimationFrame$1 = (function () {\r\n if (typeof requestAnimationFrame === 'function') {\r\n // It's required to use a bounded function because IE sometimes throws\r\n // an \"Invalid calling object\" error if rAF is invoked without the global\r\n // object on the left hand side.\r\n return requestAnimationFrame.bind(global$1);\r\n }\r\n return function (callback) { return setTimeout(function () { return callback(Date.now()); }, 1000 / 60); };\r\n})();\n\n// Defines minimum timeout before adding a trailing call.\r\nvar trailingTimeout = 2;\r\n/**\r\n * Creates a wrapper function which ensures that provided callback will be\r\n * invoked only once during the specified delay period.\r\n *\r\n * @param {Function} callback - Function to be invoked after the delay period.\r\n * @param {number} delay - Delay after which to invoke callback.\r\n * @returns {Function}\r\n */\r\nfunction throttle (callback, delay) {\r\n var leadingCall = false, trailingCall = false, lastCallTime = 0;\r\n /**\r\n * Invokes the original callback function and schedules new invocation if\r\n * the \"proxy\" was called during current request.\r\n *\r\n * @returns {void}\r\n */\r\n function resolvePending() {\r\n if (leadingCall) {\r\n leadingCall = false;\r\n callback();\r\n }\r\n if (trailingCall) {\r\n proxy();\r\n }\r\n }\r\n /**\r\n * Callback invoked after the specified delay. It will further postpone\r\n * invocation of the original function delegating it to the\r\n * requestAnimationFrame.\r\n *\r\n * @returns {void}\r\n */\r\n function timeoutCallback() {\r\n requestAnimationFrame$1(resolvePending);\r\n }\r\n /**\r\n * Schedules invocation of the original function.\r\n *\r\n * @returns {void}\r\n */\r\n function proxy() {\r\n var timeStamp = Date.now();\r\n if (leadingCall) {\r\n // Reject immediately following calls.\r\n if (timeStamp - lastCallTime < trailingTimeout) {\r\n return;\r\n }\r\n // Schedule new call to be in invoked when the pending one is resolved.\r\n // This is important for \"transitions\" which never actually start\r\n // immediately so there is a chance that we might miss one if change\r\n // happens amids the pending invocation.\r\n trailingCall = true;\r\n }\r\n else {\r\n leadingCall = true;\r\n trailingCall = false;\r\n setTimeout(timeoutCallback, delay);\r\n }\r\n lastCallTime = timeStamp;\r\n }\r\n return proxy;\r\n}\n\n// Minimum delay before invoking the update of observers.\r\nvar REFRESH_DELAY = 20;\r\n// A list of substrings of CSS properties used to find transition events that\r\n// might affect dimensions of observed elements.\r\nvar transitionKeys = ['top', 'right', 'bottom', 'left', 'width', 'height', 'size', 'weight'];\r\n// Check if MutationObserver is available.\r\nvar mutationObserverSupported = typeof MutationObserver !== 'undefined';\r\n/**\r\n * Singleton controller class which handles updates of ResizeObserver instances.\r\n */\r\nvar ResizeObserverController = /** @class */ (function () {\r\n /**\r\n * Creates a new instance of ResizeObserverController.\r\n *\r\n * @private\r\n */\r\n function ResizeObserverController() {\r\n /**\r\n * Indicates whether DOM listeners have been added.\r\n *\r\n * @private {boolean}\r\n */\r\n this.connected_ = false;\r\n /**\r\n * Tells that controller has subscribed for Mutation Events.\r\n *\r\n * @private {boolean}\r\n */\r\n this.mutationEventsAdded_ = false;\r\n /**\r\n * Keeps reference to the instance of MutationObserver.\r\n *\r\n * @private {MutationObserver}\r\n */\r\n this.mutationsObserver_ = null;\r\n /**\r\n * A list of connected observers.\r\n *\r\n * @private {Array}\r\n */\r\n this.observers_ = [];\r\n this.onTransitionEnd_ = this.onTransitionEnd_.bind(this);\r\n this.refresh = throttle(this.refresh.bind(this), REFRESH_DELAY);\r\n }\r\n /**\r\n * Adds observer to observers list.\r\n *\r\n * @param {ResizeObserverSPI} observer - Observer to be added.\r\n * @returns {void}\r\n */\r\n ResizeObserverController.prototype.addObserver = function (observer) {\r\n if (!~this.observers_.indexOf(observer)) {\r\n this.observers_.push(observer);\r\n }\r\n // Add listeners if they haven't been added yet.\r\n if (!this.connected_) {\r\n this.connect_();\r\n }\r\n };\r\n /**\r\n * Removes observer from observers list.\r\n *\r\n * @param {ResizeObserverSPI} observer - Observer to be removed.\r\n * @returns {void}\r\n */\r\n ResizeObserverController.prototype.removeObserver = function (observer) {\r\n var observers = this.observers_;\r\n var index = observers.indexOf(observer);\r\n // Remove observer if it's present in registry.\r\n if (~index) {\r\n observers.splice(index, 1);\r\n }\r\n // Remove listeners if controller has no connected observers.\r\n if (!observers.length && this.connected_) {\r\n this.disconnect_();\r\n }\r\n };\r\n /**\r\n * Invokes the update of observers. It will continue running updates insofar\r\n * it detects changes.\r\n *\r\n * @returns {void}\r\n */\r\n ResizeObserverController.prototype.refresh = function () {\r\n var changesDetected = this.updateObservers_();\r\n // Continue running updates if changes have been detected as there might\r\n // be future ones caused by CSS transitions.\r\n if (changesDetected) {\r\n this.refresh();\r\n }\r\n };\r\n /**\r\n * Updates every observer from observers list and notifies them of queued\r\n * entries.\r\n *\r\n * @private\r\n * @returns {boolean} Returns \"true\" if any observer has detected changes in\r\n * dimensions of it's elements.\r\n */\r\n ResizeObserverController.prototype.updateObservers_ = function () {\r\n // Collect observers that have active observations.\r\n var activeObservers = this.observers_.filter(function (observer) {\r\n return observer.gatherActive(), observer.hasActive();\r\n });\r\n // Deliver notifications in a separate cycle in order to avoid any\r\n // collisions between observers, e.g. when multiple instances of\r\n // ResizeObserver are tracking the same element and the callback of one\r\n // of them changes content dimensions of the observed target. Sometimes\r\n // this may result in notifications being blocked for the rest of observers.\r\n activeObservers.forEach(function (observer) { return observer.broadcastActive(); });\r\n return activeObservers.length > 0;\r\n };\r\n /**\r\n * Initializes DOM listeners.\r\n *\r\n * @private\r\n * @returns {void}\r\n */\r\n ResizeObserverController.prototype.connect_ = function () {\r\n // Do nothing if running in a non-browser environment or if listeners\r\n // have been already added.\r\n if (!isBrowser || this.connected_) {\r\n return;\r\n }\r\n // Subscription to the \"Transitionend\" event is used as a workaround for\r\n // delayed transitions. This way it's possible to capture at least the\r\n // final state of an element.\r\n document.addEventListener('transitionend', this.onTransitionEnd_);\r\n window.addEventListener('resize', this.refresh);\r\n if (mutationObserverSupported) {\r\n this.mutationsObserver_ = new MutationObserver(this.refresh);\r\n this.mutationsObserver_.observe(document, {\r\n attributes: true,\r\n childList: true,\r\n characterData: true,\r\n subtree: true\r\n });\r\n }\r\n else {\r\n document.addEventListener('DOMSubtreeModified', this.refresh);\r\n this.mutationEventsAdded_ = true;\r\n }\r\n this.connected_ = true;\r\n };\r\n /**\r\n * Removes DOM listeners.\r\n *\r\n * @private\r\n * @returns {void}\r\n */\r\n ResizeObserverController.prototype.disconnect_ = function () {\r\n // Do nothing if running in a non-browser environment or if listeners\r\n // have been already removed.\r\n if (!isBrowser || !this.connected_) {\r\n return;\r\n }\r\n document.removeEventListener('transitionend', this.onTransitionEnd_);\r\n window.removeEventListener('resize', this.refresh);\r\n if (this.mutationsObserver_) {\r\n this.mutationsObserver_.disconnect();\r\n }\r\n if (this.mutationEventsAdded_) {\r\n document.removeEventListener('DOMSubtreeModified', this.refresh);\r\n }\r\n this.mutationsObserver_ = null;\r\n this.mutationEventsAdded_ = false;\r\n this.connected_ = false;\r\n };\r\n /**\r\n * \"Transitionend\" event handler.\r\n *\r\n * @private\r\n * @param {TransitionEvent} event\r\n * @returns {void}\r\n */\r\n ResizeObserverController.prototype.onTransitionEnd_ = function (_a) {\r\n var _b = _a.propertyName, propertyName = _b === void 0 ? '' : _b;\r\n // Detect whether transition may affect dimensions of an element.\r\n var isReflowProperty = transitionKeys.some(function (key) {\r\n return !!~propertyName.indexOf(key);\r\n });\r\n if (isReflowProperty) {\r\n this.refresh();\r\n }\r\n };\r\n /**\r\n * Returns instance of the ResizeObserverController.\r\n *\r\n * @returns {ResizeObserverController}\r\n */\r\n ResizeObserverController.getInstance = function () {\r\n if (!this.instance_) {\r\n this.instance_ = new ResizeObserverController();\r\n }\r\n return this.instance_;\r\n };\r\n /**\r\n * Holds reference to the controller's instance.\r\n *\r\n * @private {ResizeObserverController}\r\n */\r\n ResizeObserverController.instance_ = null;\r\n return ResizeObserverController;\r\n}());\n\n/**\r\n * Defines non-writable/enumerable properties of the provided target object.\r\n *\r\n * @param {Object} target - Object for which to define properties.\r\n * @param {Object} props - Properties to be defined.\r\n * @returns {Object} Target object.\r\n */\r\nvar defineConfigurable = (function (target, props) {\r\n for (var _i = 0, _a = Object.keys(props); _i < _a.length; _i++) {\r\n var key = _a[_i];\r\n Object.defineProperty(target, key, {\r\n value: props[key],\r\n enumerable: false,\r\n writable: false,\r\n configurable: true\r\n });\r\n }\r\n return target;\r\n});\n\n/**\r\n * Returns the global object associated with provided element.\r\n *\r\n * @param {Object} target\r\n * @returns {Object}\r\n */\r\nvar getWindowOf = (function (target) {\r\n // Assume that the element is an instance of Node, which means that it\r\n // has the \"ownerDocument\" property from which we can retrieve a\r\n // corresponding global object.\r\n var ownerGlobal = target && target.ownerDocument && target.ownerDocument.defaultView;\r\n // Return the local global object if it's not possible extract one from\r\n // provided element.\r\n return ownerGlobal || global$1;\r\n});\n\n// Placeholder of an empty content rectangle.\r\nvar emptyRect = createRectInit(0, 0, 0, 0);\r\n/**\r\n * Converts provided string to a number.\r\n *\r\n * @param {number|string} value\r\n * @returns {number}\r\n */\r\nfunction toFloat(value) {\r\n return parseFloat(value) || 0;\r\n}\r\n/**\r\n * Extracts borders size from provided styles.\r\n *\r\n * @param {CSSStyleDeclaration} styles\r\n * @param {...string} positions - Borders positions (top, right, ...)\r\n * @returns {number}\r\n */\r\nfunction getBordersSize(styles) {\r\n var positions = [];\r\n for (var _i = 1; _i < arguments.length; _i++) {\r\n positions[_i - 1] = arguments[_i];\r\n }\r\n return positions.reduce(function (size, position) {\r\n var value = styles['border-' + position + '-width'];\r\n return size + toFloat(value);\r\n }, 0);\r\n}\r\n/**\r\n * Extracts paddings sizes from provided styles.\r\n *\r\n * @param {CSSStyleDeclaration} styles\r\n * @returns {Object} Paddings box.\r\n */\r\nfunction getPaddings(styles) {\r\n var positions = ['top', 'right', 'bottom', 'left'];\r\n var paddings = {};\r\n for (var _i = 0, positions_1 = positions; _i < positions_1.length; _i++) {\r\n var position = positions_1[_i];\r\n var value = styles['padding-' + position];\r\n paddings[position] = toFloat(value);\r\n }\r\n return paddings;\r\n}\r\n/**\r\n * Calculates content rectangle of provided SVG element.\r\n *\r\n * @param {SVGGraphicsElement} target - Element content rectangle of which needs\r\n * to be calculated.\r\n * @returns {DOMRectInit}\r\n */\r\nfunction getSVGContentRect(target) {\r\n var bbox = target.getBBox();\r\n return createRectInit(0, 0, bbox.width, bbox.height);\r\n}\r\n/**\r\n * Calculates content rectangle of provided HTMLElement.\r\n *\r\n * @param {HTMLElement} target - Element for which to calculate the content rectangle.\r\n * @returns {DOMRectInit}\r\n */\r\nfunction getHTMLElementContentRect(target) {\r\n // Client width & height properties can't be\r\n // used exclusively as they provide rounded values.\r\n var clientWidth = target.clientWidth, clientHeight = target.clientHeight;\r\n // By this condition we can catch all non-replaced inline, hidden and\r\n // detached elements. Though elements with width & height properties less\r\n // than 0.5 will be discarded as well.\r\n //\r\n // Without it we would need to implement separate methods for each of\r\n // those cases and it's not possible to perform a precise and performance\r\n // effective test for hidden elements. E.g. even jQuery's ':visible' filter\r\n // gives wrong results for elements with width & height less than 0.5.\r\n if (!clientWidth && !clientHeight) {\r\n return emptyRect;\r\n }\r\n var styles = getWindowOf(target).getComputedStyle(target);\r\n var paddings = getPaddings(styles);\r\n var horizPad = paddings.left + paddings.right;\r\n var vertPad = paddings.top + paddings.bottom;\r\n // Computed styles of width & height are being used because they are the\r\n // only dimensions available to JS that contain non-rounded values. It could\r\n // be possible to utilize the getBoundingClientRect if only it's data wasn't\r\n // affected by CSS transformations let alone paddings, borders and scroll bars.\r\n var width = toFloat(styles.width), height = toFloat(styles.height);\r\n // Width & height include paddings and borders when the 'border-box' box\r\n // model is applied (except for IE).\r\n if (styles.boxSizing === 'border-box') {\r\n // Following conditions are required to handle Internet Explorer which\r\n // doesn't include paddings and borders to computed CSS dimensions.\r\n //\r\n // We can say that if CSS dimensions + paddings are equal to the \"client\"\r\n // properties then it's either IE, and thus we don't need to subtract\r\n // anything, or an element merely doesn't have paddings/borders styles.\r\n if (Math.round(width + horizPad) !== clientWidth) {\r\n width -= getBordersSize(styles, 'left', 'right') + horizPad;\r\n }\r\n if (Math.round(height + vertPad) !== clientHeight) {\r\n height -= getBordersSize(styles, 'top', 'bottom') + vertPad;\r\n }\r\n }\r\n // Following steps can't be applied to the document's root element as its\r\n // client[Width/Height] properties represent viewport area of the window.\r\n // Besides, it's as well not necessary as the itself neither has\r\n // rendered scroll bars nor it can be clipped.\r\n if (!isDocumentElement(target)) {\r\n // In some browsers (only in Firefox, actually) CSS width & height\r\n // include scroll bars size which can be removed at this step as scroll\r\n // bars are the only difference between rounded dimensions + paddings\r\n // and \"client\" properties, though that is not always true in Chrome.\r\n var vertScrollbar = Math.round(width + horizPad) - clientWidth;\r\n var horizScrollbar = Math.round(height + vertPad) - clientHeight;\r\n // Chrome has a rather weird rounding of \"client\" properties.\r\n // E.g. for an element with content width of 314.2px it sometimes gives\r\n // the client width of 315px and for the width of 314.7px it may give\r\n // 314px. And it doesn't happen all the time. So just ignore this delta\r\n // as a non-relevant.\r\n if (Math.abs(vertScrollbar) !== 1) {\r\n width -= vertScrollbar;\r\n }\r\n if (Math.abs(horizScrollbar) !== 1) {\r\n height -= horizScrollbar;\r\n }\r\n }\r\n return createRectInit(paddings.left, paddings.top, width, height);\r\n}\r\n/**\r\n * Checks whether provided element is an instance of the SVGGraphicsElement.\r\n *\r\n * @param {Element} target - Element to be checked.\r\n * @returns {boolean}\r\n */\r\nvar isSVGGraphicsElement = (function () {\r\n // Some browsers, namely IE and Edge, don't have the SVGGraphicsElement\r\n // interface.\r\n if (typeof SVGGraphicsElement !== 'undefined') {\r\n return function (target) { return target instanceof getWindowOf(target).SVGGraphicsElement; };\r\n }\r\n // If it's so, then check that element is at least an instance of the\r\n // SVGElement and that it has the \"getBBox\" method.\r\n // eslint-disable-next-line no-extra-parens\r\n return function (target) { return (target instanceof getWindowOf(target).SVGElement &&\r\n typeof target.getBBox === 'function'); };\r\n})();\r\n/**\r\n * Checks whether provided element is a document element ().\r\n *\r\n * @param {Element} target - Element to be checked.\r\n * @returns {boolean}\r\n */\r\nfunction isDocumentElement(target) {\r\n return target === getWindowOf(target).document.documentElement;\r\n}\r\n/**\r\n * Calculates an appropriate content rectangle for provided html or svg element.\r\n *\r\n * @param {Element} target - Element content rectangle of which needs to be calculated.\r\n * @returns {DOMRectInit}\r\n */\r\nfunction getContentRect(target) {\r\n if (!isBrowser) {\r\n return emptyRect;\r\n }\r\n if (isSVGGraphicsElement(target)) {\r\n return getSVGContentRect(target);\r\n }\r\n return getHTMLElementContentRect(target);\r\n}\r\n/**\r\n * Creates rectangle with an interface of the DOMRectReadOnly.\r\n * Spec: https://drafts.fxtf.org/geometry/#domrectreadonly\r\n *\r\n * @param {DOMRectInit} rectInit - Object with rectangle's x/y coordinates and dimensions.\r\n * @returns {DOMRectReadOnly}\r\n */\r\nfunction createReadOnlyRect(_a) {\r\n var x = _a.x, y = _a.y, width = _a.width, height = _a.height;\r\n // If DOMRectReadOnly is available use it as a prototype for the rectangle.\r\n var Constr = typeof DOMRectReadOnly !== 'undefined' ? DOMRectReadOnly : Object;\r\n var rect = Object.create(Constr.prototype);\r\n // Rectangle's properties are not writable and non-enumerable.\r\n defineConfigurable(rect, {\r\n x: x, y: y, width: width, height: height,\r\n top: y,\r\n right: x + width,\r\n bottom: height + y,\r\n left: x\r\n });\r\n return rect;\r\n}\r\n/**\r\n * Creates DOMRectInit object based on the provided dimensions and the x/y coordinates.\r\n * Spec: https://drafts.fxtf.org/geometry/#dictdef-domrectinit\r\n *\r\n * @param {number} x - X coordinate.\r\n * @param {number} y - Y coordinate.\r\n * @param {number} width - Rectangle's width.\r\n * @param {number} height - Rectangle's height.\r\n * @returns {DOMRectInit}\r\n */\r\nfunction createRectInit(x, y, width, height) {\r\n return { x: x, y: y, width: width, height: height };\r\n}\n\n/**\r\n * Class that is responsible for computations of the content rectangle of\r\n * provided DOM element and for keeping track of it's changes.\r\n */\r\nvar ResizeObservation = /** @class */ (function () {\r\n /**\r\n * Creates an instance of ResizeObservation.\r\n *\r\n * @param {Element} target - Element to be observed.\r\n */\r\n function ResizeObservation(target) {\r\n /**\r\n * Broadcasted width of content rectangle.\r\n *\r\n * @type {number}\r\n */\r\n this.broadcastWidth = 0;\r\n /**\r\n * Broadcasted height of content rectangle.\r\n *\r\n * @type {number}\r\n */\r\n this.broadcastHeight = 0;\r\n /**\r\n * Reference to the last observed content rectangle.\r\n *\r\n * @private {DOMRectInit}\r\n */\r\n this.contentRect_ = createRectInit(0, 0, 0, 0);\r\n this.target = target;\r\n }\r\n /**\r\n * Updates content rectangle and tells whether it's width or height properties\r\n * have changed since the last broadcast.\r\n *\r\n * @returns {boolean}\r\n */\r\n ResizeObservation.prototype.isActive = function () {\r\n var rect = getContentRect(this.target);\r\n this.contentRect_ = rect;\r\n return (rect.width !== this.broadcastWidth ||\r\n rect.height !== this.broadcastHeight);\r\n };\r\n /**\r\n * Updates 'broadcastWidth' and 'broadcastHeight' properties with a data\r\n * from the corresponding properties of the last observed content rectangle.\r\n *\r\n * @returns {DOMRectInit} Last observed content rectangle.\r\n */\r\n ResizeObservation.prototype.broadcastRect = function () {\r\n var rect = this.contentRect_;\r\n this.broadcastWidth = rect.width;\r\n this.broadcastHeight = rect.height;\r\n return rect;\r\n };\r\n return ResizeObservation;\r\n}());\n\nvar ResizeObserverEntry = /** @class */ (function () {\r\n /**\r\n * Creates an instance of ResizeObserverEntry.\r\n *\r\n * @param {Element} target - Element that is being observed.\r\n * @param {DOMRectInit} rectInit - Data of the element's content rectangle.\r\n */\r\n function ResizeObserverEntry(target, rectInit) {\r\n var contentRect = createReadOnlyRect(rectInit);\r\n // According to the specification following properties are not writable\r\n // and are also not enumerable in the native implementation.\r\n //\r\n // Property accessors are not being used as they'd require to define a\r\n // private WeakMap storage which may cause memory leaks in browsers that\r\n // don't support this type of collections.\r\n defineConfigurable(this, { target: target, contentRect: contentRect });\r\n }\r\n return ResizeObserverEntry;\r\n}());\n\nvar ResizeObserverSPI = /** @class */ (function () {\r\n /**\r\n * Creates a new instance of ResizeObserver.\r\n *\r\n * @param {ResizeObserverCallback} callback - Callback function that is invoked\r\n * when one of the observed elements changes it's content dimensions.\r\n * @param {ResizeObserverController} controller - Controller instance which\r\n * is responsible for the updates of observer.\r\n * @param {ResizeObserver} callbackCtx - Reference to the public\r\n * ResizeObserver instance which will be passed to callback function.\r\n */\r\n function ResizeObserverSPI(callback, controller, callbackCtx) {\r\n /**\r\n * Collection of resize observations that have detected changes in dimensions\r\n * of elements.\r\n *\r\n * @private {Array}\r\n */\r\n this.activeObservations_ = [];\r\n /**\r\n * Registry of the ResizeObservation instances.\r\n *\r\n * @private {Map}\r\n */\r\n this.observations_ = new MapShim();\r\n if (typeof callback !== 'function') {\r\n throw new TypeError('The callback provided as parameter 1 is not a function.');\r\n }\r\n this.callback_ = callback;\r\n this.controller_ = controller;\r\n this.callbackCtx_ = callbackCtx;\r\n }\r\n /**\r\n * Starts observing provided element.\r\n *\r\n * @param {Element} target - Element to be observed.\r\n * @returns {void}\r\n */\r\n ResizeObserverSPI.prototype.observe = function (target) {\r\n if (!arguments.length) {\r\n throw new TypeError('1 argument required, but only 0 present.');\r\n }\r\n // Do nothing if current environment doesn't have the Element interface.\r\n if (typeof Element === 'undefined' || !(Element instanceof Object)) {\r\n return;\r\n }\r\n if (!(target instanceof getWindowOf(target).Element)) {\r\n throw new TypeError('parameter 1 is not of type \"Element\".');\r\n }\r\n var observations = this.observations_;\r\n // Do nothing if element is already being observed.\r\n if (observations.has(target)) {\r\n return;\r\n }\r\n observations.set(target, new ResizeObservation(target));\r\n this.controller_.addObserver(this);\r\n // Force the update of observations.\r\n this.controller_.refresh();\r\n };\r\n /**\r\n * Stops observing provided element.\r\n *\r\n * @param {Element} target - Element to stop observing.\r\n * @returns {void}\r\n */\r\n ResizeObserverSPI.prototype.unobserve = function (target) {\r\n if (!arguments.length) {\r\n throw new TypeError('1 argument required, but only 0 present.');\r\n }\r\n // Do nothing if current environment doesn't have the Element interface.\r\n if (typeof Element === 'undefined' || !(Element instanceof Object)) {\r\n return;\r\n }\r\n if (!(target instanceof getWindowOf(target).Element)) {\r\n throw new TypeError('parameter 1 is not of type \"Element\".');\r\n }\r\n var observations = this.observations_;\r\n // Do nothing if element is not being observed.\r\n if (!observations.has(target)) {\r\n return;\r\n }\r\n observations.delete(target);\r\n if (!observations.size) {\r\n this.controller_.removeObserver(this);\r\n }\r\n };\r\n /**\r\n * Stops observing all elements.\r\n *\r\n * @returns {void}\r\n */\r\n ResizeObserverSPI.prototype.disconnect = function () {\r\n this.clearActive();\r\n this.observations_.clear();\r\n this.controller_.removeObserver(this);\r\n };\r\n /**\r\n * Collects observation instances the associated element of which has changed\r\n * it's content rectangle.\r\n *\r\n * @returns {void}\r\n */\r\n ResizeObserverSPI.prototype.gatherActive = function () {\r\n var _this = this;\r\n this.clearActive();\r\n this.observations_.forEach(function (observation) {\r\n if (observation.isActive()) {\r\n _this.activeObservations_.push(observation);\r\n }\r\n });\r\n };\r\n /**\r\n * Invokes initial callback function with a list of ResizeObserverEntry\r\n * instances collected from active resize observations.\r\n *\r\n * @returns {void}\r\n */\r\n ResizeObserverSPI.prototype.broadcastActive = function () {\r\n // Do nothing if observer doesn't have active observations.\r\n if (!this.hasActive()) {\r\n return;\r\n }\r\n var ctx = this.callbackCtx_;\r\n // Create ResizeObserverEntry instance for every active observation.\r\n var entries = this.activeObservations_.map(function (observation) {\r\n return new ResizeObserverEntry(observation.target, observation.broadcastRect());\r\n });\r\n this.callback_.call(ctx, entries, ctx);\r\n this.clearActive();\r\n };\r\n /**\r\n * Clears the collection of active observations.\r\n *\r\n * @returns {void}\r\n */\r\n ResizeObserverSPI.prototype.clearActive = function () {\r\n this.activeObservations_.splice(0);\r\n };\r\n /**\r\n * Tells whether observer has active observations.\r\n *\r\n * @returns {boolean}\r\n */\r\n ResizeObserverSPI.prototype.hasActive = function () {\r\n return this.activeObservations_.length > 0;\r\n };\r\n return ResizeObserverSPI;\r\n}());\n\n// Registry of internal observers. If WeakMap is not available use current shim\r\n// for the Map collection as it has all required methods and because WeakMap\r\n// can't be fully polyfilled anyway.\r\nvar observers = typeof WeakMap !== 'undefined' ? new WeakMap() : new MapShim();\r\n/**\r\n * ResizeObserver API. Encapsulates the ResizeObserver SPI implementation\r\n * exposing only those methods and properties that are defined in the spec.\r\n */\r\nvar ResizeObserver = /** @class */ (function () {\r\n /**\r\n * Creates a new instance of ResizeObserver.\r\n *\r\n * @param {ResizeObserverCallback} callback - Callback that is invoked when\r\n * dimensions of the observed elements change.\r\n */\r\n function ResizeObserver(callback) {\r\n if (!(this instanceof ResizeObserver)) {\r\n throw new TypeError('Cannot call a class as a function.');\r\n }\r\n if (!arguments.length) {\r\n throw new TypeError('1 argument required, but only 0 present.');\r\n }\r\n var controller = ResizeObserverController.getInstance();\r\n var observer = new ResizeObserverSPI(callback, controller, this);\r\n observers.set(this, observer);\r\n }\r\n return ResizeObserver;\r\n}());\r\n// Expose public methods of ResizeObserver.\r\n[\r\n 'observe',\r\n 'unobserve',\r\n 'disconnect'\r\n].forEach(function (method) {\r\n ResizeObserver.prototype[method] = function () {\r\n var _a;\r\n return (_a = observers.get(this))[method].apply(_a, arguments);\r\n };\r\n});\n\nvar index = (function () {\r\n // Export existing implementation if available.\r\n if (typeof global$1.ResizeObserver !== 'undefined') {\r\n return global$1.ResizeObserver;\r\n }\r\n return ResizeObserver;\r\n})();\n\nexport default index;\n","import { Observable } from '../Observable';\nimport { from } from './from';\nexport function defer(observableFactory) {\n return new Observable(subscriber => {\n let input;\n try {\n input = observableFactory();\n }\n catch (err) {\n subscriber.error(err);\n return undefined;\n }\n const source = from(input);\n return source.subscribe(subscriber);\n });\n}\n//# sourceMappingURL=defer.js.map","/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n","import { operate } from '../util/lift';\nexport function finalize(callback) {\n return operate((source, subscriber) => {\n source.subscribe(subscriber);\n subscriber.add(callback);\n });\n}\n//# sourceMappingURL=finalize.js.map","import { isFunction } from '../util/isFunction';\nimport { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nimport { identity } from '../util/identity';\nexport function tap(observerOrNext, error, complete) {\n const tapObserver = isFunction(observerOrNext) || error || complete ? { next: observerOrNext, error, complete } : observerOrNext;\n return tapObserver\n ? operate((source, subscriber) => {\n source.subscribe(new OperatorSubscriber(subscriber, (value) => {\n var _a;\n (_a = tapObserver.next) === null || _a === void 0 ? void 0 : _a.call(tapObserver, value);\n subscriber.next(value);\n }, (err) => {\n var _a;\n (_a = tapObserver.error) === null || _a === void 0 ? void 0 : _a.call(tapObserver, err);\n subscriber.error(err);\n }, () => {\n var _a;\n (_a = tapObserver.complete) === null || _a === void 0 ? void 0 : _a.call(tapObserver);\n subscriber.complete();\n }));\n })\n :\n identity;\n}\n//# sourceMappingURL=tap.js.map","import { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function scan(accumulator, seed) {\n const hasSeed = arguments.length >= 2;\n return operate((source, subscriber) => {\n let hasState = hasSeed;\n let state = seed;\n let index = 0;\n source.subscribe(new OperatorSubscriber(subscriber, (value) => {\n const i = index++;\n subscriber.next((state = hasState\n ?\n accumulator(state, value, i)\n :\n ((hasState = true), value)));\n }));\n });\n}\n//# sourceMappingURL=scan.js.map","import { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function observeOn(scheduler, delay = 0) {\n return operate((source, subscriber) => {\n source.subscribe(new OperatorSubscriber(subscriber, (value) => subscriber.add(scheduler.schedule(() => subscriber.next(value), delay)), (err) => subscriber.add(scheduler.schedule(() => subscriber.error(err), delay)), () => subscriber.add(scheduler.schedule(() => subscriber.complete(), delay))));\n });\n}\n//# sourceMappingURL=observeOn.js.map","import { Subscription } from '../Subscription';\nexport const animationFrameProvider = {\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel === null || cancel === void 0 ? void 0 : cancel(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return ((delegate === null || delegate === void 0 ? void 0 : delegate.requestAnimationFrame) || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return ((delegate === null || delegate === void 0 ? void 0 : delegate.cancelAnimationFrame) || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n//# sourceMappingURL=animationFrameProvider.js.map","import { AsyncAction } from './AsyncAction';\nimport { animationFrameProvider } from './animationFrameProvider';\nexport class AnimationFrameAction extends AsyncAction {\n constructor(scheduler, work) {\n super(scheduler, work);\n this.scheduler = scheduler;\n this.work = work;\n }\n requestAsyncId(scheduler, id, delay = 0) {\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n scheduler.actions.push(this);\n return scheduler.scheduled || (scheduler.scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n recycleAsyncId(scheduler, id, delay = 0) {\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n if (scheduler.actions.length === 0) {\n animationFrameProvider.cancelAnimationFrame(id);\n scheduler.scheduled = undefined;\n }\n return undefined;\n }\n}\n//# sourceMappingURL=AnimationFrameAction.js.map","import { AsyncScheduler } from './AsyncScheduler';\nexport class AnimationFrameScheduler extends AsyncScheduler {\n flush(action) {\n this.active = true;\n this.scheduled = undefined;\n const { actions } = this;\n let error;\n let index = -1;\n action = action || actions.shift();\n let count = actions.length;\n do {\n if (error = action.execute(action.state, action.delay)) {\n break;\n }\n } while (++index < count && (action = actions.shift()));\n this.active = false;\n if (error) {\n while (++index < count && (action = actions.shift())) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n//# sourceMappingURL=AnimationFrameScheduler.js.map","import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\nexport const animationFrame = animationFrameScheduler;\n//# sourceMappingURL=animationFrame.js.map","(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesWhitelist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesWhitelist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. ¯\\_(ツ)_/¯\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n","var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function(\"return this\")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n","import { ReplaySubject } from '../ReplaySubject';\nimport { operate } from '../util/lift';\nexport function shareReplay(configOrBufferSize, windowTime, scheduler) {\n let config;\n if (configOrBufferSize && typeof configOrBufferSize === 'object') {\n config = configOrBufferSize;\n }\n else {\n config = {\n bufferSize: configOrBufferSize,\n windowTime,\n refCount: false,\n scheduler\n };\n }\n return operate(shareReplayOperator(config));\n}\nfunction shareReplayOperator({ bufferSize = Infinity, windowTime = Infinity, refCount: useRefCount, scheduler }) {\n let subject;\n let refCount = 0;\n let subscription;\n return (source, subscriber) => {\n refCount++;\n let innerSub;\n if (!subject) {\n subject = new ReplaySubject(bufferSize, windowTime, scheduler);\n innerSub = subject.subscribe(subscriber);\n subscription = source.subscribe({\n next(value) { subject.next(value); },\n error(err) {\n const dest = subject;\n subscription = undefined;\n subject = undefined;\n dest.error(err);\n },\n complete() {\n subscription = undefined;\n subject.complete();\n },\n });\n if (subscription.closed) {\n subscription = undefined;\n }\n }\n else {\n innerSub = subject.subscribe(subscriber);\n }\n subscriber.add(() => {\n refCount--;\n innerSub.unsubscribe();\n if (useRefCount && refCount === 0 && subscription) {\n subscription.unsubscribe();\n subscription = undefined;\n subject = undefined;\n }\n });\n };\n}\n//# sourceMappingURL=shareReplay.js.map","import { distinctUntilChanged } from './distinctUntilChanged';\nexport function distinctUntilKeyChanged(key, compare) {\n return distinctUntilChanged((x, y) => compare ? compare(x[key], y[key]) : x[key] === y[key]);\n}\n//# sourceMappingURL=distinctUntilKeyChanged.js.map","import { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nimport { from } from '../observable/from';\nimport { identity } from '../util/identity';\nimport { noop } from '../util/noop';\nexport function withLatestFrom(...inputs) {\n let project;\n if (typeof inputs[inputs.length - 1] === 'function') {\n project = inputs.pop();\n }\n return operate((source, subscriber) => {\n const len = inputs.length;\n const otherValues = new Array(len);\n let hasValue = inputs.map(() => false);\n let ready = false;\n source.subscribe(new OperatorSubscriber(subscriber, (value) => {\n if (ready) {\n const values = [value, ...otherValues];\n subscriber.next(project ? project(...values) : values);\n }\n }));\n for (let i = 0; i < len; i++) {\n const input = inputs[i];\n let otherSource;\n try {\n otherSource = from(input);\n }\n catch (err) {\n subscriber.error(err);\n return;\n }\n otherSource.subscribe(new OperatorSubscriber(subscriber, (value) => {\n otherValues[i] = value;\n if (!ready && !hasValue[i]) {\n hasValue[i] = true;\n (ready = hasValue.every(identity)) && (hasValue = null);\n }\n }, undefined, noop));\n }\n });\n}\n//# sourceMappingURL=withLatestFrom.js.map","import { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nimport { arrRemove } from '../util/arrRemove';\nexport function bufferCount(bufferSize, startBufferEvery = null) {\n startBufferEvery = startBufferEvery !== null && startBufferEvery !== void 0 ? startBufferEvery : bufferSize;\n return operate((source, subscriber) => {\n let buffers = [];\n let count = 0;\n source.subscribe(new OperatorSubscriber(subscriber, (value) => {\n let toEmit = null;\n if (count++ % startBufferEvery === 0) {\n buffers.push([]);\n }\n for (const buffer of buffers) {\n buffer.push(value);\n if (bufferSize <= buffer.length) {\n toEmit = toEmit !== null && toEmit !== void 0 ? toEmit : [];\n toEmit.push(buffer);\n }\n }\n if (toEmit) {\n for (const buffer of toEmit) {\n arrRemove(buffers, buffer);\n subscriber.next(buffer);\n }\n }\n }, undefined, () => {\n for (const buffer of buffers) {\n subscriber.next(buffer);\n }\n subscriber.complete();\n }, () => {\n buffers = null;\n }));\n });\n}\n//# sourceMappingURL=bufferCount.js.map","import { concatAll } from '../operators/concatAll';\nimport { isScheduler } from '../util/isScheduler';\nimport { fromArray } from './fromArray';\nexport function concat(...args) {\n let scheduler;\n if (isScheduler(args[args.length - 1])) {\n scheduler = args.pop();\n }\n return concatAll()(fromArray(args, scheduler));\n}\n//# sourceMappingURL=concat.js.map","import { mergeAll } from './mergeAll';\nexport function concatAll() {\n return mergeAll(1);\n}\n//# sourceMappingURL=concatAll.js.map","import { concat } from '../observable/concat';\nimport { isScheduler } from '../util/isScheduler';\nexport function startWith(...values) {\n const scheduler = values[values.length - 1];\n if (isScheduler(scheduler)) {\n values.pop();\n return (source) => concat(values, source, scheduler);\n }\n else {\n return (source) => concat(values, source);\n }\n}\n//# sourceMappingURL=startWith.js.map","import { Observable } from '../Observable';\nimport { mergeMap } from '../operators/mergeMap';\nimport { isArrayLike } from '../util/isArrayLike';\nimport { isFunction } from '../util/isFunction';\nimport { mapOneOrManyArgs } from '../util/mapOneOrManyArgs';\nimport { fromArray } from './fromArray';\nexport function fromEvent(target, eventName, options, resultSelector) {\n if (isFunction(options)) {\n resultSelector = options;\n options = undefined;\n }\n if (resultSelector) {\n return fromEvent(target, eventName, options).pipe(mapOneOrManyArgs(resultSelector));\n }\n return new Observable((subscriber) => {\n const handler = (...args) => subscriber.next(args.length > 1 ? args : args[0]);\n if (isEventTarget(target)) {\n target.addEventListener(eventName, handler, options);\n return () => target.removeEventListener(eventName, handler, options);\n }\n if (isJQueryStyleEventEmitter(target)) {\n target.on(eventName, handler);\n return () => target.off(eventName, handler);\n }\n if (isNodeStyleEventEmitter(target)) {\n target.addListener(eventName, handler);\n return () => target.removeListener(eventName, handler);\n }\n if (isArrayLike(target)) {\n return mergeMap((target) => fromEvent(target, eventName, options))(fromArray(target)).subscribe(subscriber);\n }\n subscriber.error(new TypeError('Invalid event target'));\n return;\n });\n}\nfunction isNodeStyleEventEmitter(sourceObj) {\n return sourceObj && typeof sourceObj.addListener === 'function' && typeof sourceObj.removeListener === 'function';\n}\nfunction isJQueryStyleEventEmitter(sourceObj) {\n return sourceObj && typeof sourceObj.on === 'function' && typeof sourceObj.off === 'function';\n}\nfunction isEventTarget(sourceObj) {\n return sourceObj && typeof sourceObj.addEventListener === 'function' && typeof sourceObj.removeEventListener === 'function';\n}\n//# sourceMappingURL=fromEvent.js.map","import { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function mapTo(value) {\n return operate((source, subscriber) => {\n source.subscribe(new OperatorSubscriber(subscriber, () => subscriber.next(value)));\n });\n}\n//# sourceMappingURL=mapTo.js.map","import { Observable } from '../Observable';\nimport { noop } from '../util/noop';\nexport const NEVER = new Observable(noop);\nexport function never() {\n return NEVER;\n}\n//# sourceMappingURL=never.js.map","import { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function filter(predicate, thisArg) {\n return operate((source, subscriber) => {\n let index = 0;\n source.subscribe(new OperatorSubscriber(subscriber, (value) => predicate.call(thisArg, value, index++) && subscriber.next(value)));\n });\n}\n//# sourceMappingURL=filter.js.map","import { Subject } from './Subject';\nexport class BehaviorSubject extends Subject {\n constructor(_value) {\n super();\n this._value = _value;\n }\n get value() {\n return this.getValue();\n }\n _subscribe(subscriber) {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n getValue() {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n next(value) {\n super.next((this._value = value));\n }\n}\n//# sourceMappingURL=BehaviorSubject.js.map","import { EMPTY } from '../observable/empty';\nimport { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function take(count) {\n return count <= 0\n ? () => EMPTY\n : operate((source, subscriber) => {\n let seen = 0;\n source.subscribe(new OperatorSubscriber(subscriber, (value) => {\n if (++seen <= count) {\n subscriber.next(value);\n if (count <= seen) {\n subscriber.complete();\n }\n }\n }));\n });\n}\n//# sourceMappingURL=take.js.map","import { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nimport { from } from '../observable/from';\nexport const defaultThrottleConfig = {\n leading: true,\n trailing: false,\n};\nexport function throttle(durationSelector, { leading, trailing } = defaultThrottleConfig) {\n return operate((source, subscriber) => {\n let hasValue = false;\n let sendValue = null;\n let throttled = null;\n const throttlingDone = () => {\n throttled === null || throttled === void 0 ? void 0 : throttled.unsubscribe();\n throttled = null;\n trailing && send();\n };\n const throttle = (value) => (throttled = from(durationSelector(value)).subscribe(new OperatorSubscriber(subscriber, throttlingDone, undefined, throttlingDone)));\n const send = () => {\n if (hasValue) {\n subscriber.next(sendValue);\n throttle(sendValue);\n }\n hasValue = false;\n sendValue = null;\n };\n source.subscribe(new OperatorSubscriber(subscriber, (value) => {\n hasValue = true;\n sendValue = value;\n !throttled && (leading ? send() : throttle(value));\n }));\n });\n}\n//# sourceMappingURL=throttle.js.map","import { switchMap } from './switchMap';\nexport function switchMapTo(innerObservable, resultSelector) {\n return resultSelector ? switchMap(() => innerObservable, resultSelector) : switchMap(() => innerObservable);\n}\n//# sourceMappingURL=switchMapTo.js.map","import { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function sample(notifier) {\n return operate((source, subscriber) => {\n let hasValue = false;\n let lastValue = null;\n source.subscribe(new OperatorSubscriber(subscriber, (value) => {\n hasValue = true;\n lastValue = value;\n }));\n const emit = () => {\n if (hasValue) {\n hasValue = false;\n const value = lastValue;\n lastValue = null;\n subscriber.next(value);\n }\n };\n notifier.subscribe(new OperatorSubscriber(subscriber, emit, undefined, emit));\n });\n}\n//# sourceMappingURL=sample.js.map","import { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function skip(count) {\n return operate((source, subscriber) => {\n let seen = 0;\n source.subscribe(new OperatorSubscriber(subscriber, (value) => (count === seen ? subscriber.next(value) : seen++)));\n });\n}\n//# sourceMappingURL=skip.js.map","import { from } from '../observable/from';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nimport { operate } from '../util/lift';\nexport function catchError(selector) {\n return operate((source, subscriber) => {\n let innerSub = null;\n let syncUnsub = false;\n let handledResult;\n innerSub = source.subscribe(new OperatorSubscriber(subscriber, undefined, (err) => {\n handledResult = from(selector(err, catchError(selector)(source)));\n if (innerSub) {\n innerSub.unsubscribe();\n innerSub = null;\n handledResult.subscribe(subscriber);\n }\n else {\n syncUnsub = true;\n }\n }));\n if (syncUnsub) {\n innerSub.unsubscribe();\n innerSub = null;\n handledResult.subscribe(subscriber);\n }\n });\n}\n//# sourceMappingURL=catchError.js.map","import { asyncScheduler } from '../scheduler/async';\nimport { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function debounceTime(dueTime, scheduler = asyncScheduler) {\n return operate((source, subscriber) => {\n let hasValue = false;\n let lastValue = null;\n let debounceSubscription = null;\n const emitLastValue = () => {\n hasValue = false;\n const value = lastValue;\n lastValue = null;\n subscriber.next(value);\n };\n source.subscribe(new OperatorSubscriber(subscriber, (value) => {\n debounceSubscription === null || debounceSubscription === void 0 ? void 0 : debounceSubscription.unsubscribe();\n hasValue = true;\n lastValue = value;\n subscriber.add((debounceSubscription = scheduler.schedule(() => {\n debounceSubscription = null;\n emitLastValue();\n }, dueTime)));\n }, undefined, () => {\n hasValue && emitLastValue();\n subscriber.complete();\n }));\n });\n}\n//# sourceMappingURL=debounceTime.js.map","import { mergeMap } from './mergeMap';\nexport function concatMap(project, resultSelector) {\n if (typeof resultSelector === 'function') {\n return mergeMap(project, resultSelector, 1);\n }\n return mergeMap(project, 1);\n}\n//# sourceMappingURL=concatMap.js.map","import { defer } from './defer';\nimport { EMPTY } from './empty';\nexport function iif(condition, trueResult = EMPTY, falseResult = EMPTY) {\n return defer(() => condition() ? trueResult : falseResult);\n}\n//# sourceMappingURL=iif.js.map","import { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function refCount() {\n return operate((source, subscriber) => {\n let connection = null;\n source._refCount++;\n const refCounter = new OperatorSubscriber(subscriber, undefined, undefined, undefined, () => {\n if (!source || source._refCount <= 0 || 0 < --source._refCount) {\n connection = null;\n return;\n }\n const sharedConnection = source._connection;\n const conn = connection;\n connection = null;\n if (sharedConnection && (!conn || sharedConnection === conn)) {\n sharedConnection.unsubscribe();\n }\n subscriber.unsubscribe();\n });\n source.subscribe(refCounter);\n if (!refCounter.closed) {\n connection = source.connect();\n }\n });\n}\n//# sourceMappingURL=refCount.js.map","import { Observable } from '../Observable';\nimport { Subscription } from '../Subscription';\nimport { refCount as higherOrderRefCount } from '../operators/refCount';\nimport { OperatorSubscriber } from '../operators/OperatorSubscriber';\nexport class ConnectableObservable extends Observable {\n constructor(source, subjectFactory) {\n super();\n this.source = source;\n this.subjectFactory = subjectFactory;\n this._subject = null;\n this._refCount = 0;\n this._connection = null;\n }\n _subscribe(subscriber) {\n return this.getSubject().subscribe(subscriber);\n }\n getSubject() {\n const subject = this._subject;\n if (!subject || subject.isStopped) {\n this._subject = this.subjectFactory();\n }\n return this._subject;\n }\n _teardown() {\n this._refCount = 0;\n const { _connection } = this;\n this._subject = this._connection = null;\n _connection === null || _connection === void 0 ? void 0 : _connection.unsubscribe();\n }\n connect() {\n let connection = this._connection;\n if (!connection) {\n connection = this._connection = new Subscription();\n const subject = this.getSubject();\n connection.add(this.source.subscribe(new OperatorSubscriber(subject, undefined, (err) => {\n this._teardown();\n subject.error(err);\n }, () => {\n this._teardown();\n subject.complete();\n }, () => this._teardown())));\n if (connection.closed) {\n this._connection = null;\n connection = Subscription.EMPTY;\n }\n }\n return connection;\n }\n refCount() {\n return higherOrderRefCount()(this);\n }\n}\n//# sourceMappingURL=ConnectableObservable.js.map","import { multicast } from './multicast';\nimport { refCount } from './refCount';\nimport { Subject } from '../Subject';\nfunction shareSubjectFactory() {\n return new Subject();\n}\nexport function share() {\n return (source) => refCount()(multicast(shareSubjectFactory)(source));\n}\n//# sourceMappingURL=share.js.map","import { ConnectableObservable } from '../observable/ConnectableObservable';\nimport { hasLift, operate } from '../util/lift';\nexport function multicast(subjectOrSubjectFactory, selector) {\n const subjectFactory = typeof subjectOrSubjectFactory === 'function' ? subjectOrSubjectFactory : () => subjectOrSubjectFactory;\n if (typeof selector === 'function') {\n return operate((source, subscriber) => {\n const subject = subjectFactory();\n selector(subject).subscribe(subscriber).add(source.subscribe(subject));\n });\n }\n return (source) => {\n const connectable = new ConnectableObservable(source, subjectFactory);\n if (hasLift(source)) {\n connectable.lift = source.lift;\n }\n connectable.source = source;\n connectable.subjectFactory = subjectFactory;\n return connectable;\n };\n}\n//# sourceMappingURL=multicast.js.map","import { zip as zipStatic } from '../observable/zip';\nimport { operate } from '../util/lift';\nexport function zip(...sources) {\n return operate((source, subscriber) => {\n zipStatic(source, ...sources).subscribe(subscriber);\n });\n}\nexport function zipWith(...otherInputs) {\n return zip(...otherInputs);\n}\n//# sourceMappingURL=zipWith.js.map","import { Observable } from '../Observable';\nimport { Subscription } from '../Subscription';\nimport { from } from './from';\nexport function zip(...sources) {\n let resultSelector = undefined;\n if (typeof sources[sources.length - 1] === 'function') {\n resultSelector = sources.pop();\n }\n return new Observable((subscriber) => {\n const buffers = sources.map(() => []);\n const completed = sources.map(() => false);\n const subscription = new Subscription();\n const tryEmit = () => {\n if (buffers.every((buffer) => buffer.length > 0)) {\n let result = buffers.map((buffer) => buffer.shift());\n if (resultSelector) {\n try {\n result = resultSelector(...result);\n }\n catch (err) {\n subscriber.error(err);\n return;\n }\n }\n subscriber.next(result);\n if (buffers.some((buffer, i) => buffer.length === 0 && completed[i])) {\n subscriber.complete();\n }\n }\n };\n for (let i = 0; !subscriber.closed && i < sources.length; i++) {\n const source = from(sources[i]);\n subscription.add(source.subscribe({\n next: (value) => {\n buffers[i].push(value);\n tryEmit();\n },\n error: (err) => subscriber.error(err),\n complete: () => {\n completed[i] = true;\n if (buffers[i].length === 0) {\n subscriber.complete();\n }\n },\n }));\n }\n return subscription;\n });\n}\n//# sourceMappingURL=zip.js.map","const { isArray } = Array;\nexport function argsOrArgArray(args) {\n return args.length === 1 && isArray(args[0]) ? args[0] : args;\n}\n//# sourceMappingURL=argsOrArgArray.js.map","import { isScheduler } from '../util/isScheduler';\nimport { mergeAll } from '../operators/mergeAll';\nimport { fromArray } from './fromArray';\nimport { argsOrArgArray } from '../util/argsOrArgArray';\nimport { from } from './from';\nimport { EMPTY } from './empty';\nexport function merge(...args) {\n let concurrent = Infinity;\n let scheduler = undefined;\n if (isScheduler(args[args.length - 1])) {\n scheduler = args.pop();\n }\n if (typeof args[args.length - 1] === 'number') {\n concurrent = args.pop();\n }\n args = argsOrArgArray(args);\n return !args.length\n ?\n EMPTY\n : args.length === 1\n ?\n from(args[0])\n :\n mergeAll(concurrent)(fromArray(args, scheduler));\n}\n//# sourceMappingURL=merge.js.map","import { asyncScheduler } from '../scheduler/async';\nimport { isValidDate } from '../util/isDate';\nimport { operate } from '../util/lift';\nimport { OperatorSubscriber } from './OperatorSubscriber';\nexport function delay(delay, scheduler = asyncScheduler) {\n return operate((source, subscriber) => {\n const isAbsoluteDelay = isValidDate(delay);\n let isComplete = false;\n let active = 0;\n let absoluteTimeValues = isAbsoluteDelay ? [] : null;\n const checkComplete = () => isComplete && !active && !(absoluteTimeValues === null || absoluteTimeValues === void 0 ? void 0 : absoluteTimeValues.length) && subscriber.complete();\n if (isAbsoluteDelay) {\n active++;\n subscriber.add(scheduler.schedule(() => {\n active--;\n if (absoluteTimeValues) {\n const values = absoluteTimeValues;\n absoluteTimeValues = null;\n for (const value of values) {\n subscriber.next(value);\n }\n }\n checkComplete();\n }, +delay - scheduler.now()));\n }\n source.subscribe(new OperatorSubscriber(subscriber, (value) => {\n if (isAbsoluteDelay) {\n absoluteTimeValues ? absoluteTimeValues.push(value) : subscriber.next(value);\n }\n else {\n active++;\n subscriber.add(scheduler.schedule(() => {\n active--;\n subscriber.next(value);\n checkComplete();\n }, delay));\n }\n }, undefined, () => {\n isComplete = true;\n checkComplete();\n }));\n return () => {\n absoluteTimeValues = null;\n };\n });\n}\n//# sourceMappingURL=delay.js.map","export function isValidDate(value) {\n return value instanceof Date && !isNaN(value);\n}\n//# sourceMappingURL=isDate.js.map"],"sourceRoot":""} \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/worker/search.4ac00218.min.js b/docs/v3/v2/assets/javascripts/worker/search.4ac00218.min.js deleted file mode 100644 index 0db84684e..000000000 --- a/docs/v3/v2/assets/javascripts/worker/search.4ac00218.min.js +++ /dev/null @@ -1,59 +0,0 @@ -!function(e){var t={};function r(n){if(t[n])return t[n].exports;var i=t[n]={i:n,l:!1,exports:{}};return e[n].call(i.exports,i,i.exports,r),i.l=!0,i.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)r.d(n,i,function(t){return e[t]}.bind(null,i));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=5)}([function(e,t,r){"use strict"; -/*! - * escape-html - * Copyright(c) 2012-2013 TJ Holowaychuk - * Copyright(c) 2015 Andreas Lubbe - * Copyright(c) 2015 Tiancheng "Timothy" Gu - * MIT Licensed - */var n=/["'&<>]/;e.exports=function(e){var t,r=""+e,i=n.exec(r);if(!i)return r;var s="",o=0,a=0;for(o=i.index;o0){var u=I.utils.clone(t)||{};u.position=[o,a],u.index=i.length,i.push(new I.Token(r.slice(o,s),u))}o=s+1}}return i},I.tokenizer.separator=/[\s\-]+/ -/*! - * lunr.Pipeline - * Copyright (C) 2020 Oliver Nightingale - */,I.Pipeline=function(){this._stack=[]},I.Pipeline.registeredFunctions=Object.create(null),I.Pipeline.registerFunction=function(e,t){t in this.registeredFunctions&&I.utils.warn("Overwriting existing registered function: "+t),e.label=t,I.Pipeline.registeredFunctions[e.label]=e},I.Pipeline.warnIfFunctionNotRegistered=function(e){e.label&&e.label in this.registeredFunctions||I.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},I.Pipeline.load=function(e){var t=new I.Pipeline;return e.forEach((function(e){var r=I.Pipeline.registeredFunctions[e];if(!r)throw new Error("Cannot load unregistered function: "+e);t.add(r)})),t},I.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach((function(e){I.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)}),this)},I.Pipeline.prototype.after=function(e,t){I.Pipeline.warnIfFunctionNotRegistered(t);var r=this._stack.indexOf(e);if(-1==r)throw new Error("Cannot find existingFn");r+=1,this._stack.splice(r,0,t)},I.Pipeline.prototype.before=function(e,t){I.Pipeline.warnIfFunctionNotRegistered(t);var r=this._stack.indexOf(e);if(-1==r)throw new Error("Cannot find existingFn");this._stack.splice(r,0,t)},I.Pipeline.prototype.remove=function(e){var t=this._stack.indexOf(e);-1!=t&&this._stack.splice(t,1)},I.Pipeline.prototype.run=function(e){for(var t=this._stack.length,r=0;r1&&(se&&(r=i),s!=e);)n=r-t,i=t+Math.floor(n/2),s=this.elements[2*i];return s==e||s>e?2*i:sa?l+=2:o==a&&(t+=r[u+1]*n[l+1],u+=2,l+=2);return t},I.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},I.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),t=1,r=0;t0){var s,o=i.str.charAt(0);o in i.node.edges?s=i.node.edges[o]:(s=new I.TokenSet,i.node.edges[o]=s),1==i.str.length&&(s.final=!0),n.push({node:s,editsRemaining:i.editsRemaining,str:i.str.slice(1)})}if(0!=i.editsRemaining){if("*"in i.node.edges)var a=i.node.edges["*"];else{a=new I.TokenSet;i.node.edges["*"]=a}if(0==i.str.length&&(a.final=!0),n.push({node:a,editsRemaining:i.editsRemaining-1,str:i.str}),i.str.length>1&&n.push({node:i.node,editsRemaining:i.editsRemaining-1,str:i.str.slice(1)}),1==i.str.length&&(i.node.final=!0),i.str.length>=1){if("*"in i.node.edges)var u=i.node.edges["*"];else{u=new I.TokenSet;i.node.edges["*"]=u}1==i.str.length&&(u.final=!0),n.push({node:u,editsRemaining:i.editsRemaining-1,str:i.str.slice(1)})}if(i.str.length>1){var l,c=i.str.charAt(0),h=i.str.charAt(1);h in i.node.edges?l=i.node.edges[h]:(l=new I.TokenSet,i.node.edges[h]=l),1==i.str.length&&(l.final=!0),n.push({node:l,editsRemaining:i.editsRemaining-1,str:c+i.str.slice(2)})}}}return r},I.TokenSet.fromString=function(e){for(var t=new I.TokenSet,r=t,n=0,i=e.length;n=e;t--){var r=this.uncheckedNodes[t],n=r.child.toString();n in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[n]:(r.child._str=n,this.minimizedNodes[n]=r.child),this.uncheckedNodes.pop()}} -/*! - * lunr.Index - * Copyright (C) 2020 Oliver Nightingale - */,I.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},I.Index.prototype.search=function(e){return this.query((function(t){new I.QueryParser(e,t).parse()}))},I.Index.prototype.query=function(e){for(var t=new I.Query(this.fields),r=Object.create(null),n=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=0;a1?1:e},I.Builder.prototype.k1=function(e){this._k1=e},I.Builder.prototype.add=function(e,t){var r=e[this._ref],n=Object.keys(this._fields);this._documents[r]=t||{},this.documentCount+=1;for(var i=0;i=this.length)return I.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},I.QueryLexer.prototype.width=function(){return this.pos-this.start},I.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},I.QueryLexer.prototype.backup=function(){this.pos-=1},I.QueryLexer.prototype.acceptDigitRun=function(){var e,t;do{t=(e=this.next()).charCodeAt(0)}while(t>47&&t<58);e!=I.QueryLexer.EOS&&this.backup()},I.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(I.QueryLexer.TERM)),e.ignore(),e.more())return I.QueryLexer.lexText},I.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(I.QueryLexer.EDIT_DISTANCE),I.QueryLexer.lexText},I.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(I.QueryLexer.BOOST),I.QueryLexer.lexText},I.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(I.QueryLexer.TERM)},I.QueryLexer.termSeparator=I.tokenizer.separator,I.QueryLexer.lexText=function(e){for(;;){var t=e.next();if(t==I.QueryLexer.EOS)return I.QueryLexer.lexEOS;if(92!=t.charCodeAt(0)){if(":"==t)return I.QueryLexer.lexField;if("~"==t)return e.backup(),e.width()>0&&e.emit(I.QueryLexer.TERM),I.QueryLexer.lexEditDistance;if("^"==t)return e.backup(),e.width()>0&&e.emit(I.QueryLexer.TERM),I.QueryLexer.lexBoost;if("+"==t&&1===e.width())return e.emit(I.QueryLexer.PRESENCE),I.QueryLexer.lexText;if("-"==t&&1===e.width())return e.emit(I.QueryLexer.PRESENCE),I.QueryLexer.lexText;if(t.match(I.QueryLexer.termSeparator))return I.QueryLexer.lexTerm}else e.escapeCharacter()}},I.QueryParser=function(e,t){this.lexer=new I.QueryLexer(e),this.query=t,this.currentClause={},this.lexemeIdx=0},I.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=I.QueryParser.parseClause;e;)e=e(this);return this.query},I.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},I.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},I.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},I.QueryParser.parseClause=function(e){var t=e.peekLexeme();if(null!=t)switch(t.type){case I.QueryLexer.PRESENCE:return I.QueryParser.parsePresence;case I.QueryLexer.FIELD:return I.QueryParser.parseField;case I.QueryLexer.TERM:return I.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+t.type;throw t.str.length>=1&&(r+=" with value '"+t.str+"'"),new I.QueryParseError(r,t.start,t.end)}},I.QueryParser.parsePresence=function(e){var t=e.consumeLexeme();if(null!=t){switch(t.str){case"-":e.currentClause.presence=I.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=I.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+t.str+"'";throw new I.QueryParseError(r,t.start,t.end)}var n=e.peekLexeme();if(null==n){r="expecting term or field, found nothing";throw new I.QueryParseError(r,t.start,t.end)}switch(n.type){case I.QueryLexer.FIELD:return I.QueryParser.parseField;case I.QueryLexer.TERM:return I.QueryParser.parseTerm;default:r="expecting term or field, found '"+n.type+"'";throw new I.QueryParseError(r,n.start,n.end)}}},I.QueryParser.parseField=function(e){var t=e.consumeLexeme();if(null!=t){if(-1==e.query.allFields.indexOf(t.str)){var r=e.query.allFields.map((function(e){return"'"+e+"'"})).join(", "),n="unrecognised field '"+t.str+"', possible fields: "+r;throw new I.QueryParseError(n,t.start,t.end)}e.currentClause.fields=[t.str];var i=e.peekLexeme();if(null==i){n="expecting term, found nothing";throw new I.QueryParseError(n,t.start,t.end)}switch(i.type){case I.QueryLexer.TERM:return I.QueryParser.parseTerm;default:n="expecting term, found '"+i.type+"'";throw new I.QueryParseError(n,i.start,i.end)}}},I.QueryParser.parseTerm=function(e){var t=e.consumeLexeme();if(null!=t){e.currentClause.term=t.str.toLowerCase(),-1!=t.str.indexOf("*")&&(e.currentClause.usePipeline=!1);var r=e.peekLexeme();if(null!=r)switch(r.type){case I.QueryLexer.TERM:return e.nextClause(),I.QueryParser.parseTerm;case I.QueryLexer.FIELD:return e.nextClause(),I.QueryParser.parseField;case I.QueryLexer.EDIT_DISTANCE:return I.QueryParser.parseEditDistance;case I.QueryLexer.BOOST:return I.QueryParser.parseBoost;case I.QueryLexer.PRESENCE:return e.nextClause(),I.QueryParser.parsePresence;default:var n="Unexpected lexeme type '"+r.type+"'";throw new I.QueryParseError(n,r.start,r.end)}else e.nextClause()}},I.QueryParser.parseEditDistance=function(e){var t=e.consumeLexeme();if(null!=t){var r=parseInt(t.str,10);if(isNaN(r)){var n="edit distance must be numeric";throw new I.QueryParseError(n,t.start,t.end)}e.currentClause.editDistance=r;var i=e.peekLexeme();if(null!=i)switch(i.type){case I.QueryLexer.TERM:return e.nextClause(),I.QueryParser.parseTerm;case I.QueryLexer.FIELD:return e.nextClause(),I.QueryParser.parseField;case I.QueryLexer.EDIT_DISTANCE:return I.QueryParser.parseEditDistance;case I.QueryLexer.BOOST:return I.QueryParser.parseBoost;case I.QueryLexer.PRESENCE:return e.nextClause(),I.QueryParser.parsePresence;default:n="Unexpected lexeme type '"+i.type+"'";throw new I.QueryParseError(n,i.start,i.end)}else e.nextClause()}},I.QueryParser.parseBoost=function(e){var t=e.consumeLexeme();if(null!=t){var r=parseInt(t.str,10);if(isNaN(r)){var n="boost must be numeric";throw new I.QueryParseError(n,t.start,t.end)}e.currentClause.boost=r;var i=e.peekLexeme();if(null!=i)switch(i.type){case I.QueryLexer.TERM:return e.nextClause(),I.QueryParser.parseTerm;case I.QueryLexer.FIELD:return e.nextClause(),I.QueryParser.parseField;case I.QueryLexer.EDIT_DISTANCE:return I.QueryParser.parseEditDistance;case I.QueryLexer.BOOST:return I.QueryParser.parseBoost;case I.QueryLexer.PRESENCE:return e.nextClause(),I.QueryParser.parsePresence;default:n="Unexpected lexeme type '"+i.type+"'";throw new I.QueryParseError(n,i.start,i.end)}else e.nextClause()}},void 0===(i="function"==typeof(n=function(){return I})?n.call(t,r,t,e):n)||(e.exports=i)}()},function(e,t,r){"use strict";(function(t){e.exports=function(){if("object"==typeof globalThis)return globalThis;var e;try{e=this||new Function("return this")()}catch(e){if("object"==typeof window)return window;if("object"==typeof self)return self;if(void 0!==t)return t}return e}()}).call(this,r(4))},function(e,t){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"==typeof window&&(r=window)}e.exports=r},function(e,t,r){"use strict";r.r(t),r.d(t,"handler",(function(){return u}));function n(e,t,r,n){return new(r||(r=Promise))((function(i,s){function o(e){try{u(n.next(e))}catch(e){s(e)}}function a(e){try{u(n.throw(e))}catch(e){s(e)}}function u(e){var t;e.done?i(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(o,a)}u((n=n.apply(e,t||[])).next())}))}Object.create;Object.create;r(1);var i,s=r(0);class o{constructor({config:e,docs:t,pipeline:r,index:n}){this.documents=function(e){const t=new Map,r=new Set;for(const n of e){const[e,i]=n.location.split("#"),o=n.location,a=n.title,u=s(n.text).replace(/\s+(?=[,.:;!?])/g,"").replace(/\s+/g," ");if(i){const i=t.get(e);r.has(i)?t.set(o,{location:o,title:a,text:u,parent:i}):(i.title=n.title,i.text=u,r.add(i))}else t.set(o,{location:o,title:a,text:u})}return t}(t),this.highlight=function(e){const t=new RegExp(e.separator,"img"),r=(e,t,r)=>`${t}${r}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").trim();const i=new RegExp(`(^|${e.separator})(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(t,"|")})`,"img");return e=>e.replace(i,r).replace(/<\/mark>(\s+)]*>/gim,"$1")}}(e),lunr.tokenizer.separator=new RegExp(e.separator),this.index=void 0===n?lunr((function(){1===e.lang.length&&"en"!==e.lang[0]?this.use(lunr[e.lang[0]]):e.lang.length>1&&this.use(lunr.multiLanguage(...e.lang));const n=function(e,t){const[r,n]=[new Set(e),new Set(t)];return[...new Set([...r].filter(e=>!n.has(e)))]}(["trimmer","stopWordFilter","stemmer"],r);for(const t of e.lang.map(e=>"en"===e?lunr:lunr[e]))for(const e of n)this.pipeline.remove(t[e]),this.searchPipeline.remove(t[e]);this.field("title",{boost:1e3}),this.field("text"),this.ref("location");for(const e of t)this.add(e)})):lunr.Index.load(n)}search(e){if(e)try{const t=this.highlight(e),r=function(e){const t=new lunr.Query(["title","text"]);return new lunr.QueryParser(e,t).parse(),t.clauses}(e).filter(e=>e.presence!==lunr.Query.presence.PROHIBITED);return[...this.index.search(e+"*").reduce((e,{ref:n,score:i,matchData:s})=>{const o=this.documents.get(n);if(void 0!==o){const{location:n,title:a,text:u,parent:l}=o,c=function(e,t){const r=new Set(e),n={};for(let e=0;ee);e.push({location:n,title:t(a),text:t(u),score:i*(1+h),terms:c})}return e},[]).sort((e,t)=>t.score-e.score).reduce((e,t)=>{const r=this.documents.get(t.location);if(void 0!==r){const n="parent"in r?r.parent.location:r.location;e.set(n,[...e.get(n)||[],t])}return e},new Map).values()]}catch(t){console.warn(`Invalid query: ${e} – see https://bit.ly/2s3ChXG`)}return[]}}let a;function u(e){return n(this,void 0,void 0,(function*(){switch(e.type){case i.SETUP:return yield function(e){return n(this,void 0,void 0,(function*(){let t="../lunr";if("undefined"!=typeof parent&&"IFrameWorker"in parent){const e=document.querySelector("script[src]"),[r]=e.src.split("/worker");t=t.replace("..",r)}const r=[];for(const n of e.lang)"ja"===n&&r.push(t+"/tinyseg.min.js"),"en"!==n&&r.push(`${t}/min/lunr.${n}.min.js`);e.lang.length>1&&r.push(t+"/min/lunr.multi.min.js"),r.length&&(yield importScripts(t+"/min/lunr.stemmer.support.min.js",...r))}))}(e.data.config),a=new o(e.data),{type:i.READY};case i.QUERY:return{type:i.RESULT,data:a?a.search(e.data):[]};default:throw new TypeError("Invalid message type")}}))}!function(e){e[e.SETUP=0]="SETUP",e[e.READY=1]="READY",e[e.QUERY=2]="QUERY",e[e.RESULT=3]="RESULT"}(i||(i={})),addEventListener("message",e=>n(void 0,void 0,void 0,(function*(){postMessage(yield u(e.data))})))}]); -//# sourceMappingURL=search.4ac00218.min.js.map \ No newline at end of file diff --git a/docs/v3/v2/assets/javascripts/worker/search.4ac00218.min.js.map b/docs/v3/v2/assets/javascripts/worker/search.4ac00218.min.js.map deleted file mode 100644 index f2a013179..000000000 --- a/docs/v3/v2/assets/javascripts/worker/search.4ac00218.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./node_modules/escape-html/index.js","webpack:///./node_modules/lunr/lunr-exposed.js","webpack:///./node_modules/lunr/lunr.js","webpack:///./node_modules/expose-loader/dist/runtime/getGlobalThis.js","webpack:///(webpack)/buildin/global.js","webpack:///./node_modules/tslib/tslib.es6.js","webpack:///./src/assets/javascripts/integrations/search/worker/message/index.ts","webpack:///./src/assets/javascripts/integrations/search/_/index.ts","webpack:///./src/assets/javascripts/integrations/search/document/index.ts","webpack:///./src/assets/javascripts/integrations/search/highlighter/index.ts","webpack:///./src/assets/javascripts/integrations/search/query/_/index.ts","webpack:///./src/assets/javascripts/integrations/search/worker/main/index.ts"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","matchHtmlRegExp","string","escape","str","match","exec","html","index","lastIndex","length","charCodeAt","substring","___EXPOSE_LOADER_IMPORT___","___EXPOSE_LOADER_GLOBAL_THIS___","global","step2list","step3list","v","C","re_mgr0","re_mgr1","re_meq1","re_s_v","re_1a","re2_1a","re_1b","re2_1b","re_1b_2","re2_1b_2","re3_1b_2","re4_1b_2","re_1c","re_2","re_3","re_4","re2_4","re_5","re_5_1","re3_5","porterStemmer","lunr","config","builder","Builder","pipeline","add","trimmer","stopWordFilter","stemmer","searchPipeline","build","version","utils","warn","this","message","console","asString","obj","toString","clone","keys","val","Array","isArray","slice","TypeError","FieldRef","docRef","fieldName","stringValue","_stringValue","joiner","fromString","indexOf","fieldRef","undefined","Set","elements","complete","intersect","other","union","contains","empty","a","b","intersection","element","push","concat","idf","posting","documentCount","documentsWithTerm","x","Math","log","abs","Token","metadata","update","fn","tokenizer","map","toLowerCase","len","tokens","sliceEnd","sliceStart","sliceLength","charAt","separator","tokenMetadata","Pipeline","_stack","registeredFunctions","registerFunction","label","warnIfFunctionNotRegistered","load","serialised","forEach","fnName","Error","fns","arguments","after","existingFn","newFn","pos","splice","before","remove","run","stackLength","memo","j","result","k","runString","token","reset","toJSON","Vector","_magnitude","positionForIndex","start","end","pivotPoint","floor","pivotIndex","insert","insertIdx","upsert","position","magnitude","sumOfSquares","elementsLength","sqrt","dot","otherVector","dotProduct","aLen","bLen","aVal","bVal","similarity","toArray","output","RegExp","w","stem","suffix","firstch","re","re2","re3","re4","substr","toUpperCase","test","replace","fp","generateStopWordFilter","stopWords","words","reduce","stopWord","TokenSet","final","edges","id","_nextId","fromArray","arr","finish","root","fromClause","clause","fromFuzzyString","term","editDistance","stack","node","editsRemaining","frame","pop","noEditNode","char","insertionNode","substitutionNode","transposeNode","charA","charB","next","prefix","edge","_str","labels","sort","qNode","qEdges","qLen","nEdges","nLen","q","qEdge","nEdge","previousWord","uncheckedNodes","minimizedNodes","word","commonPrefix","minimize","child","nextNode","parent","downTo","childKey","Index","attrs","invertedIndex","fieldVectors","tokenSet","fields","search","queryString","query","QueryParser","parse","Query","matchingFields","queryVectors","termFieldCache","requiredMatches","prohibitedMatches","clauses","terms","clauseMatches","usePipeline","termTokenSet","expandedTerms","presence","REQUIRED","field","expandedTerm","termIndex","_index","fieldPosting","matchingDocumentRefs","termField","matchingDocumentsSet","PROHIBITED","boost","fieldMatch","matchingDocumentRef","matchingFieldRef","MatchData","allRequiredMatches","allProhibitedMatches","matchingFieldRefs","results","matches","isNegated","docMatch","fieldVector","score","matchData","combine","ref","serializedIndex","serializedVectors","serializedInvertedIndex","tokenSetBuilder","tuple","_ref","_fields","_documents","fieldTermFrequencies","fieldLengths","_b","_k1","metadataWhitelist","attributes","RangeError","number","k1","doc","extractor","fieldTerms","metadataKey","calculateAverageFieldLengths","fieldRefs","numberOfFields","accumulator","documentsWithField","averageFieldLength","createFieldVectors","fieldRefsLength","termIdfCache","fieldLength","termFrequencies","termsLength","fieldBoost","docBoost","scoreWithPrecision","tf","round","createTokenSet","use","args","unshift","apply","clonedMetadata","metadataKeys","otherMatchData","allFields","wildcard","String","NONE","LEADING","TRAILING","OPTIONAL","options","QueryParseError","QueryLexer","lexemes","escapeCharPositions","state","lexText","sliceString","subSlices","join","emit","type","escapeCharacter","EOS","width","ignore","backup","acceptDigitRun","charCode","more","FIELD","TERM","EDIT_DISTANCE","BOOST","PRESENCE","lexField","lexer","lexTerm","lexEditDistance","lexBoost","lexEOS","termSeparator","currentClause","lexemeIdx","parseClause","peekLexeme","consumeLexeme","lexeme","nextClause","completedClause","parser","parsePresence","parseField","parseTerm","errorMessage","nextLexeme","possibleFields","f","parseEditDistance","parseBoost","parseInt","isNaN","globalThis","g","Function","e","window","self","__awaiter","thisArg","_arguments","P","generator","Promise","resolve","reject","fulfilled","step","rejected","done","then","SearchMessageType","docs","documents","Map","parents","path","hash","location","split","title","text","has","set","setupSearchDocumentMap","highlight","_","data","trim","setupSearchHighlighter","lang","multiLanguage","y","filter","difference","language","parseSearchQuery","document","startsWith","delete","getSearchQueryTerms","values","every","handler","SETUP","base","worker","querySelector","src","scripts","importScripts","setupSearchLanguages","READY","QUERY","RESULT","addEventListener","ev","postMessage"],"mappings":"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,IACN,IAEH,OAAO,SAAUK,GACf,GAAIA,GAAS6C,EAAM7C,EAAMlF,cAAgBkF,EAAMlF,WAAY,OAAOkF,IAiBtErG,EAAKO,eAAiBP,EAAKgJ,uBAAuB,CAChD,IACA,OACA,QACA,SACA,QACA,MACA,SACA,OACA,KACA,QACA,KACA,MACA,MACA,MACA,KACA,KACA,KACA,UACA,OACA,MACA,KACA,MACA,SACA,QACA,OACA,MACA,KACA,OACA,SACA,OACA,OACA,QACA,MACA,OACA,MACA,MACA,MACA,MACA,OACA,KACA,MACA,OACA,MACA,MACA,MACA,UACA,IACA,KACA,KACA,OACA,KACA,KACA,MACA,OACA,QACA,MACA,OACA,SACA,MACA,KACA,QACA,OACA,OACA,KACA,UACA,KACA,MACA,MACA,KACA,MACA,QACA,KACA,OACA,KACA,QACA,MACA,MACA,SACA,OACA,MACA,OACA,MACA,SACA,QACA,KACA,OACA,OACA,OACA,MACA,QACA,OACA,OACA,QACA,QACA,OACA,OACA,MACA,KACA,MACA,OACA,KACA,QACA,MACA,KACA,OACA,OACA,OACA,QACA,QACA,QACA,MACA,OACA,MACA,OACA,OACA,QACA,MACA,MACA,SAGFhJ,EAAK0E,SAASG,iBAAiB7E,EAAKO,eAAgB;;;;IAqBpDP,EAAKM,QAAU,SAAU+F,GACvB,OAAOA,EAAMxC,QAAO,SAAUtG,GAC5B,OAAOA,EAAEuL,QAAQ,OAAQ,IAAIA,QAAQ,OAAQ,QAIjD9I,EAAK0E,SAASG,iBAAiB7E,EAAKM,QAAS;;;;IA2B7CN,EAAKqJ,SAAW,WACdvI,KAAKwI,OAAQ,EACbxI,KAAKyI,MAAQ,GACbzI,KAAK0I,GAAKxJ,EAAKqJ,SAASI,QACxBzJ,EAAKqJ,SAASI,SAAW,GAW3BzJ,EAAKqJ,SAASI,QAAU,EASxBzJ,EAAKqJ,SAASK,UAAY,SAAUC,GAGlC,IAFA,IAAIzJ,EAAU,IAAIF,EAAKqJ,SAASlJ,QAEvB3E,EAAI,EAAG0I,EAAMyF,EAAI1L,OAAQzC,EAAI0I,EAAK1I,IACzC0E,EAAQ8G,OAAO2C,E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","file":"assets/javascripts/worker/search.4ac00218.min.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 5);\n","/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n","var ___EXPOSE_LOADER_IMPORT___ = require(\"-!./lunr.js\");\nvar ___EXPOSE_LOADER_GET_GLOBAL_THIS___ = require(\"../expose-loader/dist/runtime/getGlobalThis.js\");\nvar ___EXPOSE_LOADER_GLOBAL_THIS___ = ___EXPOSE_LOADER_GET_GLOBAL_THIS___;\nif (typeof ___EXPOSE_LOADER_GLOBAL_THIS___[\"lunr\"] === 'undefined') ___EXPOSE_LOADER_GLOBAL_THIS___[\"lunr\"] = ___EXPOSE_LOADER_IMPORT___;\nmodule.exports = ___EXPOSE_LOADER_IMPORT___;\n","/**\n * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9\n * Copyright (C) 2020 Oliver Nightingale\n * @license MIT\n */\n\n;(function(){\n\n/**\n * A convenience function for configuring and constructing\n * a new lunr Index.\n *\n * A lunr.Builder instance is created and the pipeline setup\n * with a trimmer, stop word filter and stemmer.\n *\n * This builder object is yielded to the configuration function\n * that is passed as a parameter, allowing the list of fields\n * and other builder parameters to be customised.\n *\n * All documents _must_ be added within the passed config function.\n *\n * @example\n * var idx = lunr(function () {\n * this.field('title')\n * this.field('body')\n * this.ref('id')\n *\n * documents.forEach(function (doc) {\n * this.add(doc)\n * }, this)\n * })\n *\n * @see {@link lunr.Builder}\n * @see {@link lunr.Pipeline}\n * @see {@link lunr.trimmer}\n * @see {@link lunr.stopWordFilter}\n * @see {@link lunr.stemmer}\n * @namespace {function} lunr\n */\nvar lunr = function (config) {\n var builder = new lunr.Builder\n\n builder.pipeline.add(\n lunr.trimmer,\n lunr.stopWordFilter,\n lunr.stemmer\n )\n\n builder.searchPipeline.add(\n lunr.stemmer\n )\n\n config.call(builder, builder)\n return builder.build()\n}\n\nlunr.version = \"2.3.9\"\n/*!\n * lunr.utils\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A namespace containing utils for the rest of the lunr library\n * @namespace lunr.utils\n */\nlunr.utils = {}\n\n/**\n * Print a warning message to the console.\n *\n * @param {String} message The message to be printed.\n * @memberOf lunr.utils\n * @function\n */\nlunr.utils.warn = (function (global) {\n /* eslint-disable no-console */\n return function (message) {\n if (global.console && console.warn) {\n console.warn(message)\n }\n }\n /* eslint-enable no-console */\n})(this)\n\n/**\n * Convert an object to a string.\n *\n * In the case of `null` and `undefined` the function returns\n * the empty string, in all other cases the result of calling\n * `toString` on the passed object is returned.\n *\n * @param {Any} obj The object to convert to a string.\n * @return {String} string representation of the passed object.\n * @memberOf lunr.utils\n */\nlunr.utils.asString = function (obj) {\n if (obj === void 0 || obj === null) {\n return \"\"\n } else {\n return obj.toString()\n }\n}\n\n/**\n * Clones an object.\n *\n * Will create a copy of an existing object such that any mutations\n * on the copy cannot affect the original.\n *\n * Only shallow objects are supported, passing a nested object to this\n * function will cause a TypeError.\n *\n * Objects with primitives, and arrays of primitives are supported.\n *\n * @param {Object} obj The object to clone.\n * @return {Object} a clone of the passed object.\n * @throws {TypeError} when a nested object is passed.\n * @memberOf Utils\n */\nlunr.utils.clone = function (obj) {\n if (obj === null || obj === undefined) {\n return obj\n }\n\n var clone = Object.create(null),\n keys = Object.keys(obj)\n\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i],\n val = obj[key]\n\n if (Array.isArray(val)) {\n clone[key] = val.slice()\n continue\n }\n\n if (typeof val === 'string' ||\n typeof val === 'number' ||\n typeof val === 'boolean') {\n clone[key] = val\n continue\n }\n\n throw new TypeError(\"clone is not deep and does not support nested objects\")\n }\n\n return clone\n}\nlunr.FieldRef = function (docRef, fieldName, stringValue) {\n this.docRef = docRef\n this.fieldName = fieldName\n this._stringValue = stringValue\n}\n\nlunr.FieldRef.joiner = \"/\"\n\nlunr.FieldRef.fromString = function (s) {\n var n = s.indexOf(lunr.FieldRef.joiner)\n\n if (n === -1) {\n throw \"malformed field ref string\"\n }\n\n var fieldRef = s.slice(0, n),\n docRef = s.slice(n + 1)\n\n return new lunr.FieldRef (docRef, fieldRef, s)\n}\n\nlunr.FieldRef.prototype.toString = function () {\n if (this._stringValue == undefined) {\n this._stringValue = this.fieldName + lunr.FieldRef.joiner + this.docRef\n }\n\n return this._stringValue\n}\n/*!\n * lunr.Set\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A lunr set.\n *\n * @constructor\n */\nlunr.Set = function (elements) {\n this.elements = Object.create(null)\n\n if (elements) {\n this.length = elements.length\n\n for (var i = 0; i < this.length; i++) {\n this.elements[elements[i]] = true\n }\n } else {\n this.length = 0\n }\n}\n\n/**\n * A complete set that contains all elements.\n *\n * @static\n * @readonly\n * @type {lunr.Set}\n */\nlunr.Set.complete = {\n intersect: function (other) {\n return other\n },\n\n union: function () {\n return this\n },\n\n contains: function () {\n return true\n }\n}\n\n/**\n * An empty set that contains no elements.\n *\n * @static\n * @readonly\n * @type {lunr.Set}\n */\nlunr.Set.empty = {\n intersect: function () {\n return this\n },\n\n union: function (other) {\n return other\n },\n\n contains: function () {\n return false\n }\n}\n\n/**\n * Returns true if this set contains the specified object.\n *\n * @param {object} object - Object whose presence in this set is to be tested.\n * @returns {boolean} - True if this set contains the specified object.\n */\nlunr.Set.prototype.contains = function (object) {\n return !!this.elements[object]\n}\n\n/**\n * Returns a new set containing only the elements that are present in both\n * this set and the specified set.\n *\n * @param {lunr.Set} other - set to intersect with this set.\n * @returns {lunr.Set} a new set that is the intersection of this and the specified set.\n */\n\nlunr.Set.prototype.intersect = function (other) {\n var a, b, elements, intersection = []\n\n if (other === lunr.Set.complete) {\n return this\n }\n\n if (other === lunr.Set.empty) {\n return other\n }\n\n if (this.length < other.length) {\n a = this\n b = other\n } else {\n a = other\n b = this\n }\n\n elements = Object.keys(a.elements)\n\n for (var i = 0; i < elements.length; i++) {\n var element = elements[i]\n if (element in b.elements) {\n intersection.push(element)\n }\n }\n\n return new lunr.Set (intersection)\n}\n\n/**\n * Returns a new set combining the elements of this and the specified set.\n *\n * @param {lunr.Set} other - set to union with this set.\n * @return {lunr.Set} a new set that is the union of this and the specified set.\n */\n\nlunr.Set.prototype.union = function (other) {\n if (other === lunr.Set.complete) {\n return lunr.Set.complete\n }\n\n if (other === lunr.Set.empty) {\n return this\n }\n\n return new lunr.Set(Object.keys(this.elements).concat(Object.keys(other.elements)))\n}\n/**\n * A function to calculate the inverse document frequency for\n * a posting. This is shared between the builder and the index\n *\n * @private\n * @param {object} posting - The posting for a given term\n * @param {number} documentCount - The total number of documents.\n */\nlunr.idf = function (posting, documentCount) {\n var documentsWithTerm = 0\n\n for (var fieldName in posting) {\n if (fieldName == '_index') continue // Ignore the term index, its not a field\n documentsWithTerm += Object.keys(posting[fieldName]).length\n }\n\n var x = (documentCount - documentsWithTerm + 0.5) / (documentsWithTerm + 0.5)\n\n return Math.log(1 + Math.abs(x))\n}\n\n/**\n * A token wraps a string representation of a token\n * as it is passed through the text processing pipeline.\n *\n * @constructor\n * @param {string} [str=''] - The string token being wrapped.\n * @param {object} [metadata={}] - Metadata associated with this token.\n */\nlunr.Token = function (str, metadata) {\n this.str = str || \"\"\n this.metadata = metadata || {}\n}\n\n/**\n * Returns the token string that is being wrapped by this object.\n *\n * @returns {string}\n */\nlunr.Token.prototype.toString = function () {\n return this.str\n}\n\n/**\n * A token update function is used when updating or optionally\n * when cloning a token.\n *\n * @callback lunr.Token~updateFunction\n * @param {string} str - The string representation of the token.\n * @param {Object} metadata - All metadata associated with this token.\n */\n\n/**\n * Applies the given function to the wrapped string token.\n *\n * @example\n * token.update(function (str, metadata) {\n * return str.toUpperCase()\n * })\n *\n * @param {lunr.Token~updateFunction} fn - A function to apply to the token string.\n * @returns {lunr.Token}\n */\nlunr.Token.prototype.update = function (fn) {\n this.str = fn(this.str, this.metadata)\n return this\n}\n\n/**\n * Creates a clone of this token. Optionally a function can be\n * applied to the cloned token.\n *\n * @param {lunr.Token~updateFunction} [fn] - An optional function to apply to the cloned token.\n * @returns {lunr.Token}\n */\nlunr.Token.prototype.clone = function (fn) {\n fn = fn || function (s) { return s }\n return new lunr.Token (fn(this.str, this.metadata), this.metadata)\n}\n/*!\n * lunr.tokenizer\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A function for splitting a string into tokens ready to be inserted into\n * the search index. Uses `lunr.tokenizer.separator` to split strings, change\n * the value of this property to change how strings are split into tokens.\n *\n * This tokenizer will convert its parameter to a string by calling `toString` and\n * then will split this string on the character in `lunr.tokenizer.separator`.\n * Arrays will have their elements converted to strings and wrapped in a lunr.Token.\n *\n * Optional metadata can be passed to the tokenizer, this metadata will be cloned and\n * added as metadata to every token that is created from the object to be tokenized.\n *\n * @static\n * @param {?(string|object|object[])} obj - The object to convert into tokens\n * @param {?object} metadata - Optional metadata to associate with every token\n * @returns {lunr.Token[]}\n * @see {@link lunr.Pipeline}\n */\nlunr.tokenizer = function (obj, metadata) {\n if (obj == null || obj == undefined) {\n return []\n }\n\n if (Array.isArray(obj)) {\n return obj.map(function (t) {\n return new lunr.Token(\n lunr.utils.asString(t).toLowerCase(),\n lunr.utils.clone(metadata)\n )\n })\n }\n\n var str = obj.toString().toLowerCase(),\n len = str.length,\n tokens = []\n\n for (var sliceEnd = 0, sliceStart = 0; sliceEnd <= len; sliceEnd++) {\n var char = str.charAt(sliceEnd),\n sliceLength = sliceEnd - sliceStart\n\n if ((char.match(lunr.tokenizer.separator) || sliceEnd == len)) {\n\n if (sliceLength > 0) {\n var tokenMetadata = lunr.utils.clone(metadata) || {}\n tokenMetadata[\"position\"] = [sliceStart, sliceLength]\n tokenMetadata[\"index\"] = tokens.length\n\n tokens.push(\n new lunr.Token (\n str.slice(sliceStart, sliceEnd),\n tokenMetadata\n )\n )\n }\n\n sliceStart = sliceEnd + 1\n }\n\n }\n\n return tokens\n}\n\n/**\n * The separator used to split a string into tokens. Override this property to change the behaviour of\n * `lunr.tokenizer` behaviour when tokenizing strings. By default this splits on whitespace and hyphens.\n *\n * @static\n * @see lunr.tokenizer\n */\nlunr.tokenizer.separator = /[\\s\\-]+/\n/*!\n * lunr.Pipeline\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.Pipelines maintain an ordered list of functions to be applied to all\n * tokens in documents entering the search index and queries being ran against\n * the index.\n *\n * An instance of lunr.Index created with the lunr shortcut will contain a\n * pipeline with a stop word filter and an English language stemmer. Extra\n * functions can be added before or after either of these functions or these\n * default functions can be removed.\n *\n * When run the pipeline will call each function in turn, passing a token, the\n * index of that token in the original list of all tokens and finally a list of\n * all the original tokens.\n *\n * The output of functions in the pipeline will be passed to the next function\n * in the pipeline. To exclude a token from entering the index the function\n * should return undefined, the rest of the pipeline will not be called with\n * this token.\n *\n * For serialisation of pipelines to work, all functions used in an instance of\n * a pipeline should be registered with lunr.Pipeline. Registered functions can\n * then be loaded. If trying to load a serialised pipeline that uses functions\n * that are not registered an error will be thrown.\n *\n * If not planning on serialising the pipeline then registering pipeline functions\n * is not necessary.\n *\n * @constructor\n */\nlunr.Pipeline = function () {\n this._stack = []\n}\n\nlunr.Pipeline.registeredFunctions = Object.create(null)\n\n/**\n * A pipeline function maps lunr.Token to lunr.Token. A lunr.Token contains the token\n * string as well as all known metadata. A pipeline function can mutate the token string\n * or mutate (or add) metadata for a given token.\n *\n * A pipeline function can indicate that the passed token should be discarded by returning\n * null, undefined or an empty string. This token will not be passed to any downstream pipeline\n * functions and will not be added to the index.\n *\n * Multiple tokens can be returned by returning an array of tokens. Each token will be passed\n * to any downstream pipeline functions and all will returned tokens will be added to the index.\n *\n * Any number of pipeline functions may be chained together using a lunr.Pipeline.\n *\n * @interface lunr.PipelineFunction\n * @param {lunr.Token} token - A token from the document being processed.\n * @param {number} i - The index of this token in the complete list of tokens for this document/field.\n * @param {lunr.Token[]} tokens - All tokens for this document/field.\n * @returns {(?lunr.Token|lunr.Token[])}\n */\n\n/**\n * Register a function with the pipeline.\n *\n * Functions that are used in the pipeline should be registered if the pipeline\n * needs to be serialised, or a serialised pipeline needs to be loaded.\n *\n * Registering a function does not add it to a pipeline, functions must still be\n * added to instances of the pipeline for them to be used when running a pipeline.\n *\n * @param {lunr.PipelineFunction} fn - The function to check for.\n * @param {String} label - The label to register this function with\n */\nlunr.Pipeline.registerFunction = function (fn, label) {\n if (label in this.registeredFunctions) {\n lunr.utils.warn('Overwriting existing registered function: ' + label)\n }\n\n fn.label = label\n lunr.Pipeline.registeredFunctions[fn.label] = fn\n}\n\n/**\n * Warns if the function is not registered as a Pipeline function.\n *\n * @param {lunr.PipelineFunction} fn - The function to check for.\n * @private\n */\nlunr.Pipeline.warnIfFunctionNotRegistered = function (fn) {\n var isRegistered = fn.label && (fn.label in this.registeredFunctions)\n\n if (!isRegistered) {\n lunr.utils.warn('Function is not registered with pipeline. This may cause problems when serialising the index.\\n', fn)\n }\n}\n\n/**\n * Loads a previously serialised pipeline.\n *\n * All functions to be loaded must already be registered with lunr.Pipeline.\n * If any function from the serialised data has not been registered then an\n * error will be thrown.\n *\n * @param {Object} serialised - The serialised pipeline to load.\n * @returns {lunr.Pipeline}\n */\nlunr.Pipeline.load = function (serialised) {\n var pipeline = new lunr.Pipeline\n\n serialised.forEach(function (fnName) {\n var fn = lunr.Pipeline.registeredFunctions[fnName]\n\n if (fn) {\n pipeline.add(fn)\n } else {\n throw new Error('Cannot load unregistered function: ' + fnName)\n }\n })\n\n return pipeline\n}\n\n/**\n * Adds new functions to the end of the pipeline.\n *\n * Logs a warning if the function has not been registered.\n *\n * @param {lunr.PipelineFunction[]} functions - Any number of functions to add to the pipeline.\n */\nlunr.Pipeline.prototype.add = function () {\n var fns = Array.prototype.slice.call(arguments)\n\n fns.forEach(function (fn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(fn)\n this._stack.push(fn)\n }, this)\n}\n\n/**\n * Adds a single function after a function that already exists in the\n * pipeline.\n *\n * Logs a warning if the function has not been registered.\n *\n * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline.\n * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline.\n */\nlunr.Pipeline.prototype.after = function (existingFn, newFn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(newFn)\n\n var pos = this._stack.indexOf(existingFn)\n if (pos == -1) {\n throw new Error('Cannot find existingFn')\n }\n\n pos = pos + 1\n this._stack.splice(pos, 0, newFn)\n}\n\n/**\n * Adds a single function before a function that already exists in the\n * pipeline.\n *\n * Logs a warning if the function has not been registered.\n *\n * @param {lunr.PipelineFunction} existingFn - A function that already exists in the pipeline.\n * @param {lunr.PipelineFunction} newFn - The new function to add to the pipeline.\n */\nlunr.Pipeline.prototype.before = function (existingFn, newFn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(newFn)\n\n var pos = this._stack.indexOf(existingFn)\n if (pos == -1) {\n throw new Error('Cannot find existingFn')\n }\n\n this._stack.splice(pos, 0, newFn)\n}\n\n/**\n * Removes a function from the pipeline.\n *\n * @param {lunr.PipelineFunction} fn The function to remove from the pipeline.\n */\nlunr.Pipeline.prototype.remove = function (fn) {\n var pos = this._stack.indexOf(fn)\n if (pos == -1) {\n return\n }\n\n this._stack.splice(pos, 1)\n}\n\n/**\n * Runs the current list of functions that make up the pipeline against the\n * passed tokens.\n *\n * @param {Array} tokens The tokens to run through the pipeline.\n * @returns {Array}\n */\nlunr.Pipeline.prototype.run = function (tokens) {\n var stackLength = this._stack.length\n\n for (var i = 0; i < stackLength; i++) {\n var fn = this._stack[i]\n var memo = []\n\n for (var j = 0; j < tokens.length; j++) {\n var result = fn(tokens[j], j, tokens)\n\n if (result === null || result === void 0 || result === '') continue\n\n if (Array.isArray(result)) {\n for (var k = 0; k < result.length; k++) {\n memo.push(result[k])\n }\n } else {\n memo.push(result)\n }\n }\n\n tokens = memo\n }\n\n return tokens\n}\n\n/**\n * Convenience method for passing a string through a pipeline and getting\n * strings out. This method takes care of wrapping the passed string in a\n * token and mapping the resulting tokens back to strings.\n *\n * @param {string} str - The string to pass through the pipeline.\n * @param {?object} metadata - Optional metadata to associate with the token\n * passed to the pipeline.\n * @returns {string[]}\n */\nlunr.Pipeline.prototype.runString = function (str, metadata) {\n var token = new lunr.Token (str, metadata)\n\n return this.run([token]).map(function (t) {\n return t.toString()\n })\n}\n\n/**\n * Resets the pipeline by removing any existing processors.\n *\n */\nlunr.Pipeline.prototype.reset = function () {\n this._stack = []\n}\n\n/**\n * Returns a representation of the pipeline ready for serialisation.\n *\n * Logs a warning if the function has not been registered.\n *\n * @returns {Array}\n */\nlunr.Pipeline.prototype.toJSON = function () {\n return this._stack.map(function (fn) {\n lunr.Pipeline.warnIfFunctionNotRegistered(fn)\n\n return fn.label\n })\n}\n/*!\n * lunr.Vector\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A vector is used to construct the vector space of documents and queries. These\n * vectors support operations to determine the similarity between two documents or\n * a document and a query.\n *\n * Normally no parameters are required for initializing a vector, but in the case of\n * loading a previously dumped vector the raw elements can be provided to the constructor.\n *\n * For performance reasons vectors are implemented with a flat array, where an elements\n * index is immediately followed by its value. E.g. [index, value, index, value]. This\n * allows the underlying array to be as sparse as possible and still offer decent\n * performance when being used for vector calculations.\n *\n * @constructor\n * @param {Number[]} [elements] - The flat list of element index and element value pairs.\n */\nlunr.Vector = function (elements) {\n this._magnitude = 0\n this.elements = elements || []\n}\n\n\n/**\n * Calculates the position within the vector to insert a given index.\n *\n * This is used internally by insert and upsert. If there are duplicate indexes then\n * the position is returned as if the value for that index were to be updated, but it\n * is the callers responsibility to check whether there is a duplicate at that index\n *\n * @param {Number} insertIdx - The index at which the element should be inserted.\n * @returns {Number}\n */\nlunr.Vector.prototype.positionForIndex = function (index) {\n // For an empty vector the tuple can be inserted at the beginning\n if (this.elements.length == 0) {\n return 0\n }\n\n var start = 0,\n end = this.elements.length / 2,\n sliceLength = end - start,\n pivotPoint = Math.floor(sliceLength / 2),\n pivotIndex = this.elements[pivotPoint * 2]\n\n while (sliceLength > 1) {\n if (pivotIndex < index) {\n start = pivotPoint\n }\n\n if (pivotIndex > index) {\n end = pivotPoint\n }\n\n if (pivotIndex == index) {\n break\n }\n\n sliceLength = end - start\n pivotPoint = start + Math.floor(sliceLength / 2)\n pivotIndex = this.elements[pivotPoint * 2]\n }\n\n if (pivotIndex == index) {\n return pivotPoint * 2\n }\n\n if (pivotIndex > index) {\n return pivotPoint * 2\n }\n\n if (pivotIndex < index) {\n return (pivotPoint + 1) * 2\n }\n}\n\n/**\n * Inserts an element at an index within the vector.\n *\n * Does not allow duplicates, will throw an error if there is already an entry\n * for this index.\n *\n * @param {Number} insertIdx - The index at which the element should be inserted.\n * @param {Number} val - The value to be inserted into the vector.\n */\nlunr.Vector.prototype.insert = function (insertIdx, val) {\n this.upsert(insertIdx, val, function () {\n throw \"duplicate index\"\n })\n}\n\n/**\n * Inserts or updates an existing index within the vector.\n *\n * @param {Number} insertIdx - The index at which the element should be inserted.\n * @param {Number} val - The value to be inserted into the vector.\n * @param {function} fn - A function that is called for updates, the existing value and the\n * requested value are passed as arguments\n */\nlunr.Vector.prototype.upsert = function (insertIdx, val, fn) {\n this._magnitude = 0\n var position = this.positionForIndex(insertIdx)\n\n if (this.elements[position] == insertIdx) {\n this.elements[position + 1] = fn(this.elements[position + 1], val)\n } else {\n this.elements.splice(position, 0, insertIdx, val)\n }\n}\n\n/**\n * Calculates the magnitude of this vector.\n *\n * @returns {Number}\n */\nlunr.Vector.prototype.magnitude = function () {\n if (this._magnitude) return this._magnitude\n\n var sumOfSquares = 0,\n elementsLength = this.elements.length\n\n for (var i = 1; i < elementsLength; i += 2) {\n var val = this.elements[i]\n sumOfSquares += val * val\n }\n\n return this._magnitude = Math.sqrt(sumOfSquares)\n}\n\n/**\n * Calculates the dot product of this vector and another vector.\n *\n * @param {lunr.Vector} otherVector - The vector to compute the dot product with.\n * @returns {Number}\n */\nlunr.Vector.prototype.dot = function (otherVector) {\n var dotProduct = 0,\n a = this.elements, b = otherVector.elements,\n aLen = a.length, bLen = b.length,\n aVal = 0, bVal = 0,\n i = 0, j = 0\n\n while (i < aLen && j < bLen) {\n aVal = a[i], bVal = b[j]\n if (aVal < bVal) {\n i += 2\n } else if (aVal > bVal) {\n j += 2\n } else if (aVal == bVal) {\n dotProduct += a[i + 1] * b[j + 1]\n i += 2\n j += 2\n }\n }\n\n return dotProduct\n}\n\n/**\n * Calculates the similarity between this vector and another vector.\n *\n * @param {lunr.Vector} otherVector - The other vector to calculate the\n * similarity with.\n * @returns {Number}\n */\nlunr.Vector.prototype.similarity = function (otherVector) {\n return this.dot(otherVector) / this.magnitude() || 0\n}\n\n/**\n * Converts the vector to an array of the elements within the vector.\n *\n * @returns {Number[]}\n */\nlunr.Vector.prototype.toArray = function () {\n var output = new Array (this.elements.length / 2)\n\n for (var i = 1, j = 0; i < this.elements.length; i += 2, j++) {\n output[j] = this.elements[i]\n }\n\n return output\n}\n\n/**\n * A JSON serializable representation of the vector.\n *\n * @returns {Number[]}\n */\nlunr.Vector.prototype.toJSON = function () {\n return this.elements\n}\n/* eslint-disable */\n/*!\n * lunr.stemmer\n * Copyright (C) 2020 Oliver Nightingale\n * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt\n */\n\n/**\n * lunr.stemmer is an english language stemmer, this is a JavaScript\n * implementation of the PorterStemmer taken from http://tartarus.org/~martin\n *\n * @static\n * @implements {lunr.PipelineFunction}\n * @param {lunr.Token} token - The string to stem\n * @returns {lunr.Token}\n * @see {@link lunr.Pipeline}\n * @function\n */\nlunr.stemmer = (function(){\n var step2list = {\n \"ational\" : \"ate\",\n \"tional\" : \"tion\",\n \"enci\" : \"ence\",\n \"anci\" : \"ance\",\n \"izer\" : \"ize\",\n \"bli\" : \"ble\",\n \"alli\" : \"al\",\n \"entli\" : \"ent\",\n \"eli\" : \"e\",\n \"ousli\" : \"ous\",\n \"ization\" : \"ize\",\n \"ation\" : \"ate\",\n \"ator\" : \"ate\",\n \"alism\" : \"al\",\n \"iveness\" : \"ive\",\n \"fulness\" : \"ful\",\n \"ousness\" : \"ous\",\n \"aliti\" : \"al\",\n \"iviti\" : \"ive\",\n \"biliti\" : \"ble\",\n \"logi\" : \"log\"\n },\n\n step3list = {\n \"icate\" : \"ic\",\n \"ative\" : \"\",\n \"alize\" : \"al\",\n \"iciti\" : \"ic\",\n \"ical\" : \"ic\",\n \"ful\" : \"\",\n \"ness\" : \"\"\n },\n\n c = \"[^aeiou]\", // consonant\n v = \"[aeiouy]\", // vowel\n C = c + \"[^aeiouy]*\", // consonant sequence\n V = v + \"[aeiou]*\", // vowel sequence\n\n mgr0 = \"^(\" + C + \")?\" + V + C, // [C]VC... is m>0\n meq1 = \"^(\" + C + \")?\" + V + C + \"(\" + V + \")?$\", // [C]VC[V] is m=1\n mgr1 = \"^(\" + C + \")?\" + V + C + V + C, // [C]VCVC... is m>1\n s_v = \"^(\" + C + \")?\" + v; // vowel in stem\n\n var re_mgr0 = new RegExp(mgr0);\n var re_mgr1 = new RegExp(mgr1);\n var re_meq1 = new RegExp(meq1);\n var re_s_v = new RegExp(s_v);\n\n var re_1a = /^(.+?)(ss|i)es$/;\n var re2_1a = /^(.+?)([^s])s$/;\n var re_1b = /^(.+?)eed$/;\n var re2_1b = /^(.+?)(ed|ing)$/;\n var re_1b_2 = /.$/;\n var re2_1b_2 = /(at|bl|iz)$/;\n var re3_1b_2 = new RegExp(\"([^aeiouylsz])\\\\1$\");\n var re4_1b_2 = new RegExp(\"^\" + C + v + \"[^aeiouwxy]$\");\n\n var re_1c = /^(.+?[^aeiou])y$/;\n var re_2 = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;\n\n var re_3 = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;\n\n var re_4 = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;\n var re2_4 = /^(.+?)(s|t)(ion)$/;\n\n var re_5 = /^(.+?)e$/;\n var re_5_1 = /ll$/;\n var re3_5 = new RegExp(\"^\" + C + v + \"[^aeiouwxy]$\");\n\n var porterStemmer = function porterStemmer(w) {\n var stem,\n suffix,\n firstch,\n re,\n re2,\n re3,\n re4;\n\n if (w.length < 3) { return w; }\n\n firstch = w.substr(0,1);\n if (firstch == \"y\") {\n w = firstch.toUpperCase() + w.substr(1);\n }\n\n // Step 1a\n re = re_1a\n re2 = re2_1a;\n\n if (re.test(w)) { w = w.replace(re,\"$1$2\"); }\n else if (re2.test(w)) { w = w.replace(re2,\"$1$2\"); }\n\n // Step 1b\n re = re_1b;\n re2 = re2_1b;\n if (re.test(w)) {\n var fp = re.exec(w);\n re = re_mgr0;\n if (re.test(fp[1])) {\n re = re_1b_2;\n w = w.replace(re,\"\");\n }\n } else if (re2.test(w)) {\n var fp = re2.exec(w);\n stem = fp[1];\n re2 = re_s_v;\n if (re2.test(stem)) {\n w = stem;\n re2 = re2_1b_2;\n re3 = re3_1b_2;\n re4 = re4_1b_2;\n if (re2.test(w)) { w = w + \"e\"; }\n else if (re3.test(w)) { re = re_1b_2; w = w.replace(re,\"\"); }\n else if (re4.test(w)) { w = w + \"e\"; }\n }\n }\n\n // Step 1c - replace suffix y or Y by i if preceded by a non-vowel which is not the first letter of the word (so cry -> cri, by -> by, say -> say)\n re = re_1c;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n w = stem + \"i\";\n }\n\n // Step 2\n re = re_2;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n suffix = fp[2];\n re = re_mgr0;\n if (re.test(stem)) {\n w = stem + step2list[suffix];\n }\n }\n\n // Step 3\n re = re_3;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n suffix = fp[2];\n re = re_mgr0;\n if (re.test(stem)) {\n w = stem + step3list[suffix];\n }\n }\n\n // Step 4\n re = re_4;\n re2 = re2_4;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n re = re_mgr1;\n if (re.test(stem)) {\n w = stem;\n }\n } else if (re2.test(w)) {\n var fp = re2.exec(w);\n stem = fp[1] + fp[2];\n re2 = re_mgr1;\n if (re2.test(stem)) {\n w = stem;\n }\n }\n\n // Step 5\n re = re_5;\n if (re.test(w)) {\n var fp = re.exec(w);\n stem = fp[1];\n re = re_mgr1;\n re2 = re_meq1;\n re3 = re3_5;\n if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) {\n w = stem;\n }\n }\n\n re = re_5_1;\n re2 = re_mgr1;\n if (re.test(w) && re2.test(w)) {\n re = re_1b_2;\n w = w.replace(re,\"\");\n }\n\n // and turn initial Y back to y\n\n if (firstch == \"y\") {\n w = firstch.toLowerCase() + w.substr(1);\n }\n\n return w;\n };\n\n return function (token) {\n return token.update(porterStemmer);\n }\n})();\n\nlunr.Pipeline.registerFunction(lunr.stemmer, 'stemmer')\n/*!\n * lunr.stopWordFilter\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.generateStopWordFilter builds a stopWordFilter function from the provided\n * list of stop words.\n *\n * The built in lunr.stopWordFilter is built using this generator and can be used\n * to generate custom stopWordFilters for applications or non English languages.\n *\n * @function\n * @param {Array} token The token to pass through the filter\n * @returns {lunr.PipelineFunction}\n * @see lunr.Pipeline\n * @see lunr.stopWordFilter\n */\nlunr.generateStopWordFilter = function (stopWords) {\n var words = stopWords.reduce(function (memo, stopWord) {\n memo[stopWord] = stopWord\n return memo\n }, {})\n\n return function (token) {\n if (token && words[token.toString()] !== token.toString()) return token\n }\n}\n\n/**\n * lunr.stopWordFilter is an English language stop word list filter, any words\n * contained in the list will not be passed through the filter.\n *\n * This is intended to be used in the Pipeline. If the token does not pass the\n * filter then undefined will be returned.\n *\n * @function\n * @implements {lunr.PipelineFunction}\n * @params {lunr.Token} token - A token to check for being a stop word.\n * @returns {lunr.Token}\n * @see {@link lunr.Pipeline}\n */\nlunr.stopWordFilter = lunr.generateStopWordFilter([\n 'a',\n 'able',\n 'about',\n 'across',\n 'after',\n 'all',\n 'almost',\n 'also',\n 'am',\n 'among',\n 'an',\n 'and',\n 'any',\n 'are',\n 'as',\n 'at',\n 'be',\n 'because',\n 'been',\n 'but',\n 'by',\n 'can',\n 'cannot',\n 'could',\n 'dear',\n 'did',\n 'do',\n 'does',\n 'either',\n 'else',\n 'ever',\n 'every',\n 'for',\n 'from',\n 'get',\n 'got',\n 'had',\n 'has',\n 'have',\n 'he',\n 'her',\n 'hers',\n 'him',\n 'his',\n 'how',\n 'however',\n 'i',\n 'if',\n 'in',\n 'into',\n 'is',\n 'it',\n 'its',\n 'just',\n 'least',\n 'let',\n 'like',\n 'likely',\n 'may',\n 'me',\n 'might',\n 'most',\n 'must',\n 'my',\n 'neither',\n 'no',\n 'nor',\n 'not',\n 'of',\n 'off',\n 'often',\n 'on',\n 'only',\n 'or',\n 'other',\n 'our',\n 'own',\n 'rather',\n 'said',\n 'say',\n 'says',\n 'she',\n 'should',\n 'since',\n 'so',\n 'some',\n 'than',\n 'that',\n 'the',\n 'their',\n 'them',\n 'then',\n 'there',\n 'these',\n 'they',\n 'this',\n 'tis',\n 'to',\n 'too',\n 'twas',\n 'us',\n 'wants',\n 'was',\n 'we',\n 'were',\n 'what',\n 'when',\n 'where',\n 'which',\n 'while',\n 'who',\n 'whom',\n 'why',\n 'will',\n 'with',\n 'would',\n 'yet',\n 'you',\n 'your'\n])\n\nlunr.Pipeline.registerFunction(lunr.stopWordFilter, 'stopWordFilter')\n/*!\n * lunr.trimmer\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.trimmer is a pipeline function for trimming non word\n * characters from the beginning and end of tokens before they\n * enter the index.\n *\n * This implementation may not work correctly for non latin\n * characters and should either be removed or adapted for use\n * with languages with non-latin characters.\n *\n * @static\n * @implements {lunr.PipelineFunction}\n * @param {lunr.Token} token The token to pass through the filter\n * @returns {lunr.Token}\n * @see lunr.Pipeline\n */\nlunr.trimmer = function (token) {\n return token.update(function (s) {\n return s.replace(/^\\W+/, '').replace(/\\W+$/, '')\n })\n}\n\nlunr.Pipeline.registerFunction(lunr.trimmer, 'trimmer')\n/*!\n * lunr.TokenSet\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * A token set is used to store the unique list of all tokens\n * within an index. Token sets are also used to represent an\n * incoming query to the index, this query token set and index\n * token set are then intersected to find which tokens to look\n * up in the inverted index.\n *\n * A token set can hold multiple tokens, as in the case of the\n * index token set, or it can hold a single token as in the\n * case of a simple query token set.\n *\n * Additionally token sets are used to perform wildcard matching.\n * Leading, contained and trailing wildcards are supported, and\n * from this edit distance matching can also be provided.\n *\n * Token sets are implemented as a minimal finite state automata,\n * where both common prefixes and suffixes are shared between tokens.\n * This helps to reduce the space used for storing the token set.\n *\n * @constructor\n */\nlunr.TokenSet = function () {\n this.final = false\n this.edges = {}\n this.id = lunr.TokenSet._nextId\n lunr.TokenSet._nextId += 1\n}\n\n/**\n * Keeps track of the next, auto increment, identifier to assign\n * to a new tokenSet.\n *\n * TokenSets require a unique identifier to be correctly minimised.\n *\n * @private\n */\nlunr.TokenSet._nextId = 1\n\n/**\n * Creates a TokenSet instance from the given sorted array of words.\n *\n * @param {String[]} arr - A sorted array of strings to create the set from.\n * @returns {lunr.TokenSet}\n * @throws Will throw an error if the input array is not sorted.\n */\nlunr.TokenSet.fromArray = function (arr) {\n var builder = new lunr.TokenSet.Builder\n\n for (var i = 0, len = arr.length; i < len; i++) {\n builder.insert(arr[i])\n }\n\n builder.finish()\n return builder.root\n}\n\n/**\n * Creates a token set from a query clause.\n *\n * @private\n * @param {Object} clause - A single clause from lunr.Query.\n * @param {string} clause.term - The query clause term.\n * @param {number} [clause.editDistance] - The optional edit distance for the term.\n * @returns {lunr.TokenSet}\n */\nlunr.TokenSet.fromClause = function (clause) {\n if ('editDistance' in clause) {\n return lunr.TokenSet.fromFuzzyString(clause.term, clause.editDistance)\n } else {\n return lunr.TokenSet.fromString(clause.term)\n }\n}\n\n/**\n * Creates a token set representing a single string with a specified\n * edit distance.\n *\n * Insertions, deletions, substitutions and transpositions are each\n * treated as an edit distance of 1.\n *\n * Increasing the allowed edit distance will have a dramatic impact\n * on the performance of both creating and intersecting these TokenSets.\n * It is advised to keep the edit distance less than 3.\n *\n * @param {string} str - The string to create the token set from.\n * @param {number} editDistance - The allowed edit distance to match.\n * @returns {lunr.Vector}\n */\nlunr.TokenSet.fromFuzzyString = function (str, editDistance) {\n var root = new lunr.TokenSet\n\n var stack = [{\n node: root,\n editsRemaining: editDistance,\n str: str\n }]\n\n while (stack.length) {\n var frame = stack.pop()\n\n // no edit\n if (frame.str.length > 0) {\n var char = frame.str.charAt(0),\n noEditNode\n\n if (char in frame.node.edges) {\n noEditNode = frame.node.edges[char]\n } else {\n noEditNode = new lunr.TokenSet\n frame.node.edges[char] = noEditNode\n }\n\n if (frame.str.length == 1) {\n noEditNode.final = true\n }\n\n stack.push({\n node: noEditNode,\n editsRemaining: frame.editsRemaining,\n str: frame.str.slice(1)\n })\n }\n\n if (frame.editsRemaining == 0) {\n continue\n }\n\n // insertion\n if (\"*\" in frame.node.edges) {\n var insertionNode = frame.node.edges[\"*\"]\n } else {\n var insertionNode = new lunr.TokenSet\n frame.node.edges[\"*\"] = insertionNode\n }\n\n if (frame.str.length == 0) {\n insertionNode.final = true\n }\n\n stack.push({\n node: insertionNode,\n editsRemaining: frame.editsRemaining - 1,\n str: frame.str\n })\n\n // deletion\n // can only do a deletion if we have enough edits remaining\n // and if there are characters left to delete in the string\n if (frame.str.length > 1) {\n stack.push({\n node: frame.node,\n editsRemaining: frame.editsRemaining - 1,\n str: frame.str.slice(1)\n })\n }\n\n // deletion\n // just removing the last character from the str\n if (frame.str.length == 1) {\n frame.node.final = true\n }\n\n // substitution\n // can only do a substitution if we have enough edits remaining\n // and if there are characters left to substitute\n if (frame.str.length >= 1) {\n if (\"*\" in frame.node.edges) {\n var substitutionNode = frame.node.edges[\"*\"]\n } else {\n var substitutionNode = new lunr.TokenSet\n frame.node.edges[\"*\"] = substitutionNode\n }\n\n if (frame.str.length == 1) {\n substitutionNode.final = true\n }\n\n stack.push({\n node: substitutionNode,\n editsRemaining: frame.editsRemaining - 1,\n str: frame.str.slice(1)\n })\n }\n\n // transposition\n // can only do a transposition if there are edits remaining\n // and there are enough characters to transpose\n if (frame.str.length > 1) {\n var charA = frame.str.charAt(0),\n charB = frame.str.charAt(1),\n transposeNode\n\n if (charB in frame.node.edges) {\n transposeNode = frame.node.edges[charB]\n } else {\n transposeNode = new lunr.TokenSet\n frame.node.edges[charB] = transposeNode\n }\n\n if (frame.str.length == 1) {\n transposeNode.final = true\n }\n\n stack.push({\n node: transposeNode,\n editsRemaining: frame.editsRemaining - 1,\n str: charA + frame.str.slice(2)\n })\n }\n }\n\n return root\n}\n\n/**\n * Creates a TokenSet from a string.\n *\n * The string may contain one or more wildcard characters (*)\n * that will allow wildcard matching when intersecting with\n * another TokenSet.\n *\n * @param {string} str - The string to create a TokenSet from.\n * @returns {lunr.TokenSet}\n */\nlunr.TokenSet.fromString = function (str) {\n var node = new lunr.TokenSet,\n root = node\n\n /*\n * Iterates through all characters within the passed string\n * appending a node for each character.\n *\n * When a wildcard character is found then a self\n * referencing edge is introduced to continually match\n * any number of any characters.\n */\n for (var i = 0, len = str.length; i < len; i++) {\n var char = str[i],\n final = (i == len - 1)\n\n if (char == \"*\") {\n node.edges[char] = node\n node.final = final\n\n } else {\n var next = new lunr.TokenSet\n next.final = final\n\n node.edges[char] = next\n node = next\n }\n }\n\n return root\n}\n\n/**\n * Converts this TokenSet into an array of strings\n * contained within the TokenSet.\n *\n * This is not intended to be used on a TokenSet that\n * contains wildcards, in these cases the results are\n * undefined and are likely to cause an infinite loop.\n *\n * @returns {string[]}\n */\nlunr.TokenSet.prototype.toArray = function () {\n var words = []\n\n var stack = [{\n prefix: \"\",\n node: this\n }]\n\n while (stack.length) {\n var frame = stack.pop(),\n edges = Object.keys(frame.node.edges),\n len = edges.length\n\n if (frame.node.final) {\n /* In Safari, at this point the prefix is sometimes corrupted, see:\n * https://github.com/olivernn/lunr.js/issues/279 Calling any\n * String.prototype method forces Safari to \"cast\" this string to what\n * it's supposed to be, fixing the bug. */\n frame.prefix.charAt(0)\n words.push(frame.prefix)\n }\n\n for (var i = 0; i < len; i++) {\n var edge = edges[i]\n\n stack.push({\n prefix: frame.prefix.concat(edge),\n node: frame.node.edges[edge]\n })\n }\n }\n\n return words\n}\n\n/**\n * Generates a string representation of a TokenSet.\n *\n * This is intended to allow TokenSets to be used as keys\n * in objects, largely to aid the construction and minimisation\n * of a TokenSet. As such it is not designed to be a human\n * friendly representation of the TokenSet.\n *\n * @returns {string}\n */\nlunr.TokenSet.prototype.toString = function () {\n // NOTE: Using Object.keys here as this.edges is very likely\n // to enter 'hash-mode' with many keys being added\n //\n // avoiding a for-in loop here as it leads to the function\n // being de-optimised (at least in V8). From some simple\n // benchmarks the performance is comparable, but allowing\n // V8 to optimize may mean easy performance wins in the future.\n\n if (this._str) {\n return this._str\n }\n\n var str = this.final ? '1' : '0',\n labels = Object.keys(this.edges).sort(),\n len = labels.length\n\n for (var i = 0; i < len; i++) {\n var label = labels[i],\n node = this.edges[label]\n\n str = str + label + node.id\n }\n\n return str\n}\n\n/**\n * Returns a new TokenSet that is the intersection of\n * this TokenSet and the passed TokenSet.\n *\n * This intersection will take into account any wildcards\n * contained within the TokenSet.\n *\n * @param {lunr.TokenSet} b - An other TokenSet to intersect with.\n * @returns {lunr.TokenSet}\n */\nlunr.TokenSet.prototype.intersect = function (b) {\n var output = new lunr.TokenSet,\n frame = undefined\n\n var stack = [{\n qNode: b,\n output: output,\n node: this\n }]\n\n while (stack.length) {\n frame = stack.pop()\n\n // NOTE: As with the #toString method, we are using\n // Object.keys and a for loop instead of a for-in loop\n // as both of these objects enter 'hash' mode, causing\n // the function to be de-optimised in V8\n var qEdges = Object.keys(frame.qNode.edges),\n qLen = qEdges.length,\n nEdges = Object.keys(frame.node.edges),\n nLen = nEdges.length\n\n for (var q = 0; q < qLen; q++) {\n var qEdge = qEdges[q]\n\n for (var n = 0; n < nLen; n++) {\n var nEdge = nEdges[n]\n\n if (nEdge == qEdge || qEdge == '*') {\n var node = frame.node.edges[nEdge],\n qNode = frame.qNode.edges[qEdge],\n final = node.final && qNode.final,\n next = undefined\n\n if (nEdge in frame.output.edges) {\n // an edge already exists for this character\n // no need to create a new node, just set the finality\n // bit unless this node is already final\n next = frame.output.edges[nEdge]\n next.final = next.final || final\n\n } else {\n // no edge exists yet, must create one\n // set the finality bit and insert it\n // into the output\n next = new lunr.TokenSet\n next.final = final\n frame.output.edges[nEdge] = next\n }\n\n stack.push({\n qNode: qNode,\n output: next,\n node: node\n })\n }\n }\n }\n }\n\n return output\n}\nlunr.TokenSet.Builder = function () {\n this.previousWord = \"\"\n this.root = new lunr.TokenSet\n this.uncheckedNodes = []\n this.minimizedNodes = {}\n}\n\nlunr.TokenSet.Builder.prototype.insert = function (word) {\n var node,\n commonPrefix = 0\n\n if (word < this.previousWord) {\n throw new Error (\"Out of order word insertion\")\n }\n\n for (var i = 0; i < word.length && i < this.previousWord.length; i++) {\n if (word[i] != this.previousWord[i]) break\n commonPrefix++\n }\n\n this.minimize(commonPrefix)\n\n if (this.uncheckedNodes.length == 0) {\n node = this.root\n } else {\n node = this.uncheckedNodes[this.uncheckedNodes.length - 1].child\n }\n\n for (var i = commonPrefix; i < word.length; i++) {\n var nextNode = new lunr.TokenSet,\n char = word[i]\n\n node.edges[char] = nextNode\n\n this.uncheckedNodes.push({\n parent: node,\n char: char,\n child: nextNode\n })\n\n node = nextNode\n }\n\n node.final = true\n this.previousWord = word\n}\n\nlunr.TokenSet.Builder.prototype.finish = function () {\n this.minimize(0)\n}\n\nlunr.TokenSet.Builder.prototype.minimize = function (downTo) {\n for (var i = this.uncheckedNodes.length - 1; i >= downTo; i--) {\n var node = this.uncheckedNodes[i],\n childKey = node.child.toString()\n\n if (childKey in this.minimizedNodes) {\n node.parent.edges[node.char] = this.minimizedNodes[childKey]\n } else {\n // Cache the key for this node since\n // we know it can't change anymore\n node.child._str = childKey\n\n this.minimizedNodes[childKey] = node.child\n }\n\n this.uncheckedNodes.pop()\n }\n}\n/*!\n * lunr.Index\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * An index contains the built index of all documents and provides a query interface\n * to the index.\n *\n * Usually instances of lunr.Index will not be created using this constructor, instead\n * lunr.Builder should be used to construct new indexes, or lunr.Index.load should be\n * used to load previously built and serialized indexes.\n *\n * @constructor\n * @param {Object} attrs - The attributes of the built search index.\n * @param {Object} attrs.invertedIndex - An index of term/field to document reference.\n * @param {Object} attrs.fieldVectors - Field vectors\n * @param {lunr.TokenSet} attrs.tokenSet - An set of all corpus tokens.\n * @param {string[]} attrs.fields - The names of indexed document fields.\n * @param {lunr.Pipeline} attrs.pipeline - The pipeline to use for search terms.\n */\nlunr.Index = function (attrs) {\n this.invertedIndex = attrs.invertedIndex\n this.fieldVectors = attrs.fieldVectors\n this.tokenSet = attrs.tokenSet\n this.fields = attrs.fields\n this.pipeline = attrs.pipeline\n}\n\n/**\n * A result contains details of a document matching a search query.\n * @typedef {Object} lunr.Index~Result\n * @property {string} ref - The reference of the document this result represents.\n * @property {number} score - A number between 0 and 1 representing how similar this document is to the query.\n * @property {lunr.MatchData} matchData - Contains metadata about this match including which term(s) caused the match.\n */\n\n/**\n * Although lunr provides the ability to create queries using lunr.Query, it also provides a simple\n * query language which itself is parsed into an instance of lunr.Query.\n *\n * For programmatically building queries it is advised to directly use lunr.Query, the query language\n * is best used for human entered text rather than program generated text.\n *\n * At its simplest queries can just be a single term, e.g. `hello`, multiple terms are also supported\n * and will be combined with OR, e.g `hello world` will match documents that contain either 'hello'\n * or 'world', though those that contain both will rank higher in the results.\n *\n * Wildcards can be included in terms to match one or more unspecified characters, these wildcards can\n * be inserted anywhere within the term, and more than one wildcard can exist in a single term. Adding\n * wildcards will increase the number of documents that will be found but can also have a negative\n * impact on query performance, especially with wildcards at the beginning of a term.\n *\n * Terms can be restricted to specific fields, e.g. `title:hello`, only documents with the term\n * hello in the title field will match this query. Using a field not present in the index will lead\n * to an error being thrown.\n *\n * Modifiers can also be added to terms, lunr supports edit distance and boost modifiers on terms. A term\n * boost will make documents matching that term score higher, e.g. `foo^5`. Edit distance is also supported\n * to provide fuzzy matching, e.g. 'hello~2' will match documents with hello with an edit distance of 2.\n * Avoid large values for edit distance to improve query performance.\n *\n * Each term also supports a presence modifier. By default a term's presence in document is optional, however\n * this can be changed to either required or prohibited. For a term's presence to be required in a document the\n * term should be prefixed with a '+', e.g. `+foo bar` is a search for documents that must contain 'foo' and\n * optionally contain 'bar'. Conversely a leading '-' sets the terms presence to prohibited, i.e. it must not\n * appear in a document, e.g. `-foo bar` is a search for documents that do not contain 'foo' but may contain 'bar'.\n *\n * To escape special characters the backslash character '\\' can be used, this allows searches to include\n * characters that would normally be considered modifiers, e.g. `foo\\~2` will search for a term \"foo~2\" instead\n * of attempting to apply a boost of 2 to the search term \"foo\".\n *\n * @typedef {string} lunr.Index~QueryString\n * @example Simple single term query\n * hello\n * @example Multiple term query\n * hello world\n * @example term scoped to a field\n * title:hello\n * @example term with a boost of 10\n * hello^10\n * @example term with an edit distance of 2\n * hello~2\n * @example terms with presence modifiers\n * -foo +bar baz\n */\n\n/**\n * Performs a search against the index using lunr query syntax.\n *\n * Results will be returned sorted by their score, the most relevant results\n * will be returned first. For details on how the score is calculated, please see\n * the {@link https://lunrjs.com/guides/searching.html#scoring|guide}.\n *\n * For more programmatic querying use lunr.Index#query.\n *\n * @param {lunr.Index~QueryString} queryString - A string containing a lunr query.\n * @throws {lunr.QueryParseError} If the passed query string cannot be parsed.\n * @returns {lunr.Index~Result[]}\n */\nlunr.Index.prototype.search = function (queryString) {\n return this.query(function (query) {\n var parser = new lunr.QueryParser(queryString, query)\n parser.parse()\n })\n}\n\n/**\n * A query builder callback provides a query object to be used to express\n * the query to perform on the index.\n *\n * @callback lunr.Index~queryBuilder\n * @param {lunr.Query} query - The query object to build up.\n * @this lunr.Query\n */\n\n/**\n * Performs a query against the index using the yielded lunr.Query object.\n *\n * If performing programmatic queries against the index, this method is preferred\n * over lunr.Index#search so as to avoid the additional query parsing overhead.\n *\n * A query object is yielded to the supplied function which should be used to\n * express the query to be run against the index.\n *\n * Note that although this function takes a callback parameter it is _not_ an\n * asynchronous operation, the callback is just yielded a query object to be\n * customized.\n *\n * @param {lunr.Index~queryBuilder} fn - A function that is used to build the query.\n * @returns {lunr.Index~Result[]}\n */\nlunr.Index.prototype.query = function (fn) {\n // for each query clause\n // * process terms\n // * expand terms from token set\n // * find matching documents and metadata\n // * get document vectors\n // * score documents\n\n var query = new lunr.Query(this.fields),\n matchingFields = Object.create(null),\n queryVectors = Object.create(null),\n termFieldCache = Object.create(null),\n requiredMatches = Object.create(null),\n prohibitedMatches = Object.create(null)\n\n /*\n * To support field level boosts a query vector is created per\n * field. An empty vector is eagerly created to support negated\n * queries.\n */\n for (var i = 0; i < this.fields.length; i++) {\n queryVectors[this.fields[i]] = new lunr.Vector\n }\n\n fn.call(query, query)\n\n for (var i = 0; i < query.clauses.length; i++) {\n /*\n * Unless the pipeline has been disabled for this term, which is\n * the case for terms with wildcards, we need to pass the clause\n * term through the search pipeline. A pipeline returns an array\n * of processed terms. Pipeline functions may expand the passed\n * term, which means we may end up performing multiple index lookups\n * for a single query term.\n */\n var clause = query.clauses[i],\n terms = null,\n clauseMatches = lunr.Set.empty\n\n if (clause.usePipeline) {\n terms = this.pipeline.runString(clause.term, {\n fields: clause.fields\n })\n } else {\n terms = [clause.term]\n }\n\n for (var m = 0; m < terms.length; m++) {\n var term = terms[m]\n\n /*\n * Each term returned from the pipeline needs to use the same query\n * clause object, e.g. the same boost and or edit distance. The\n * simplest way to do this is to re-use the clause object but mutate\n * its term property.\n */\n clause.term = term\n\n /*\n * From the term in the clause we create a token set which will then\n * be used to intersect the indexes token set to get a list of terms\n * to lookup in the inverted index\n */\n var termTokenSet = lunr.TokenSet.fromClause(clause),\n expandedTerms = this.tokenSet.intersect(termTokenSet).toArray()\n\n /*\n * If a term marked as required does not exist in the tokenSet it is\n * impossible for the search to return any matches. We set all the field\n * scoped required matches set to empty and stop examining any further\n * clauses.\n */\n if (expandedTerms.length === 0 && clause.presence === lunr.Query.presence.REQUIRED) {\n for (var k = 0; k < clause.fields.length; k++) {\n var field = clause.fields[k]\n requiredMatches[field] = lunr.Set.empty\n }\n\n break\n }\n\n for (var j = 0; j < expandedTerms.length; j++) {\n /*\n * For each term get the posting and termIndex, this is required for\n * building the query vector.\n */\n var expandedTerm = expandedTerms[j],\n posting = this.invertedIndex[expandedTerm],\n termIndex = posting._index\n\n for (var k = 0; k < clause.fields.length; k++) {\n /*\n * For each field that this query term is scoped by (by default\n * all fields are in scope) we need to get all the document refs\n * that have this term in that field.\n *\n * The posting is the entry in the invertedIndex for the matching\n * term from above.\n */\n var field = clause.fields[k],\n fieldPosting = posting[field],\n matchingDocumentRefs = Object.keys(fieldPosting),\n termField = expandedTerm + \"/\" + field,\n matchingDocumentsSet = new lunr.Set(matchingDocumentRefs)\n\n /*\n * if the presence of this term is required ensure that the matching\n * documents are added to the set of required matches for this clause.\n *\n */\n if (clause.presence == lunr.Query.presence.REQUIRED) {\n clauseMatches = clauseMatches.union(matchingDocumentsSet)\n\n if (requiredMatches[field] === undefined) {\n requiredMatches[field] = lunr.Set.complete\n }\n }\n\n /*\n * if the presence of this term is prohibited ensure that the matching\n * documents are added to the set of prohibited matches for this field,\n * creating that set if it does not yet exist.\n */\n if (clause.presence == lunr.Query.presence.PROHIBITED) {\n if (prohibitedMatches[field] === undefined) {\n prohibitedMatches[field] = lunr.Set.empty\n }\n\n prohibitedMatches[field] = prohibitedMatches[field].union(matchingDocumentsSet)\n\n /*\n * Prohibited matches should not be part of the query vector used for\n * similarity scoring and no metadata should be extracted so we continue\n * to the next field\n */\n continue\n }\n\n /*\n * The query field vector is populated using the termIndex found for\n * the term and a unit value with the appropriate boost applied.\n * Using upsert because there could already be an entry in the vector\n * for the term we are working with. In that case we just add the scores\n * together.\n */\n queryVectors[field].upsert(termIndex, clause.boost, function (a, b) { return a + b })\n\n /**\n * If we've already seen this term, field combo then we've already collected\n * the matching documents and metadata, no need to go through all that again\n */\n if (termFieldCache[termField]) {\n continue\n }\n\n for (var l = 0; l < matchingDocumentRefs.length; l++) {\n /*\n * All metadata for this term/field/document triple\n * are then extracted and collected into an instance\n * of lunr.MatchData ready to be returned in the query\n * results\n */\n var matchingDocumentRef = matchingDocumentRefs[l],\n matchingFieldRef = new lunr.FieldRef (matchingDocumentRef, field),\n metadata = fieldPosting[matchingDocumentRef],\n fieldMatch\n\n if ((fieldMatch = matchingFields[matchingFieldRef]) === undefined) {\n matchingFields[matchingFieldRef] = new lunr.MatchData (expandedTerm, field, metadata)\n } else {\n fieldMatch.add(expandedTerm, field, metadata)\n }\n\n }\n\n termFieldCache[termField] = true\n }\n }\n }\n\n /**\n * If the presence was required we need to update the requiredMatches field sets.\n * We do this after all fields for the term have collected their matches because\n * the clause terms presence is required in _any_ of the fields not _all_ of the\n * fields.\n */\n if (clause.presence === lunr.Query.presence.REQUIRED) {\n for (var k = 0; k < clause.fields.length; k++) {\n var field = clause.fields[k]\n requiredMatches[field] = requiredMatches[field].intersect(clauseMatches)\n }\n }\n }\n\n /**\n * Need to combine the field scoped required and prohibited\n * matching documents into a global set of required and prohibited\n * matches\n */\n var allRequiredMatches = lunr.Set.complete,\n allProhibitedMatches = lunr.Set.empty\n\n for (var i = 0; i < this.fields.length; i++) {\n var field = this.fields[i]\n\n if (requiredMatches[field]) {\n allRequiredMatches = allRequiredMatches.intersect(requiredMatches[field])\n }\n\n if (prohibitedMatches[field]) {\n allProhibitedMatches = allProhibitedMatches.union(prohibitedMatches[field])\n }\n }\n\n var matchingFieldRefs = Object.keys(matchingFields),\n results = [],\n matches = Object.create(null)\n\n /*\n * If the query is negated (contains only prohibited terms)\n * we need to get _all_ fieldRefs currently existing in the\n * index. This is only done when we know that the query is\n * entirely prohibited terms to avoid any cost of getting all\n * fieldRefs unnecessarily.\n *\n * Additionally, blank MatchData must be created to correctly\n * populate the results.\n */\n if (query.isNegated()) {\n matchingFieldRefs = Object.keys(this.fieldVectors)\n\n for (var i = 0; i < matchingFieldRefs.length; i++) {\n var matchingFieldRef = matchingFieldRefs[i]\n var fieldRef = lunr.FieldRef.fromString(matchingFieldRef)\n matchingFields[matchingFieldRef] = new lunr.MatchData\n }\n }\n\n for (var i = 0; i < matchingFieldRefs.length; i++) {\n /*\n * Currently we have document fields that match the query, but we\n * need to return documents. The matchData and scores are combined\n * from multiple fields belonging to the same document.\n *\n * Scores are calculated by field, using the query vectors created\n * above, and combined into a final document score using addition.\n */\n var fieldRef = lunr.FieldRef.fromString(matchingFieldRefs[i]),\n docRef = fieldRef.docRef\n\n if (!allRequiredMatches.contains(docRef)) {\n continue\n }\n\n if (allProhibitedMatches.contains(docRef)) {\n continue\n }\n\n var fieldVector = this.fieldVectors[fieldRef],\n score = queryVectors[fieldRef.fieldName].similarity(fieldVector),\n docMatch\n\n if ((docMatch = matches[docRef]) !== undefined) {\n docMatch.score += score\n docMatch.matchData.combine(matchingFields[fieldRef])\n } else {\n var match = {\n ref: docRef,\n score: score,\n matchData: matchingFields[fieldRef]\n }\n matches[docRef] = match\n results.push(match)\n }\n }\n\n /*\n * Sort the results objects by score, highest first.\n */\n return results.sort(function (a, b) {\n return b.score - a.score\n })\n}\n\n/**\n * Prepares the index for JSON serialization.\n *\n * The schema for this JSON blob will be described in a\n * separate JSON schema file.\n *\n * @returns {Object}\n */\nlunr.Index.prototype.toJSON = function () {\n var invertedIndex = Object.keys(this.invertedIndex)\n .sort()\n .map(function (term) {\n return [term, this.invertedIndex[term]]\n }, this)\n\n var fieldVectors = Object.keys(this.fieldVectors)\n .map(function (ref) {\n return [ref, this.fieldVectors[ref].toJSON()]\n }, this)\n\n return {\n version: lunr.version,\n fields: this.fields,\n fieldVectors: fieldVectors,\n invertedIndex: invertedIndex,\n pipeline: this.pipeline.toJSON()\n }\n}\n\n/**\n * Loads a previously serialized lunr.Index\n *\n * @param {Object} serializedIndex - A previously serialized lunr.Index\n * @returns {lunr.Index}\n */\nlunr.Index.load = function (serializedIndex) {\n var attrs = {},\n fieldVectors = {},\n serializedVectors = serializedIndex.fieldVectors,\n invertedIndex = Object.create(null),\n serializedInvertedIndex = serializedIndex.invertedIndex,\n tokenSetBuilder = new lunr.TokenSet.Builder,\n pipeline = lunr.Pipeline.load(serializedIndex.pipeline)\n\n if (serializedIndex.version != lunr.version) {\n lunr.utils.warn(\"Version mismatch when loading serialised index. Current version of lunr '\" + lunr.version + \"' does not match serialized index '\" + serializedIndex.version + \"'\")\n }\n\n for (var i = 0; i < serializedVectors.length; i++) {\n var tuple = serializedVectors[i],\n ref = tuple[0],\n elements = tuple[1]\n\n fieldVectors[ref] = new lunr.Vector(elements)\n }\n\n for (var i = 0; i < serializedInvertedIndex.length; i++) {\n var tuple = serializedInvertedIndex[i],\n term = tuple[0],\n posting = tuple[1]\n\n tokenSetBuilder.insert(term)\n invertedIndex[term] = posting\n }\n\n tokenSetBuilder.finish()\n\n attrs.fields = serializedIndex.fields\n\n attrs.fieldVectors = fieldVectors\n attrs.invertedIndex = invertedIndex\n attrs.tokenSet = tokenSetBuilder.root\n attrs.pipeline = pipeline\n\n return new lunr.Index(attrs)\n}\n/*!\n * lunr.Builder\n * Copyright (C) 2020 Oliver Nightingale\n */\n\n/**\n * lunr.Builder performs indexing on a set of documents and\n * returns instances of lunr.Index ready for querying.\n *\n * All configuration of the index is done via the builder, the\n * fields to index, the document reference, the text processing\n * pipeline and document scoring parameters are all set on the\n * builder before indexing.\n *\n * @constructor\n * @property {string} _ref - Internal reference to the document reference field.\n * @property {string[]} _fields - Internal reference to the document fields to index.\n * @property {object} invertedIndex - The inverted index maps terms to document fields.\n * @property {object} documentTermFrequencies - Keeps track of document term frequencies.\n * @property {object} documentLengths - Keeps track of the length of documents added to the index.\n * @property {lunr.tokenizer} tokenizer - Function for splitting strings into tokens for indexing.\n * @property {lunr.Pipeline} pipeline - The pipeline performs text processing on tokens before indexing.\n * @property {lunr.Pipeline} searchPipeline - A pipeline for processing search terms before querying the index.\n * @property {number} documentCount - Keeps track of the total number of documents indexed.\n * @property {number} _b - A parameter to control field length normalization, setting this to 0 disabled normalization, 1 fully normalizes field lengths, the default value is 0.75.\n * @property {number} _k1 - A parameter to control how quickly an increase in term frequency results in term frequency saturation, the default value is 1.2.\n * @property {number} termIndex - A counter incremented for each unique term, used to identify a terms position in the vector space.\n * @property {array} metadataWhitelist - A list of metadata keys that have been whitelisted for entry in the index.\n */\nlunr.Builder = function () {\n this._ref = \"id\"\n this._fields = Object.create(null)\n this._documents = Object.create(null)\n this.invertedIndex = Object.create(null)\n this.fieldTermFrequencies = {}\n this.fieldLengths = {}\n this.tokenizer = lunr.tokenizer\n this.pipeline = new lunr.Pipeline\n this.searchPipeline = new lunr.Pipeline\n this.documentCount = 0\n this._b = 0.75\n this._k1 = 1.2\n this.termIndex = 0\n this.metadataWhitelist = []\n}\n\n/**\n * Sets the document field used as the document reference. Every document must have this field.\n * The type of this field in the document should be a string, if it is not a string it will be\n * coerced into a string by calling toString.\n *\n * The default ref is 'id'.\n *\n * The ref should _not_ be changed during indexing, it should be set before any documents are\n * added to the index. Changing it during indexing can lead to inconsistent results.\n *\n * @param {string} ref - The name of the reference field in the document.\n */\nlunr.Builder.prototype.ref = function (ref) {\n this._ref = ref\n}\n\n/**\n * A function that is used to extract a field from a document.\n *\n * Lunr expects a field to be at the top level of a document, if however the field\n * is deeply nested within a document an extractor function can be used to extract\n * the right field for indexing.\n *\n * @callback fieldExtractor\n * @param {object} doc - The document being added to the index.\n * @returns {?(string|object|object[])} obj - The object that will be indexed for this field.\n * @example Extracting a nested field\n * function (doc) { return doc.nested.field }\n */\n\n/**\n * Adds a field to the list of document fields that will be indexed. Every document being\n * indexed should have this field. Null values for this field in indexed documents will\n * not cause errors but will limit the chance of that document being retrieved by searches.\n *\n * All fields should be added before adding documents to the index. Adding fields after\n * a document has been indexed will have no effect on already indexed documents.\n *\n * Fields can be boosted at build time. This allows terms within that field to have more\n * importance when ranking search results. Use a field boost to specify that matches within\n * one field are more important than other fields.\n *\n * @param {string} fieldName - The name of a field to index in all documents.\n * @param {object} attributes - Optional attributes associated with this field.\n * @param {number} [attributes.boost=1] - Boost applied to all terms within this field.\n * @param {fieldExtractor} [attributes.extractor] - Function to extract a field from a document.\n * @throws {RangeError} fieldName cannot contain unsupported characters '/'\n */\nlunr.Builder.prototype.field = function (fieldName, attributes) {\n if (/\\//.test(fieldName)) {\n throw new RangeError (\"Field '\" + fieldName + \"' contains illegal character '/'\")\n }\n\n this._fields[fieldName] = attributes || {}\n}\n\n/**\n * A parameter to tune the amount of field length normalisation that is applied when\n * calculating relevance scores. A value of 0 will completely disable any normalisation\n * and a value of 1 will fully normalise field lengths. The default is 0.75. Values of b\n * will be clamped to the range 0 - 1.\n *\n * @param {number} number - The value to set for this tuning parameter.\n */\nlunr.Builder.prototype.b = function (number) {\n if (number < 0) {\n this._b = 0\n } else if (number > 1) {\n this._b = 1\n } else {\n this._b = number\n }\n}\n\n/**\n * A parameter that controls the speed at which a rise in term frequency results in term\n * frequency saturation. The default value is 1.2. Setting this to a higher value will give\n * slower saturation levels, a lower value will result in quicker saturation.\n *\n * @param {number} number - The value to set for this tuning parameter.\n */\nlunr.Builder.prototype.k1 = function (number) {\n this._k1 = number\n}\n\n/**\n * Adds a document to the index.\n *\n * Before adding fields to the index the index should have been fully setup, with the document\n * ref and all fields to index already having been specified.\n *\n * The document must have a field name as specified by the ref (by default this is 'id') and\n * it should have all fields defined for indexing, though null or undefined values will not\n * cause errors.\n *\n * Entire documents can be boosted at build time. Applying a boost to a document indicates that\n * this document should rank higher in search results than other documents.\n *\n * @param {object} doc - The document to add to the index.\n * @param {object} attributes - Optional attributes associated with this document.\n * @param {number} [attributes.boost=1] - Boost applied to all terms within this document.\n */\nlunr.Builder.prototype.add = function (doc, attributes) {\n var docRef = doc[this._ref],\n fields = Object.keys(this._fields)\n\n this._documents[docRef] = attributes || {}\n this.documentCount += 1\n\n for (var i = 0; i < fields.length; i++) {\n var fieldName = fields[i],\n extractor = this._fields[fieldName].extractor,\n field = extractor ? extractor(doc) : doc[fieldName],\n tokens = this.tokenizer(field, {\n fields: [fieldName]\n }),\n terms = this.pipeline.run(tokens),\n fieldRef = new lunr.FieldRef (docRef, fieldName),\n fieldTerms = Object.create(null)\n\n this.fieldTermFrequencies[fieldRef] = fieldTerms\n this.fieldLengths[fieldRef] = 0\n\n // store the length of this field for this document\n this.fieldLengths[fieldRef] += terms.length\n\n // calculate term frequencies for this field\n for (var j = 0; j < terms.length; j++) {\n var term = terms[j]\n\n if (fieldTerms[term] == undefined) {\n fieldTerms[term] = 0\n }\n\n fieldTerms[term] += 1\n\n // add to inverted index\n // create an initial posting if one doesn't exist\n if (this.invertedIndex[term] == undefined) {\n var posting = Object.create(null)\n posting[\"_index\"] = this.termIndex\n this.termIndex += 1\n\n for (var k = 0; k < fields.length; k++) {\n posting[fields[k]] = Object.create(null)\n }\n\n this.invertedIndex[term] = posting\n }\n\n // add an entry for this term/fieldName/docRef to the invertedIndex\n if (this.invertedIndex[term][fieldName][docRef] == undefined) {\n this.invertedIndex[term][fieldName][docRef] = Object.create(null)\n }\n\n // store all whitelisted metadata about this token in the\n // inverted index\n for (var l = 0; l < this.metadataWhitelist.length; l++) {\n var metadataKey = this.metadataWhitelist[l],\n metadata = term.metadata[metadataKey]\n\n if (this.invertedIndex[term][fieldName][docRef][metadataKey] == undefined) {\n this.invertedIndex[term][fieldName][docRef][metadataKey] = []\n }\n\n this.invertedIndex[term][fieldName][docRef][metadataKey].push(metadata)\n }\n }\n\n }\n}\n\n/**\n * Calculates the average document length for this index\n *\n * @private\n */\nlunr.Builder.prototype.calculateAverageFieldLengths = function () {\n\n var fieldRefs = Object.keys(this.fieldLengths),\n numberOfFields = fieldRefs.length,\n accumulator = {},\n documentsWithField = {}\n\n for (var i = 0; i < numberOfFields; i++) {\n var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]),\n field = fieldRef.fieldName\n\n documentsWithField[field] || (documentsWithField[field] = 0)\n documentsWithField[field] += 1\n\n accumulator[field] || (accumulator[field] = 0)\n accumulator[field] += this.fieldLengths[fieldRef]\n }\n\n var fields = Object.keys(this._fields)\n\n for (var i = 0; i < fields.length; i++) {\n var fieldName = fields[i]\n accumulator[fieldName] = accumulator[fieldName] / documentsWithField[fieldName]\n }\n\n this.averageFieldLength = accumulator\n}\n\n/**\n * Builds a vector space model of every document using lunr.Vector\n *\n * @private\n */\nlunr.Builder.prototype.createFieldVectors = function () {\n var fieldVectors = {},\n fieldRefs = Object.keys(this.fieldTermFrequencies),\n fieldRefsLength = fieldRefs.length,\n termIdfCache = Object.create(null)\n\n for (var i = 0; i < fieldRefsLength; i++) {\n var fieldRef = lunr.FieldRef.fromString(fieldRefs[i]),\n fieldName = fieldRef.fieldName,\n fieldLength = this.fieldLengths[fieldRef],\n fieldVector = new lunr.Vector,\n termFrequencies = this.fieldTermFrequencies[fieldRef],\n terms = Object.keys(termFrequencies),\n termsLength = terms.length\n\n\n var fieldBoost = this._fields[fieldName].boost || 1,\n docBoost = this._documents[fieldRef.docRef].boost || 1\n\n for (var j = 0; j < termsLength; j++) {\n var term = terms[j],\n tf = termFrequencies[term],\n termIndex = this.invertedIndex[term]._index,\n idf, score, scoreWithPrecision\n\n if (termIdfCache[term] === undefined) {\n idf = lunr.idf(this.invertedIndex[term], this.documentCount)\n termIdfCache[term] = idf\n } else {\n idf = termIdfCache[term]\n }\n\n score = idf * ((this._k1 + 1) * tf) / (this._k1 * (1 - this._b + this._b * (fieldLength / this.averageFieldLength[fieldName])) + tf)\n score *= fieldBoost\n score *= docBoost\n scoreWithPrecision = Math.round(score * 1000) / 1000\n // Converts 1.23456789 to 1.234.\n // Reducing the precision so that the vectors take up less\n // space when serialised. Doing it now so that they behave\n // the same before and after serialisation. Also, this is\n // the fastest approach to reducing a number's precision in\n // JavaScript.\n\n fieldVector.insert(termIndex, scoreWithPrecision)\n }\n\n fieldVectors[fieldRef] = fieldVector\n }\n\n this.fieldVectors = fieldVectors\n}\n\n/**\n * Creates a token set of all tokens in the index using lunr.TokenSet\n *\n * @private\n */\nlunr.Builder.prototype.createTokenSet = function () {\n this.tokenSet = lunr.TokenSet.fromArray(\n Object.keys(this.invertedIndex).sort()\n )\n}\n\n/**\n * Builds the index, creating an instance of lunr.Index.\n *\n * This completes the indexing process and should only be called\n * once all documents have been added to the index.\n *\n * @returns {lunr.Index}\n */\nlunr.Builder.prototype.build = function () {\n this.calculateAverageFieldLengths()\n this.createFieldVectors()\n this.createTokenSet()\n\n return new lunr.Index({\n invertedIndex: this.invertedIndex,\n fieldVectors: this.fieldVectors,\n tokenSet: this.tokenSet,\n fields: Object.keys(this._fields),\n pipeline: this.searchPipeline\n })\n}\n\n/**\n * Applies a plugin to the index builder.\n *\n * A plugin is a function that is called with the index builder as its context.\n * Plugins can be used to customise or extend the behaviour of the index\n * in some way. A plugin is just a function, that encapsulated the custom\n * behaviour that should be applied when building the index.\n *\n * The plugin function will be called with the index builder as its argument, additional\n * arguments can also be passed when calling use. The function will be called\n * with the index builder as its context.\n *\n * @param {Function} plugin The plugin to apply.\n */\nlunr.Builder.prototype.use = function (fn) {\n var args = Array.prototype.slice.call(arguments, 1)\n args.unshift(this)\n fn.apply(this, args)\n}\n/**\n * Contains and collects metadata about a matching document.\n * A single instance of lunr.MatchData is returned as part of every\n * lunr.Index~Result.\n *\n * @constructor\n * @param {string} term - The term this match data is associated with\n * @param {string} field - The field in which the term was found\n * @param {object} metadata - The metadata recorded about this term in this field\n * @property {object} metadata - A cloned collection of metadata associated with this document.\n * @see {@link lunr.Index~Result}\n */\nlunr.MatchData = function (term, field, metadata) {\n var clonedMetadata = Object.create(null),\n metadataKeys = Object.keys(metadata || {})\n\n // Cloning the metadata to prevent the original\n // being mutated during match data combination.\n // Metadata is kept in an array within the inverted\n // index so cloning the data can be done with\n // Array#slice\n for (var i = 0; i < metadataKeys.length; i++) {\n var key = metadataKeys[i]\n clonedMetadata[key] = metadata[key].slice()\n }\n\n this.metadata = Object.create(null)\n\n if (term !== undefined) {\n this.metadata[term] = Object.create(null)\n this.metadata[term][field] = clonedMetadata\n }\n}\n\n/**\n * An instance of lunr.MatchData will be created for every term that matches a\n * document. However only one instance is required in a lunr.Index~Result. This\n * method combines metadata from another instance of lunr.MatchData with this\n * objects metadata.\n *\n * @param {lunr.MatchData} otherMatchData - Another instance of match data to merge with this one.\n * @see {@link lunr.Index~Result}\n */\nlunr.MatchData.prototype.combine = function (otherMatchData) {\n var terms = Object.keys(otherMatchData.metadata)\n\n for (var i = 0; i < terms.length; i++) {\n var term = terms[i],\n fields = Object.keys(otherMatchData.metadata[term])\n\n if (this.metadata[term] == undefined) {\n this.metadata[term] = Object.create(null)\n }\n\n for (var j = 0; j < fields.length; j++) {\n var field = fields[j],\n keys = Object.keys(otherMatchData.metadata[term][field])\n\n if (this.metadata[term][field] == undefined) {\n this.metadata[term][field] = Object.create(null)\n }\n\n for (var k = 0; k < keys.length; k++) {\n var key = keys[k]\n\n if (this.metadata[term][field][key] == undefined) {\n this.metadata[term][field][key] = otherMatchData.metadata[term][field][key]\n } else {\n this.metadata[term][field][key] = this.metadata[term][field][key].concat(otherMatchData.metadata[term][field][key])\n }\n\n }\n }\n }\n}\n\n/**\n * Add metadata for a term/field pair to this instance of match data.\n *\n * @param {string} term - The term this match data is associated with\n * @param {string} field - The field in which the term was found\n * @param {object} metadata - The metadata recorded about this term in this field\n */\nlunr.MatchData.prototype.add = function (term, field, metadata) {\n if (!(term in this.metadata)) {\n this.metadata[term] = Object.create(null)\n this.metadata[term][field] = metadata\n return\n }\n\n if (!(field in this.metadata[term])) {\n this.metadata[term][field] = metadata\n return\n }\n\n var metadataKeys = Object.keys(metadata)\n\n for (var i = 0; i < metadataKeys.length; i++) {\n var key = metadataKeys[i]\n\n if (key in this.metadata[term][field]) {\n this.metadata[term][field][key] = this.metadata[term][field][key].concat(metadata[key])\n } else {\n this.metadata[term][field][key] = metadata[key]\n }\n }\n}\n/**\n * A lunr.Query provides a programmatic way of defining queries to be performed\n * against a {@link lunr.Index}.\n *\n * Prefer constructing a lunr.Query using the {@link lunr.Index#query} method\n * so the query object is pre-initialized with the right index fields.\n *\n * @constructor\n * @property {lunr.Query~Clause[]} clauses - An array of query clauses.\n * @property {string[]} allFields - An array of all available fields in a lunr.Index.\n */\nlunr.Query = function (allFields) {\n this.clauses = []\n this.allFields = allFields\n}\n\n/**\n * Constants for indicating what kind of automatic wildcard insertion will be used when constructing a query clause.\n *\n * This allows wildcards to be added to the beginning and end of a term without having to manually do any string\n * concatenation.\n *\n * The wildcard constants can be bitwise combined to select both leading and trailing wildcards.\n *\n * @constant\n * @default\n * @property {number} wildcard.NONE - The term will have no wildcards inserted, this is the default behaviour\n * @property {number} wildcard.LEADING - Prepend the term with a wildcard, unless a leading wildcard already exists\n * @property {number} wildcard.TRAILING - Append a wildcard to the term, unless a trailing wildcard already exists\n * @see lunr.Query~Clause\n * @see lunr.Query#clause\n * @see lunr.Query#term\n * @example query term with trailing wildcard\n * query.term('foo', { wildcard: lunr.Query.wildcard.TRAILING })\n * @example query term with leading and trailing wildcard\n * query.term('foo', {\n * wildcard: lunr.Query.wildcard.LEADING | lunr.Query.wildcard.TRAILING\n * })\n */\n\nlunr.Query.wildcard = new String (\"*\")\nlunr.Query.wildcard.NONE = 0\nlunr.Query.wildcard.LEADING = 1\nlunr.Query.wildcard.TRAILING = 2\n\n/**\n * Constants for indicating what kind of presence a term must have in matching documents.\n *\n * @constant\n * @enum {number}\n * @see lunr.Query~Clause\n * @see lunr.Query#clause\n * @see lunr.Query#term\n * @example query term with required presence\n * query.term('foo', { presence: lunr.Query.presence.REQUIRED })\n */\nlunr.Query.presence = {\n /**\n * Term's presence in a document is optional, this is the default value.\n */\n OPTIONAL: 1,\n\n /**\n * Term's presence in a document is required, documents that do not contain\n * this term will not be returned.\n */\n REQUIRED: 2,\n\n /**\n * Term's presence in a document is prohibited, documents that do contain\n * this term will not be returned.\n */\n PROHIBITED: 3\n}\n\n/**\n * A single clause in a {@link lunr.Query} contains a term and details on how to\n * match that term against a {@link lunr.Index}.\n *\n * @typedef {Object} lunr.Query~Clause\n * @property {string[]} fields - The fields in an index this clause should be matched against.\n * @property {number} [boost=1] - Any boost that should be applied when matching this clause.\n * @property {number} [editDistance] - Whether the term should have fuzzy matching applied, and how fuzzy the match should be.\n * @property {boolean} [usePipeline] - Whether the term should be passed through the search pipeline.\n * @property {number} [wildcard=lunr.Query.wildcard.NONE] - Whether the term should have wildcards appended or prepended.\n * @property {number} [presence=lunr.Query.presence.OPTIONAL] - The terms presence in any matching documents.\n */\n\n/**\n * Adds a {@link lunr.Query~Clause} to this query.\n *\n * Unless the clause contains the fields to be matched all fields will be matched. In addition\n * a default boost of 1 is applied to the clause.\n *\n * @param {lunr.Query~Clause} clause - The clause to add to this query.\n * @see lunr.Query~Clause\n * @returns {lunr.Query}\n */\nlunr.Query.prototype.clause = function (clause) {\n if (!('fields' in clause)) {\n clause.fields = this.allFields\n }\n\n if (!('boost' in clause)) {\n clause.boost = 1\n }\n\n if (!('usePipeline' in clause)) {\n clause.usePipeline = true\n }\n\n if (!('wildcard' in clause)) {\n clause.wildcard = lunr.Query.wildcard.NONE\n }\n\n if ((clause.wildcard & lunr.Query.wildcard.LEADING) && (clause.term.charAt(0) != lunr.Query.wildcard)) {\n clause.term = \"*\" + clause.term\n }\n\n if ((clause.wildcard & lunr.Query.wildcard.TRAILING) && (clause.term.slice(-1) != lunr.Query.wildcard)) {\n clause.term = \"\" + clause.term + \"*\"\n }\n\n if (!('presence' in clause)) {\n clause.presence = lunr.Query.presence.OPTIONAL\n }\n\n this.clauses.push(clause)\n\n return this\n}\n\n/**\n * A negated query is one in which every clause has a presence of\n * prohibited. These queries require some special processing to return\n * the expected results.\n *\n * @returns boolean\n */\nlunr.Query.prototype.isNegated = function () {\n for (var i = 0; i < this.clauses.length; i++) {\n if (this.clauses[i].presence != lunr.Query.presence.PROHIBITED) {\n return false\n }\n }\n\n return true\n}\n\n/**\n * Adds a term to the current query, under the covers this will create a {@link lunr.Query~Clause}\n * to the list of clauses that make up this query.\n *\n * The term is used as is, i.e. no tokenization will be performed by this method. Instead conversion\n * to a token or token-like string should be done before calling this method.\n *\n * The term will be converted to a string by calling `toString`. Multiple terms can be passed as an\n * array, each term in the array will share the same options.\n *\n * @param {object|object[]} term - The term(s) to add to the query.\n * @param {object} [options] - Any additional properties to add to the query clause.\n * @returns {lunr.Query}\n * @see lunr.Query#clause\n * @see lunr.Query~Clause\n * @example adding a single term to a query\n * query.term(\"foo\")\n * @example adding a single term to a query and specifying search fields, term boost and automatic trailing wildcard\n * query.term(\"foo\", {\n * fields: [\"title\"],\n * boost: 10,\n * wildcard: lunr.Query.wildcard.TRAILING\n * })\n * @example using lunr.tokenizer to convert a string to tokens before using them as terms\n * query.term(lunr.tokenizer(\"foo bar\"))\n */\nlunr.Query.prototype.term = function (term, options) {\n if (Array.isArray(term)) {\n term.forEach(function (t) { this.term(t, lunr.utils.clone(options)) }, this)\n return this\n }\n\n var clause = options || {}\n clause.term = term.toString()\n\n this.clause(clause)\n\n return this\n}\nlunr.QueryParseError = function (message, start, end) {\n this.name = \"QueryParseError\"\n this.message = message\n this.start = start\n this.end = end\n}\n\nlunr.QueryParseError.prototype = new Error\nlunr.QueryLexer = function (str) {\n this.lexemes = []\n this.str = str\n this.length = str.length\n this.pos = 0\n this.start = 0\n this.escapeCharPositions = []\n}\n\nlunr.QueryLexer.prototype.run = function () {\n var state = lunr.QueryLexer.lexText\n\n while (state) {\n state = state(this)\n }\n}\n\nlunr.QueryLexer.prototype.sliceString = function () {\n var subSlices = [],\n sliceStart = this.start,\n sliceEnd = this.pos\n\n for (var i = 0; i < this.escapeCharPositions.length; i++) {\n sliceEnd = this.escapeCharPositions[i]\n subSlices.push(this.str.slice(sliceStart, sliceEnd))\n sliceStart = sliceEnd + 1\n }\n\n subSlices.push(this.str.slice(sliceStart, this.pos))\n this.escapeCharPositions.length = 0\n\n return subSlices.join('')\n}\n\nlunr.QueryLexer.prototype.emit = function (type) {\n this.lexemes.push({\n type: type,\n str: this.sliceString(),\n start: this.start,\n end: this.pos\n })\n\n this.start = this.pos\n}\n\nlunr.QueryLexer.prototype.escapeCharacter = function () {\n this.escapeCharPositions.push(this.pos - 1)\n this.pos += 1\n}\n\nlunr.QueryLexer.prototype.next = function () {\n if (this.pos >= this.length) {\n return lunr.QueryLexer.EOS\n }\n\n var char = this.str.charAt(this.pos)\n this.pos += 1\n return char\n}\n\nlunr.QueryLexer.prototype.width = function () {\n return this.pos - this.start\n}\n\nlunr.QueryLexer.prototype.ignore = function () {\n if (this.start == this.pos) {\n this.pos += 1\n }\n\n this.start = this.pos\n}\n\nlunr.QueryLexer.prototype.backup = function () {\n this.pos -= 1\n}\n\nlunr.QueryLexer.prototype.acceptDigitRun = function () {\n var char, charCode\n\n do {\n char = this.next()\n charCode = char.charCodeAt(0)\n } while (charCode > 47 && charCode < 58)\n\n if (char != lunr.QueryLexer.EOS) {\n this.backup()\n }\n}\n\nlunr.QueryLexer.prototype.more = function () {\n return this.pos < this.length\n}\n\nlunr.QueryLexer.EOS = 'EOS'\nlunr.QueryLexer.FIELD = 'FIELD'\nlunr.QueryLexer.TERM = 'TERM'\nlunr.QueryLexer.EDIT_DISTANCE = 'EDIT_DISTANCE'\nlunr.QueryLexer.BOOST = 'BOOST'\nlunr.QueryLexer.PRESENCE = 'PRESENCE'\n\nlunr.QueryLexer.lexField = function (lexer) {\n lexer.backup()\n lexer.emit(lunr.QueryLexer.FIELD)\n lexer.ignore()\n return lunr.QueryLexer.lexText\n}\n\nlunr.QueryLexer.lexTerm = function (lexer) {\n if (lexer.width() > 1) {\n lexer.backup()\n lexer.emit(lunr.QueryLexer.TERM)\n }\n\n lexer.ignore()\n\n if (lexer.more()) {\n return lunr.QueryLexer.lexText\n }\n}\n\nlunr.QueryLexer.lexEditDistance = function (lexer) {\n lexer.ignore()\n lexer.acceptDigitRun()\n lexer.emit(lunr.QueryLexer.EDIT_DISTANCE)\n return lunr.QueryLexer.lexText\n}\n\nlunr.QueryLexer.lexBoost = function (lexer) {\n lexer.ignore()\n lexer.acceptDigitRun()\n lexer.emit(lunr.QueryLexer.BOOST)\n return lunr.QueryLexer.lexText\n}\n\nlunr.QueryLexer.lexEOS = function (lexer) {\n if (lexer.width() > 0) {\n lexer.emit(lunr.QueryLexer.TERM)\n }\n}\n\n// This matches the separator used when tokenising fields\n// within a document. These should match otherwise it is\n// not possible to search for some tokens within a document.\n//\n// It is possible for the user to change the separator on the\n// tokenizer so it _might_ clash with any other of the special\n// characters already used within the search string, e.g. :.\n//\n// This means that it is possible to change the separator in\n// such a way that makes some words unsearchable using a search\n// string.\nlunr.QueryLexer.termSeparator = lunr.tokenizer.separator\n\nlunr.QueryLexer.lexText = function (lexer) {\n while (true) {\n var char = lexer.next()\n\n if (char == lunr.QueryLexer.EOS) {\n return lunr.QueryLexer.lexEOS\n }\n\n // Escape character is '\\'\n if (char.charCodeAt(0) == 92) {\n lexer.escapeCharacter()\n continue\n }\n\n if (char == \":\") {\n return lunr.QueryLexer.lexField\n }\n\n if (char == \"~\") {\n lexer.backup()\n if (lexer.width() > 0) {\n lexer.emit(lunr.QueryLexer.TERM)\n }\n return lunr.QueryLexer.lexEditDistance\n }\n\n if (char == \"^\") {\n lexer.backup()\n if (lexer.width() > 0) {\n lexer.emit(lunr.QueryLexer.TERM)\n }\n return lunr.QueryLexer.lexBoost\n }\n\n // \"+\" indicates term presence is required\n // checking for length to ensure that only\n // leading \"+\" are considered\n if (char == \"+\" && lexer.width() === 1) {\n lexer.emit(lunr.QueryLexer.PRESENCE)\n return lunr.QueryLexer.lexText\n }\n\n // \"-\" indicates term presence is prohibited\n // checking for length to ensure that only\n // leading \"-\" are considered\n if (char == \"-\" && lexer.width() === 1) {\n lexer.emit(lunr.QueryLexer.PRESENCE)\n return lunr.QueryLexer.lexText\n }\n\n if (char.match(lunr.QueryLexer.termSeparator)) {\n return lunr.QueryLexer.lexTerm\n }\n }\n}\n\nlunr.QueryParser = function (str, query) {\n this.lexer = new lunr.QueryLexer (str)\n this.query = query\n this.currentClause = {}\n this.lexemeIdx = 0\n}\n\nlunr.QueryParser.prototype.parse = function () {\n this.lexer.run()\n this.lexemes = this.lexer.lexemes\n\n var state = lunr.QueryParser.parseClause\n\n while (state) {\n state = state(this)\n }\n\n return this.query\n}\n\nlunr.QueryParser.prototype.peekLexeme = function () {\n return this.lexemes[this.lexemeIdx]\n}\n\nlunr.QueryParser.prototype.consumeLexeme = function () {\n var lexeme = this.peekLexeme()\n this.lexemeIdx += 1\n return lexeme\n}\n\nlunr.QueryParser.prototype.nextClause = function () {\n var completedClause = this.currentClause\n this.query.clause(completedClause)\n this.currentClause = {}\n}\n\nlunr.QueryParser.parseClause = function (parser) {\n var lexeme = parser.peekLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n switch (lexeme.type) {\n case lunr.QueryLexer.PRESENCE:\n return lunr.QueryParser.parsePresence\n case lunr.QueryLexer.FIELD:\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.TERM:\n return lunr.QueryParser.parseTerm\n default:\n var errorMessage = \"expected either a field or a term, found \" + lexeme.type\n\n if (lexeme.str.length >= 1) {\n errorMessage += \" with value '\" + lexeme.str + \"'\"\n }\n\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n}\n\nlunr.QueryParser.parsePresence = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n switch (lexeme.str) {\n case \"-\":\n parser.currentClause.presence = lunr.Query.presence.PROHIBITED\n break\n case \"+\":\n parser.currentClause.presence = lunr.Query.presence.REQUIRED\n break\n default:\n var errorMessage = \"unrecognised presence operator'\" + lexeme.str + \"'\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n var errorMessage = \"expecting term or field, found nothing\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.FIELD:\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.TERM:\n return lunr.QueryParser.parseTerm\n default:\n var errorMessage = \"expecting term or field, found '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseField = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n if (parser.query.allFields.indexOf(lexeme.str) == -1) {\n var possibleFields = parser.query.allFields.map(function (f) { return \"'\" + f + \"'\" }).join(', '),\n errorMessage = \"unrecognised field '\" + lexeme.str + \"', possible fields: \" + possibleFields\n\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n parser.currentClause.fields = [lexeme.str]\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n var errorMessage = \"expecting term, found nothing\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n return lunr.QueryParser.parseTerm\n default:\n var errorMessage = \"expecting term, found '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseTerm = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n parser.currentClause.term = lexeme.str.toLowerCase()\n\n if (lexeme.str.indexOf(\"*\") != -1) {\n parser.currentClause.usePipeline = false\n }\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n parser.nextClause()\n return\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n parser.nextClause()\n return lunr.QueryParser.parseTerm\n case lunr.QueryLexer.FIELD:\n parser.nextClause()\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.EDIT_DISTANCE:\n return lunr.QueryParser.parseEditDistance\n case lunr.QueryLexer.BOOST:\n return lunr.QueryParser.parseBoost\n case lunr.QueryLexer.PRESENCE:\n parser.nextClause()\n return lunr.QueryParser.parsePresence\n default:\n var errorMessage = \"Unexpected lexeme type '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseEditDistance = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n var editDistance = parseInt(lexeme.str, 10)\n\n if (isNaN(editDistance)) {\n var errorMessage = \"edit distance must be numeric\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n parser.currentClause.editDistance = editDistance\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n parser.nextClause()\n return\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n parser.nextClause()\n return lunr.QueryParser.parseTerm\n case lunr.QueryLexer.FIELD:\n parser.nextClause()\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.EDIT_DISTANCE:\n return lunr.QueryParser.parseEditDistance\n case lunr.QueryLexer.BOOST:\n return lunr.QueryParser.parseBoost\n case lunr.QueryLexer.PRESENCE:\n parser.nextClause()\n return lunr.QueryParser.parsePresence\n default:\n var errorMessage = \"Unexpected lexeme type '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\nlunr.QueryParser.parseBoost = function (parser) {\n var lexeme = parser.consumeLexeme()\n\n if (lexeme == undefined) {\n return\n }\n\n var boost = parseInt(lexeme.str, 10)\n\n if (isNaN(boost)) {\n var errorMessage = \"boost must be numeric\"\n throw new lunr.QueryParseError (errorMessage, lexeme.start, lexeme.end)\n }\n\n parser.currentClause.boost = boost\n\n var nextLexeme = parser.peekLexeme()\n\n if (nextLexeme == undefined) {\n parser.nextClause()\n return\n }\n\n switch (nextLexeme.type) {\n case lunr.QueryLexer.TERM:\n parser.nextClause()\n return lunr.QueryParser.parseTerm\n case lunr.QueryLexer.FIELD:\n parser.nextClause()\n return lunr.QueryParser.parseField\n case lunr.QueryLexer.EDIT_DISTANCE:\n return lunr.QueryParser.parseEditDistance\n case lunr.QueryLexer.BOOST:\n return lunr.QueryParser.parseBoost\n case lunr.QueryLexer.PRESENCE:\n parser.nextClause()\n return lunr.QueryParser.parsePresence\n default:\n var errorMessage = \"Unexpected lexeme type '\" + nextLexeme.type + \"'\"\n throw new lunr.QueryParseError (errorMessage, nextLexeme.start, nextLexeme.end)\n }\n}\n\n /**\n * export the module via AMD, CommonJS or as a browser global\n * Export code from https://github.com/umdjs/umd/blob/master/returnExports.js\n */\n ;(function (root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define(factory)\n } else if (typeof exports === 'object') {\n /**\n * Node. Does not work with strict CommonJS, but\n * only CommonJS-like enviroments that support module.exports,\n * like Node.\n */\n module.exports = factory()\n } else {\n // Browser globals (root is window)\n root.lunr = factory()\n }\n }(this, function () {\n /**\n * Just return a value to define the module export.\n * This example returns an object, but the module\n * can return a function as the exported value.\n */\n return lunr\n }))\n})();\n","\"use strict\";\n\n// eslint-disable-next-line func-names\nmodule.exports = function () {\n if (typeof globalThis === 'object') {\n return globalThis;\n }\n\n var g;\n\n try {\n // This works if eval is allowed (see CSP)\n // eslint-disable-next-line no-new-func\n g = this || new Function('return this')();\n } catch (e) {\n // This works if the window reference is available\n if (typeof window === 'object') {\n return window;\n } // This works if the self reference is available\n\n\n if (typeof self === 'object') {\n return self;\n } // This works if the global reference is available\n\n\n if (typeof global !== 'undefined') {\n return global;\n }\n }\n\n return g;\n}();","var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function(\"return this\")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n","/*! *****************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (_) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n};\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === \"return\" } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, privateMap) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to get private field on non-instance\");\r\n }\r\n return privateMap.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, privateMap, value) {\r\n if (!privateMap.has(receiver)) {\r\n throw new TypeError(\"attempted to set private field on non-instance\");\r\n }\r\n privateMap.set(receiver, value);\r\n return value;\r\n}\r\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A RTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { SearchIndex, SearchResult } from \"../../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search message type\n */\nexport const enum SearchMessageType {\n SETUP, /* Search index setup */\n READY, /* Search index ready */\n QUERY, /* Search query */\n RESULT /* Search results */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * A message containing the data necessary to setup the search index\n */\nexport interface SearchSetupMessage {\n type: SearchMessageType.SETUP /* Message type */\n data: SearchIndex /* Message data */\n}\n\n/**\n * A message indicating the search index is ready\n */\nexport interface SearchReadyMessage {\n type: SearchMessageType.READY /* Message type */\n}\n\n/**\n * A message containing a search query\n */\nexport interface SearchQueryMessage {\n type: SearchMessageType.QUERY /* Message type */\n data: string /* Message data */\n}\n\n/**\n * A message containing results for a search query\n */\nexport interface SearchResultMessage {\n type: SearchMessageType.RESULT /* Message type */\n data: SearchResult[] /* Message data */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * A message exchanged with the search worker\n */\nexport type SearchMessage =\n | SearchSetupMessage\n | SearchReadyMessage\n | SearchQueryMessage\n | SearchResultMessage\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Type guard for search setup messages\n *\n * @param message - Search worker message\n *\n * @return Test result\n */\nexport function isSearchSetupMessage(\n message: SearchMessage\n): message is SearchSetupMessage {\n return message.type === SearchMessageType.SETUP\n}\n\n/**\n * Type guard for search ready messages\n *\n * @param message - Search worker message\n *\n * @return Test result\n */\nexport function isSearchReadyMessage(\n message: SearchMessage\n): message is SearchReadyMessage {\n return message.type === SearchMessageType.READY\n}\n\n/**\n * Type guard for search query messages\n *\n * @param message - Search worker message\n *\n * @return Test result\n */\nexport function isSearchQueryMessage(\n message: SearchMessage\n): message is SearchQueryMessage {\n return message.type === SearchMessageType.QUERY\n}\n\n/**\n * Type guard for search result messages\n *\n * @param message - Search worker message\n *\n * @return Test result\n */\nexport function isSearchResultMessage(\n message: SearchMessage\n): message is SearchResultMessage {\n return message.type === SearchMessageType.RESULT\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport {\n SearchDocument,\n SearchDocumentMap,\n setupSearchDocumentMap\n} from \"../document\"\nimport {\n SearchHighlightFactoryFn,\n setupSearchHighlighter\n} from \"../highlighter\"\nimport {\n SearchQueryTerms,\n getSearchQueryTerms,\n parseSearchQuery\n} from \"../query\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search index configuration\n */\nexport interface SearchIndexConfig {\n lang: string[] /* Search languages */\n separator: string /* Search separator */\n}\n\n/**\n * Search index document\n */\nexport interface SearchIndexDocument {\n location: string /* Document location */\n title: string /* Document title */\n text: string /* Document text */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search index pipeline function\n */\nexport type SearchIndexPipelineFn =\n | \"trimmer\" /* Trimmer */\n | \"stopWordFilter\" /* Stop word filter */\n | \"stemmer\" /* Stemmer */\n\n/**\n * Search index pipeline\n */\nexport type SearchIndexPipeline = SearchIndexPipelineFn[]\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search index\n *\n * This interfaces describes the format of the `search_index.json` file which\n * is automatically built by the MkDocs search plugin.\n */\nexport interface SearchIndex {\n config: SearchIndexConfig /* Search index configuration */\n docs: SearchIndexDocument[] /* Search index documents */\n index?: object /* Prebuilt index */\n pipeline?: SearchIndexPipeline /* Search index pipeline */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search metadata\n */\nexport interface SearchMetadata {\n score: number /* Score (relevance) */\n terms: SearchQueryTerms /* Search query terms */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search result\n */\nexport type SearchResult = Array<\n SearchDocument & SearchMetadata\n> // tslint:disable-line\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Compute the difference of two lists of strings\n *\n * @param a - 1st list of strings\n * @param b - 2nd list of strings\n *\n * @return Difference\n */\nfunction difference(a: string[], b: string[]): string[] {\n const [x, y] = [new Set(a), new Set(b)]\n return [\n ...new Set([...x].filter(value => !y.has(value)))\n ]\n}\n\n/* ----------------------------------------------------------------------------\n * Class\n * ------------------------------------------------------------------------- */\n\n/**\n * Search index\n *\n * Note that `lunr` is injected via Webpack, as it will otherwise also be\n * bundled in the application bundle.\n */\nexport class Search {\n\n /**\n * Search document mapping\n *\n * A mapping of URLs (including hash fragments) to the actual articles and\n * sections of the documentation. The search document mapping must be created\n * regardless of whether the index was prebuilt or not, as `lunr` itself will\n * only store the actual index.\n */\n protected documents: SearchDocumentMap\n\n /**\n * Search highlight factory function\n */\n protected highlight: SearchHighlightFactoryFn\n\n /**\n * The underlying `lunr` search index\n */\n protected index: lunr.Index\n\n /**\n * Create the search integration\n *\n * @param data - Search index\n */\n public constructor({ config, docs, pipeline, index }: SearchIndex) {\n this.documents = setupSearchDocumentMap(docs)\n this.highlight = setupSearchHighlighter(config)\n\n /* Set separator for tokenizer */\n lunr.tokenizer.separator = new RegExp(config.separator)\n\n /* If no index was given, create it */\n if (typeof index === \"undefined\") {\n this.index = lunr(function() {\n\n /* Set up multi-language support */\n if (config.lang.length === 1 && config.lang[0] !== \"en\") {\n this.use((lunr as any)[config.lang[0]])\n } else if (config.lang.length > 1) {\n this.use((lunr as any).multiLanguage(...config.lang))\n }\n\n /* Compute functions to be removed from the pipeline */\n const fns = difference([\n \"trimmer\", \"stopWordFilter\", \"stemmer\"\n ], pipeline!)\n\n /* Remove functions from the pipeline for registered languages */\n for (const lang of config.lang.map(language => (\n language === \"en\" ? lunr : (lunr as any)[language]\n ))) {\n for (const fn of fns) {\n this.pipeline.remove(lang[fn])\n this.searchPipeline.remove(lang[fn])\n }\n }\n\n /* Set up fields and reference */\n this.field(\"title\", { boost: 1000 })\n this.field(\"text\")\n this.ref(\"location\")\n\n /* Index documents */\n for (const doc of docs)\n this.add(doc)\n })\n\n /* Handle prebuilt index */\n } else {\n this.index = lunr.Index.load(index)\n }\n }\n\n /**\n * Search for matching documents\n *\n * The search index which MkDocs provides is divided up into articles, which\n * contain the whole content of the individual pages, and sections, which only\n * contain the contents of the subsections obtained by breaking the individual\n * pages up at `h1` ... `h6`. As there may be many sections on different pages\n * with identical titles (for example within this very project, e.g. \"Usage\"\n * or \"Installation\"), they need to be put into the context of the containing\n * page. For this reason, section results are grouped within their respective\n * articles which are the top-level results that are returned.\n *\n * @param query - Query value\n *\n * @return Search results\n */\n public search(query: string): SearchResult[] {\n if (query) {\n try {\n const highlight = this.highlight(query)\n\n /* Parse query to extract clauses for analysis */\n const clauses = parseSearchQuery(query)\n .filter(clause => (\n clause.presence !== lunr.Query.presence.PROHIBITED\n ))\n\n /* Perform search and post-process results */\n const groups = this.index.search(`${query}*`)\n\n /* Apply post-query boosts based on title and search query terms */\n .reduce((results, { ref, score, matchData }) => {\n const document = this.documents.get(ref)\n if (typeof document !== \"undefined\") {\n const { location, title, text, parent } = document\n\n /* Compute and analyze search query terms */\n const terms = getSearchQueryTerms(\n clauses,\n Object.keys(matchData.metadata)\n )\n\n /* Highlight title and text and apply post-query boosts */\n const boost = +!parent + +Object.values(terms).every(t => t)\n results.push({\n location,\n title: highlight(title),\n text: highlight(text),\n score: score * (1 + boost),\n terms\n })\n }\n return results\n }, [])\n\n /* Sort search results again after applying boosts */\n .sort((a, b) => b.score - a.score)\n\n /* Group search results by page */\n .reduce((results, result) => {\n const document = this.documents.get(result.location)\n if (typeof document !== \"undefined\") {\n const ref = \"parent\" in document\n ? document.parent!.location\n : document.location\n results.set(ref, [...results.get(ref) || [], result])\n }\n return results\n }, new Map())\n\n /* Expand grouped search results */\n return [...groups.values()]\n\n /* Log errors to console (for now) */\n } catch {\n // tslint:disable-next-line no-console\n console.warn(`Invalid query: ${query} – see https://bit.ly/2s3ChXG`)\n }\n }\n\n /* Return nothing in case of error or empty query */\n return []\n }\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n// @ts-ignore\nimport * as escapeHTML from \"escape-html\"\n\nimport { SearchIndexDocument } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search document\n */\nexport interface SearchDocument extends SearchIndexDocument {\n parent?: SearchIndexDocument /* Parent article */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search document mapping\n */\nexport type SearchDocumentMap = Map\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Create a search document mapping\n *\n * @param docs - Search index documents\n *\n * @return Search document map\n */\nexport function setupSearchDocumentMap(\n docs: SearchIndexDocument[]\n): SearchDocumentMap {\n const documents = new Map()\n const parents = new Set()\n for (const doc of docs) {\n const [path, hash] = doc.location.split(\"#\")\n\n /* Extract location and title */\n const location = doc.location\n const title = doc.title\n\n /* Escape and cleanup text */\n const text = escapeHTML(doc.text)\n .replace(/\\s+(?=[,.:;!?])/g, \"\")\n .replace(/\\s+/g, \" \")\n\n /* Handle section */\n if (hash) {\n const parent = documents.get(path)!\n\n /* Ignore first section, override article */\n if (!parents.has(parent)) {\n parent.title = doc.title\n parent.text = text\n\n /* Remember that we processed the article */\n parents.add(parent)\n\n /* Add subsequent section */\n } else {\n documents.set(location, {\n location,\n title,\n text,\n parent\n })\n }\n\n /* Add article */\n } else {\n documents.set(location, {\n location,\n title,\n text\n })\n }\n }\n return documents\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport { SearchIndexConfig } from \"../_\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search highlight function\n *\n * @param value - Value\n *\n * @return Highlighted value\n */\nexport type SearchHighlightFn = (value: string) => string\n\n/**\n * Search highlight factory function\n *\n * @param query - Query value\n *\n * @return Search highlight function\n */\nexport type SearchHighlightFactoryFn = (query: string) => SearchHighlightFn\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Create a search highlighter\n *\n * @param config - Search index configuration\n *\n * @return Search highlight factory function\n */\nexport function setupSearchHighlighter(\n config: SearchIndexConfig\n): SearchHighlightFactoryFn {\n const separator = new RegExp(config.separator, \"img\")\n const highlight = (_: unknown, data: string, term: string) => {\n return `${data}${term}`\n }\n\n /* Return factory function */\n return (query: string) => {\n query = query\n .replace(/[\\s*+\\-:~^]+/g, \" \")\n .trim()\n\n /* Create search term match expression */\n const match = new RegExp(`(^|${config.separator})(${\n query\n .replace(/[|\\\\{}()[\\]^$+*?.-]/g, \"\\\\$&\")\n .replace(separator, \"|\")\n })`, \"img\")\n\n /* Highlight string value */\n return value => value\n .replace(match, highlight)\n .replace(/<\\/mark>(\\s+)]*>/img, \"\\$1\")\n }\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Search query clause\n */\nexport interface SearchQueryClause {\n presence: lunr.Query.presence /* Clause presence */\n term: string /* Clause term */\n}\n\n/* ------------------------------------------------------------------------- */\n\n/**\n * Search query terms\n */\nexport type SearchQueryTerms = Record\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Parse a search query for analysis\n *\n * @param value - Query value\n *\n * @return Search query clauses\n */\nexport function parseSearchQuery(\n value: string\n): SearchQueryClause[] {\n const query = new (lunr as any).Query([\"title\", \"text\"])\n const parser = new (lunr as any).QueryParser(value, query)\n\n /* Parse and return query clauses */\n parser.parse()\n return query.clauses\n}\n\n/**\n * Analyze the search query clauses in regard to the search terms found\n *\n * @param query - Search query clauses\n * @param terms - Search terms\n *\n * @return Search query terms\n */\nexport function getSearchQueryTerms(\n query: SearchQueryClause[], terms: string[]\n): SearchQueryTerms {\n const clauses = new Set(query)\n\n /* Match query clauses against terms */\n const result: SearchQueryTerms = {}\n for (let t = 0; t < terms.length; t++)\n for (const clause of clauses)\n if (terms[t].startsWith(clause.term)) {\n result[clause.term] = true\n clauses.delete(clause)\n }\n\n /* Annotate unmatched query clauses */\n for (const clause of clauses)\n result[clause.term] = false\n\n /* Return query terms */\n return result\n}\n","/*\n * Copyright (c) 2016-2020 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A RTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"lunr\"\n\nimport { Search, SearchIndexConfig } from \"../../_\"\nimport {\n SearchMessage,\n SearchMessageType\n} from \"../message\"\n\n/* ----------------------------------------------------------------------------\n * Types\n * ------------------------------------------------------------------------- */\n\n/**\n * Add support for usage with `iframe-worker` polyfill\n *\n * While `importScripts` is synchronous when executed inside of a web worker,\n * it's not possible to provide a synchronous polyfilled implementation. The\n * cool thing is that awaiting a non-Promise is a noop, so extending the type\n * definition to return a `Promise` shouldn't break anything.\n *\n * @see https://bit.ly/2PjDnXi - GitHub comment\n */\ndeclare global {\n function importScripts(...urls: string[]): Promise | void\n}\n\n/* ----------------------------------------------------------------------------\n * Data\n * ------------------------------------------------------------------------- */\n\n/**\n * Search index\n */\nlet index: Search\n\n/* ----------------------------------------------------------------------------\n * Helper functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch (= import) multi-language support through `lunr-languages`\n *\n * This function will automatically import the stemmers necessary to process\n * the languages which were given through the search index configuration.\n *\n * If the worker runs inside of an `iframe` (when using `iframe-worker` as\n * a shim), the base URL for the stemmers to be loaded must be determined by\n * searching for the first `script` element with a `src` attribute, which will\n * contain the contents of this script.\n *\n * @param config - Search index configuration\n *\n * @return Promise resolving with no result\n */\nasync function setupSearchLanguages(\n config: SearchIndexConfig\n): Promise {\n let base = \"../lunr\"\n\n /* Detect `iframe-worker` and fix base URL */\n if (typeof parent !== \"undefined\" && \"IFrameWorker\" in parent) {\n const worker = document.querySelector(\"script[src]\")!\n const [path] = worker.src.split(\"/worker\")\n\n /* Prefix base with path */\n base = base.replace(\"..\", path)\n }\n\n /* Add scripts for languages */\n const scripts = []\n for (const lang of config.lang) {\n if (lang === \"ja\") scripts.push(`${base}/tinyseg.min.js`)\n if (lang !== \"en\") scripts.push(`${base}/min/lunr.${lang}.min.js`)\n }\n\n /* Add multi-language support */\n if (config.lang.length > 1)\n scripts.push(`${base}/min/lunr.multi.min.js`)\n\n /* Load scripts synchronously */\n if (scripts.length)\n await importScripts(\n `${base}/min/lunr.stemmer.support.min.js`,\n ...scripts\n )\n}\n\n/* ----------------------------------------------------------------------------\n * Functions\n * ------------------------------------------------------------------------- */\n\n/**\n * Message handler\n *\n * @param message - Source message\n *\n * @return Target message\n */\nexport async function handler(\n message: SearchMessage\n): Promise {\n switch (message.type) {\n\n /* Search setup message */\n case SearchMessageType.SETUP:\n await setupSearchLanguages(message.data.config)\n index = new Search(message.data)\n return {\n type: SearchMessageType.READY\n }\n\n /* Search query message */\n case SearchMessageType.QUERY:\n return {\n type: SearchMessageType.RESULT,\n data: index ? index.search(message.data) : []\n }\n\n /* All other messages */\n default:\n throw new TypeError(\"Invalid message type\")\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Worker\n * ------------------------------------------------------------------------- */\n\naddEventListener(\"message\", async ev => {\n postMessage(await handler(ev.data))\n})\n"],"sourceRoot":""} \ No newline at end of file diff --git a/docs/v3/v2/assets/stylesheets/main.38780c08.min.css b/docs/v3/v2/assets/stylesheets/main.38780c08.min.css deleted file mode 100644 index 51d1aaf9d..000000000 --- a/docs/v3/v2/assets/stylesheets/main.38780c08.min.css +++ /dev/null @@ -1,3 +0,0 @@ -html{box-sizing:border-box;-webkit-text-size-adjust:none;-moz-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none}*,*::before,*::after{box-sizing:inherit}body{margin:0}hr{box-sizing:content-box;overflow:visible}a,button,label,input{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:separate;border-spacing:0}td,th{font-weight:normal;vertical-align:top}button{margin:0;padding:0;font-size:inherit;background:transparent;border:0}input{border:0;outline:none}:root{--md-default-fg-color: hsla(0, 0%, 0%, 0.87);--md-default-fg-color--light: hsla(0, 0%, 0%, 0.54);--md-default-fg-color--lighter: hsla(0, 0%, 0%, 0.32);--md-default-fg-color--lightest: hsla(0, 0%, 0%, 0.07);--md-default-bg-color: hsla(0, 0%, 100%, 1);--md-default-bg-color--light: hsla(0, 0%, 100%, 0.7);--md-default-bg-color--lighter: hsla(0, 0%, 100%, 0.3);--md-default-bg-color--lightest: hsla(0, 0%, 100%, 0.12);--md-primary-fg-color: hsla(231, 48%, 48%, 1);--md-primary-fg-color--light: hsla(230, 44%, 64%, 1);--md-primary-fg-color--dark: hsla(232, 54%, 41%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);--md-accent-fg-color: hsla(231, 99%, 66%, 1);--md-accent-fg-color--transparent: hsla(231, 99%, 66%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}:root>*{--md-code-fg-color: hsla(200, 18%, 26%, 1);--md-code-bg-color: hsla(0, 0%, 96%, 1);--md-code-hl-color: hsla(60, 100%, 50%, 0.5);--md-code-hl-number-color: hsla(0, 67%, 50%, 1);--md-code-hl-special-color: hsla(340, 83%, 47%, 1);--md-code-hl-function-color: hsla(291, 45%, 50%, 1);--md-code-hl-constant-color: hsla(250, 63%, 60%, 1);--md-code-hl-keyword-color: hsla(219, 54%, 51%, 1);--md-code-hl-string-color: hsla(150, 63%, 30%, 1);--md-code-hl-name-color: var(--md-code-fg-color);--md-code-hl-operator-color: var(--md-default-fg-color--light);--md-code-hl-punctuation-color: var(--md-default-fg-color--light);--md-code-hl-comment-color: var(--md-default-fg-color--light);--md-code-hl-generic-color: var(--md-default-fg-color--light);--md-code-hl-variable-color: var(--md-default-fg-color--light);--md-typeset-color: var(--md-default-fg-color);--md-typeset-a-color: var(--md-primary-fg-color);--md-typeset-mark-color: hsla(60, 100%, 50%, 0.5);--md-typeset-del-color: hsla(6, 90%, 60%, 0.15);--md-typeset-ins-color: hsla(150, 90%, 44%, 0.15);--md-typeset-kbd-color: hsla(0, 0%, 98%, 1);--md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);--md-typeset-kbd-border-color: hsla(0, 0%, 72%, 1);--md-admonition-fg-color: var(--md-default-fg-color);--md-admonition-bg-color: var(--md-default-bg-color);--md-footer-fg-color: hsla(0, 0%, 100%, 1);--md-footer-fg-color--light: hsla(0, 0%, 100%, 0.7);--md-footer-fg-color--lighter: hsla(0, 0%, 100%, 0.3);--md-footer-bg-color: hsla(0, 0%, 0%, 0.87);--md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.32)}.md-icon svg{display:block;width:1.2rem;height:1.2rem;margin:0 auto;fill:currentColor}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,input{color:var(--md-typeset-color);font-feature-settings:"kern","liga";font-family:-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif}code,pre,kbd{color:var(--md-typeset-color);font-feature-settings:"kern";font-family:SFMono-Regular,Consolas,Menlo,monospace}:root{--md-typeset-table--ascending: url('data:image/svg+xml;charset=utf-8,');--md-typeset-table--descending: url('data:image/svg+xml;charset=utf-8,')}.md-typeset{font-size:.8rem;line-height:1.6;-webkit-print-color-adjust:exact;color-adjust:exact}@media print{.md-typeset{font-size:.68rem}}.md-typeset p,.md-typeset ul,.md-typeset ol,.md-typeset blockquote{margin:1em 0}.md-typeset h1{margin:0 0 1.25em;color:var(--md-default-fg-color--light);font-weight:300;font-size:2em;line-height:1.3;letter-spacing:-0.01em}.md-typeset h2{margin:1.6em 0 .64em;font-weight:300;font-size:1.5625em;line-height:1.4;letter-spacing:-0.01em}.md-typeset h3{margin:1.6em 0 .8em;font-weight:400;font-size:1.25em;line-height:1.5;letter-spacing:-0.01em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{margin:1em 0;font-weight:700;letter-spacing:-0.01em}.md-typeset h5,.md-typeset h6{margin:1.25em 0;color:var(--md-default-fg-color--light);font-weight:700;font-size:.8em;letter-spacing:-0.01em}.md-typeset h5{text-transform:uppercase}.md-typeset hr{margin:1.5em 0;border-bottom:.05rem dotted var(--md-default-fg-color--lighter)}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a::before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset code,.md-typeset pre,.md-typeset kbd{color:var(--md-code-fg-color);direction:ltr}@media print{.md-typeset code,.md-typeset pre,.md-typeset kbd{white-space:pre-wrap}}.md-typeset code{padding:0 .2941176471em;font-size:.85em;word-break:break-word;background-color:var(--md-code-bg-color);border-radius:.1rem;-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset h1 code,.md-typeset h2 code,.md-typeset h3 code,.md-typeset h4 code,.md-typeset h5 code,.md-typeset h6 code{margin:initial;padding:initial;background-color:transparent;box-shadow:none}.md-typeset a>code{color:currentColor}.md-typeset pre{position:relative;margin:1em 0;line-height:1.4}.md-typeset pre>code{display:block;margin:0;padding:.7720588235em 1.1764705882em;overflow:auto;word-break:normal;box-shadow:none;-webkit-box-decoration-break:slice;box-decoration-break:slice;touch-action:auto;scrollbar-width:thin;scrollbar-color:var(--md-default-fg-color--lighter) transparent}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-typeset pre>code::-webkit-scrollbar{width:.2rem;height:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@media screen and (max-width: 44.9375em){.md-typeset>pre{margin:1em -0.8rem}.md-typeset>pre code{border-radius:0}}.md-typeset kbd{display:inline-block;padding:0 .6666666667em;color:var(--md-default-fg-color);font-size:.75em;vertical-align:text-top;word-break:break-word;background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -0.1rem .2rem var(--md-typeset-kbd-accent-color) inset}.md-typeset mark{color:inherit;word-break:break-word;background-color:var(--md-typeset-mark-color);-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset abbr{text-decoration:none;border-bottom:.05rem dotted var(--md-default-fg-color--light);cursor:help}@media(hover: none){.md-typeset abbr{position:relative}.md-typeset abbr[title]:focus::after,.md-typeset abbr[title]:hover::after{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);position:absolute;left:0;display:inline-block;width:auto;min-width:-webkit-max-content;min-width:-moz-max-content;min-width:max-content;max-width:80%;margin-top:2em;padding:.2rem .3rem;color:var(--md-default-bg-color);font-size:.7rem;background:var(--md-default-fg-color);border-radius:.1rem;content:attr(title)}}.md-typeset small{opacity:.75}.md-typeset sup,.md-typeset sub{margin-left:.078125em}[dir=rtl] .md-typeset sup,[dir=rtl] .md-typeset sub{margin-right:.078125em;margin-left:initial}.md-typeset blockquote{padding-left:.6rem;color:var(--md-default-fg-color--light);border-left:.2rem solid var(--md-default-fg-color--lighter)}[dir=rtl] .md-typeset blockquote{padding-right:.6rem;padding-left:initial;border-right:.2rem solid var(--md-default-fg-color--lighter);border-left:initial}.md-typeset ul{list-style-type:disc}.md-typeset ul,.md-typeset ol{margin-left:.625em;padding:0}[dir=rtl] .md-typeset ul,[dir=rtl] .md-typeset ol{margin-right:.625em;margin-left:initial}.md-typeset ul ol,.md-typeset ol ol{list-style-type:lower-alpha}.md-typeset ul ol ol,.md-typeset ol ol ol{list-style-type:lower-roman}.md-typeset ul li,.md-typeset ol li{margin-bottom:.5em;margin-left:1.25em}[dir=rtl] .md-typeset ul li,[dir=rtl] .md-typeset ol li{margin-right:1.25em;margin-left:initial}.md-typeset ul li p,.md-typeset ul li blockquote,.md-typeset ol li p,.md-typeset ol li blockquote{margin:.5em 0}.md-typeset ul li:last-child,.md-typeset ol li:last-child{margin-bottom:0}.md-typeset ul li ul,.md-typeset ul li ol,.md-typeset ol li ul,.md-typeset ol li ol{margin:.5em 0 .5em .625em}[dir=rtl] .md-typeset ul li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ol li ol{margin-right:.625em;margin-left:initial}.md-typeset dd{margin:1em 0 1.5em 1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em;margin-left:initial}.md-typeset img,.md-typeset svg{max-width:100%;height:auto}.md-typeset img[align=left],.md-typeset svg[align=left]{margin:1em;margin-left:0}.md-typeset img[align=right],.md-typeset svg[align=right]{margin:1em;margin-right:0}.md-typeset img[align]:only-child,.md-typeset svg[align]:only-child{margin-top:0}.md-typeset figure{width:-webkit-fit-content;width:-moz-fit-content;width:fit-content;max-width:100%;margin:0 auto;text-align:center}.md-typeset figcaption{max-width:24rem;margin:.5em auto 2em;font-style:italic}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){display:inline-block;max-width:100%;overflow:auto;font-size:.64rem;background:var(--md-default-bg-color);border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .05rem rgba(0,0,0,.1);touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) th>*:first-child,.md-typeset table:not([class]) td>*:first-child{margin-top:0}.md-typeset table:not([class]) th>*:last-child,.md-typeset table:not([class]) td>*:last-child{margin-bottom:0}.md-typeset table:not([class]) th:not([align]),.md-typeset table:not([class]) td:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) th:not([align]),[dir=rtl] .md-typeset table:not([class]) td:not([align]){text-align:right}.md-typeset table:not([class]) th{min-width:5rem;padding:.9375em 1.25em;color:var(--md-default-bg-color);vertical-align:top;background-color:var(--md-default-fg-color--light)}.md-typeset table:not([class]) th a{color:inherit}.md-typeset table:not([class]) td{padding:.9375em 1.25em;vertical-align:top;border-top:.05rem solid var(--md-default-fg-color--lightest)}.md-typeset table:not([class]) tr{transition:background-color 125ms}.md-typeset table:not([class]) tr:hover{background-color:rgba(0,0,0,.035);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) tr:first-child td{border-top:0}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}.md-typeset table th[role=columnheader]::after{display:inline-block;width:1.2em;height:1.2em;margin-left:.5em;vertical-align:sub;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;content:" "}.md-typeset table th[role=columnheader][aria-sort=ascending]::after{background-color:currentColor;-webkit-mask-image:var(--md-typeset-table--ascending);mask-image:var(--md-typeset-table--ascending)}.md-typeset table th[role=columnheader][aria-sort=descending]::after{background-color:currentColor;-webkit-mask-image:var(--md-typeset-table--descending);mask-image:var(--md-typeset-table--descending)}.md-typeset__scrollwrap{margin:1em -0.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;width:100%;margin:0;overflow:hidden}html{height:100%;overflow-x:hidden;font-size:125%}@media screen and (min-width: 100em){html{font-size:137.5%}}@media screen and (min-width: 125em){html{font-size:150%}}body{position:relative;display:flex;flex-direction:column;width:100%;min-height:100%;font-size:.5rem;background-color:var(--md-default-bg-color)}@media screen and (max-width: 59.9375em){body[data-md-state=lock]{position:fixed}}@media print{body{display:block}}hr{display:block;height:.05rem;padding:0;border:0}.md-grid{max-width:61rem;margin-right:auto;margin-left:auto}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.md-toggle{display:none}.md-overlay{position:fixed;top:0;z-index:3;width:0;height:0;background-color:rgba(0,0,0,.54);opacity:0;transition:width 0ms 250ms,height 0ms 250ms,opacity 250ms}@media screen and (max-width: 76.1875em){[data-md-toggle=drawer]:checked~.md-overlay{width:100%;height:100%;opacity:1;transition:width 0ms,height 0ms,opacity 250ms}}.md-skip{position:fixed;z-index:-1;margin:.5rem;padding:.3rem .5rem;color:var(--md-default-bg-color);font-size:.64rem;background-color:var(--md-default-fg-color);border-radius:.1rem;transform:translateY(0.4rem);opacity:0}.md-skip:focus{z-index:10;transform:translateY(0);opacity:1;transition:transform 250ms cubic-bezier(0.4, 0, 0.2, 1),opacity 175ms 75ms}@page{margin:25mm}.md-announce{overflow:auto;background-color:var(--md-footer-bg-color)}.md-announce__inner{margin:.6rem auto;padding:0 .8rem;color:var(--md-footer-fg-color);font-size:.7rem}@media print{.md-announce{display:none}}.md-typeset .md-button{display:inline-block;padding:.625em 2em;color:var(--md-primary-fg-color);font-weight:700;border:.1rem solid currentColor;border-radius:.1rem;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{color:var(--md-accent-bg-color);background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color)}:root{--md-clipboard-icon: url('data:image/svg+xml;charset=utf-8,')}.md-clipboard{position:absolute;top:.5em;right:.5em;z-index:1;width:1.5em;height:1.5em;color:var(--md-default-fg-color--lightest);border-radius:.1rem;cursor:pointer;transition:color 125ms}@media print{.md-clipboard{display:none}}.md-clipboard::after{display:block;width:1.125em;height:1.125em;margin:0 auto;background-color:currentColor;-webkit-mask-image:var(--md-clipboard-icon);mask-image:var(--md-clipboard-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;content:""}pre:hover .md-clipboard{color:var(--md-default-fg-color--light)}pre .md-clipboard:focus,pre .md-clipboard:hover{color:var(--md-accent-fg-color)}.md-content{flex:1;max-width:100%}@media screen and (min-width: 60em)and (max-width: 76.1875em){.md-content{max-width:calc(100% - 12.1rem)}}@media screen and (min-width: 76.25em){.md-content{max-width:calc(100% - 12.1rem * 2)}}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width: 76.25em){.md-content__inner{margin-right:1.2rem;margin-left:1.2rem}}.md-content__inner::before{display:block;height:.4rem;content:""}.md-content__inner>:last-child{margin-bottom:0}.md-content__button{float:right;margin:.4rem 0;margin-left:.4rem;padding:0}[dir=rtl] .md-content__button{float:left;margin-right:.4rem;margin-left:initial}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}@media print{.md-content__button{display:none}}.md-dialog{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);position:fixed;right:.8rem;bottom:.8rem;left:initial;z-index:2;display:block;min-width:11.1rem;padding:.4rem .6rem;color:var(--md-default-bg-color);font-size:.7rem;background:var(--md-default-fg-color);border:none;border-radius:.1rem;transform:translateY(100%);opacity:0;transition:transform 0ms 400ms,opacity 400ms}[dir=rtl] .md-dialog{right:initial;left:.8rem}.md-dialog[data-md-state=open]{transform:translateY(0);opacity:1;transition:transform 400ms cubic-bezier(0.075, 0.85, 0.175, 1),opacity 400ms}@media print{.md-dialog{display:none}}.md-header{position:-webkit-sticky;position:sticky;top:0;right:0;left:0;z-index:2;height:2.4rem;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem rgba(0,0,0,0),0 .2rem .4rem rgba(0,0,0,0);transition:color 250ms,background-color 250ms}.no-js .md-header{box-shadow:none;transition:none}.md-header[data-md-state=shadow]{box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2);transition:color 250ms,background-color 250ms,box-shadow 250ms}@media print{.md-header{display:none}}.md-header-nav{display:flex;padding:0 .2rem}.md-header-nav__button{position:relative;z-index:1;display:block;margin:.2rem;padding:.4rem;cursor:pointer;transition:opacity 250ms}[dir=rtl] .md-header-nav__button svg{transform:scaleX(-1)}.md-header-nav__button:focus,.md-header-nav__button:hover{opacity:.7}.md-header-nav__button.md-logo{margin:.2rem;padding:.4rem}.md-header-nav__button.md-logo img,.md-header-nav__button.md-logo svg{display:block;width:1.2rem;height:1.2rem;fill:currentColor}.no-js .md-header-nav__button[for=__search]{display:none}@media screen and (min-width: 60em){.md-header-nav__button[for=__search]{display:none}}@media screen and (max-width: 76.1875em){.md-header-nav__button.md-logo{display:none}}@media screen and (min-width: 76.25em){.md-header-nav__button[for=__drawer]{display:none}}.md-header-nav__topic{position:absolute;width:100%;transition:transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),opacity 150ms}.md-header-nav__topic+.md-header-nav__topic{z-index:-1;transform:translateX(1.25rem);opacity:0;transition:transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),opacity 150ms;pointer-events:none}[dir=rtl] .md-header-nav__topic+.md-header-nav__topic{transform:translateX(-1.25rem)}.no-js .md-header-nav__topic{position:initial}.no-js .md-header-nav__topic+.md-header-nav__topic{display:none}.md-header-nav__title{flex-grow:1;padding:0 1rem;font-size:.9rem;line-height:2.4rem}.md-header-nav__title[data-md-state=active] .md-header-nav__topic{z-index:-1;transform:translateX(-1.25rem);opacity:0;transition:transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),opacity 150ms;pointer-events:none}[dir=rtl] .md-header-nav__title[data-md-state=active] .md-header-nav__topic{transform:translateX(1.25rem)}.md-header-nav__title[data-md-state=active] .md-header-nav__topic+.md-header-nav__topic{z-index:0;transform:translateX(0);opacity:1;transition:transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),opacity 150ms;pointer-events:initial}.md-header-nav__title>.md-header-nav__ellipsis{position:relative;width:100%;height:100%}.md-header-nav__source{display:none}@media screen and (min-width: 60em){.md-header-nav__source{display:block;width:11.7rem;max-width:11.7rem;margin-left:1rem}[dir=rtl] .md-header-nav__source{margin-right:1rem;margin-left:initial}}@media screen and (min-width: 76.25em){.md-header-nav__source{margin-left:1.4rem}[dir=rtl] .md-header-nav__source{margin-right:1.4rem}}.md-footer{color:var(--md-footer-fg-color);background-color:var(--md-footer-bg-color)}@media print{.md-footer{display:none}}.md-footer-nav__inner{padding:.2rem;overflow:auto}.md-footer-nav__link{display:flex;padding-top:1.4rem;padding-bottom:.4rem;transition:opacity 250ms}@media screen and (min-width: 45em){.md-footer-nav__link{width:50%}}.md-footer-nav__link:focus,.md-footer-nav__link:hover{opacity:.7}.md-footer-nav__link--prev{float:left}[dir=rtl] .md-footer-nav__link--prev{float:right}[dir=rtl] .md-footer-nav__link--prev svg{transform:scaleX(-1)}@media screen and (max-width: 44.9375em){.md-footer-nav__link--prev{width:25%}.md-footer-nav__link--prev .md-footer-nav__title{display:none}}.md-footer-nav__link--next{float:right;text-align:right}[dir=rtl] .md-footer-nav__link--next{float:left;text-align:left}[dir=rtl] .md-footer-nav__link--next svg{transform:scaleX(-1)}@media screen and (max-width: 44.9375em){.md-footer-nav__link--next{width:75%}}.md-footer-nav__title{position:relative;flex-grow:1;max-width:calc(100% - 2.4rem);padding:0 1rem;font-size:.9rem;line-height:2.4rem}.md-footer-nav__button{margin:.2rem;padding:.4rem}.md-footer-nav__direction{position:absolute;right:0;left:0;margin-top:-1rem;padding:0 1rem;font-size:.64rem;opacity:.7}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-footer-copyright{width:100%;margin:auto .6rem;padding:.4rem 0;color:var(--md-footer-fg-color--lighter);font-size:.64rem}@media screen and (min-width: 45em){.md-footer-copyright{width:auto}}.md-footer-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-footer-social{margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width: 45em){.md-footer-social{padding:.6rem 0}}.md-footer-social__link{display:inline-block;width:1.6rem;height:1.6rem;text-align:center}.md-footer-social__link::before{line-height:1.9}.md-footer-social__link svg{max-height:.8rem;vertical-align:-25%;fill:currentColor}:root{--md-nav-icon--prev: url('data:image/svg+xml;charset=utf-8,');--md-nav-icon--next: url('data:image/svg+xml;charset=utf-8,');--md-toc-icon: url('data:image/svg+xml;charset=utf-8,')}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{display:block;padding:0 .6rem;overflow:hidden;font-weight:700;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{width:100%;height:auto}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{display:block;width:2.4rem;height:2.4rem}.md-nav__title .md-nav__button.md-logo svg{fill:currentColor}.md-nav__list{margin:0;padding:0;list-style:none}.md-nav__item{padding:0 .6rem}.md-nav__item:last-child{padding-bottom:.6rem}.md-nav__item .md-nav__item{padding-right:0}[dir=rtl] .md-nav__item .md-nav__item{padding-right:.6rem;padding-left:0}.md-nav__item .md-nav__item:last-child{padding-bottom:0}.md-nav__link{display:block;margin-top:.625em;overflow:hidden;text-overflow:ellipsis;cursor:pointer;transition:color 125ms;scroll-snap-align:start}html .md-nav__link[for=__toc]{display:none}html .md-nav__link[for=__toc]~.md-nav{display:none}.md-nav__link[data-md-state=blur]{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active{color:var(--md-typeset-a-color)}.md-nav__item--nested>.md-nav__link{color:inherit}.md-nav__link:focus,.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav__source{display:none}@media screen and (max-width: 76.1875em){.md-nav{background-color:var(--md-default-bg-color)}.md-nav--primary,.md-nav--primary .md-nav{position:absolute;top:0;right:0;left:0;z-index:1;display:flex;flex-direction:column;height:100%}.md-nav--primary .md-nav__title,.md-nav--primary .md-nav__item{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{position:relative;height:5.6rem;padding:3rem .8rem .2rem;color:var(--md-default-fg-color--light);font-weight:400;line-height:2.4rem;white-space:nowrap;background-color:var(--md-default-fg-color--lightest);cursor:pointer}.md-nav--primary .md-nav__title .md-nav__icon{position:absolute;top:.4rem;left:.4rem;display:block;width:1.2rem;height:1.2rem;margin:.2rem}.md-nav--primary .md-nav__title .md-nav__icon::after{display:block;width:100%;height:100%;background-color:currentColor;-webkit-mask-image:var(--md-nav-icon--prev);mask-image:var(--md-nav-icon--prev);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;content:""}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{right:.4rem;left:initial}.md-nav--primary .md-nav__title~.md-nav__list{overflow-y:auto;background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;-webkit-scroll-snap-type:y mandatory;-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>.md-nav__item:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{position:relative;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color)}.md-nav--primary .md-nav__title[for=__drawer] .md-nav__button{position:absolute;top:.2rem;left:.2rem;display:block;margin:.2rem;padding:.4rem;font-size:2.4rem}html [dir=rtl] .md-nav--primary .md-nav__title[for=__drawer] .md-nav__button{right:.2rem;left:initial}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{padding:0;border-top:.05rem solid var(--md-default-fg-color--lightest)}[dir=rtl] .md-nav--primary .md-nav__item{padding:0}.md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:2.4rem}[dir=rtl] .md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:.8rem;padding-left:2.4rem}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{position:relative;margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link .md-nav__icon{position:absolute;top:50%;right:.6rem;width:1.2rem;height:1.2rem;margin-top:-0.6rem;color:inherit;font-size:1.2rem}.md-nav--primary .md-nav__link .md-nav__icon::after{display:block;width:100%;height:100%;background-color:currentColor;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;content:""}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{right:initial;left:.6rem}[dir=rtl] .md-nav--primary .md-nav__icon::after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav__link{position:static}.md-nav--primary .md-nav--secondary .md-nav{position:static;background-color:transparent}.md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:1.4rem;padding-left:initial}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:2rem;padding-left:initial}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:2.6rem;padding-left:initial}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:3.2rem;padding-left:initial}.md-nav__toggle~.md-nav{display:flex;transform:translateX(100%);opacity:0;transition:transform 250ms cubic-bezier(0.8, 0, 0.6, 1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{transform:translateX(0);opacity:1;transition:transform 250ms cubic-bezier(0.4, 0, 0.2, 1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{-webkit-backface-visibility:hidden;backface-visibility:hidden}}@media screen and (max-width: 59.9375em){html .md-nav__link[for=__toc]{display:block;padding-right:2.4rem}html .md-nav__link[for=__toc]+.md-nav__link{display:none}html .md-nav__link[for=__toc] .md-icon::after{display:block;width:100%;height:100%;-webkit-mask-image:var(--md-toc-icon);mask-image:var(--md-toc-icon);background-color:currentColor;content:""}html .md-nav__link[for=__toc]~.md-nav{display:flex}html [dir=rtl] .md-nav__link{padding-right:.8rem;padding-left:2.4rem}.md-nav__source{display:block;padding:0 .2rem;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color--dark)}}@media screen and (min-width: 60em){.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}}@media screen and (min-width: 76.25em){.md-nav{transition:max-height 250ms cubic-bezier(0.86, 0, 0.07, 1)}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon{display:none}.md-nav__toggle~.md-nav{display:none}.md-nav__toggle:checked~.md-nav{display:block}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__icon{float:right;width:.9rem;height:.9rem;transition:transform 250ms}[dir=rtl] .md-nav__icon{float:left;transform:rotate(180deg)}.md-nav__icon::after{display:inline-block;width:100%;height:100%;vertical-align:-0.1rem;background-color:currentColor;-webkit-mask-image:var(--md-nav-icon--next);mask-image:var(--md-nav-icon--next);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;content:""}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon{transform:rotate(90deg)}}:root{--md-search-result-icon: url('data:image/svg+xml;charset=utf-8,')}.md-search{position:relative}.no-js .md-search{display:none}@media screen and (min-width: 60em){.md-search{padding:.2rem 0}}.md-search__overlay{z-index:1;opacity:0}@media screen and (max-width: 59.9375em){.md-search__overlay{position:absolute;top:.2rem;left:-2.2rem;width:2rem;height:2rem;overflow:hidden;background-color:var(--md-default-bg-color);border-radius:1rem;transform-origin:center;transition:transform 300ms 100ms,opacity 200ms 200ms;pointer-events:none}[dir=rtl] .md-search__overlay{right:-2.2rem;left:initial}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform 400ms,opacity 100ms}}@media screen and (max-width: 29.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width: 30em)and (max-width: 44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width: 45em)and (max-width: 59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}@media screen and (min-width: 60em){.md-search__overlay{position:fixed;top:0;left:0;width:0;height:0;background-color:rgba(0,0,0,.54);cursor:pointer;transition:width 0ms 250ms,height 0ms 250ms,opacity 250ms}[dir=rtl] .md-search__overlay{right:0;left:initial}[data-md-toggle=search]:checked~.md-header .md-search__overlay{width:100%;height:100%;opacity:1;transition:width 0ms,height 0ms,opacity 250ms}}.md-search__inner{-webkit-backface-visibility:hidden;backface-visibility:hidden}@media screen and (max-width: 59.9375em){.md-search__inner{position:fixed;top:0;left:100%;z-index:2;width:100%;height:100%;transform:translateX(5%);opacity:0;transition:right 0ms 300ms,left 0ms 300ms,transform 150ms 150ms cubic-bezier(0.4, 0, 0.2, 1),opacity 150ms 150ms}[data-md-toggle=search]:checked~.md-header .md-search__inner{left:0;transform:translateX(0);opacity:1;transition:right 0ms 0ms,left 0ms 0ms,transform 150ms 150ms cubic-bezier(0.1, 0.7, 0.1, 1),opacity 150ms 150ms}[dir=rtl] [data-md-toggle=search]:checked~.md-header .md-search__inner{right:0;left:initial}html [dir=rtl] .md-search__inner{right:100%;left:initial;transform:translateX(-5%)}}@media screen and (min-width: 60em){.md-search__inner{position:relative;float:right;width:11.7rem;padding:.1rem 0;transition:width 250ms cubic-bezier(0.1, 0.7, 0.1, 1)}[dir=rtl] .md-search__inner{float:left}}@media screen and (min-width: 60em)and (max-width: 76.1875em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width: 76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{position:relative}@media screen and (min-width: 60em){.md-search__form{border-radius:.1rem}}.md-search__input{position:relative;z-index:2;padding:0 2.2rem 0 3.6rem;text-overflow:ellipsis;background-color:var(--md-default-bg-color);transition:color 250ms,background-color 250ms}[dir=rtl] .md-search__input{padding:0 3.6rem 0 2.2rem}.md-search__input::-webkit-input-placeholder{-webkit-transition:color 250ms;transition:color 250ms}.md-search__input::-moz-placeholder{-moz-transition:color 250ms;transition:color 250ms}.md-search__input::-ms-input-placeholder{-ms-transition:color 250ms;transition:color 250ms}.md-search__input::placeholder{transition:color 250ms}.md-search__input::-webkit-input-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::-moz-placeholder{color:var(--md-default-fg-color--light)}.md-search__input::-ms-input-placeholder{color:var(--md-default-fg-color--light)}.md-search__input~.md-search__icon,.md-search__input::placeholder{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}@media screen and (max-width: 59.9375em){.md-search__input{width:100%;height:2.4rem;font-size:.9rem}}@media screen and (min-width: 60em){.md-search__input{width:100%;height:1.8rem;padding-left:2.2rem;color:inherit;font-size:.8rem;background-color:rgba(0,0,0,.26);border-radius:.1rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}.md-search__input::-webkit-input-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::-moz-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::-ms-input-placeholder{color:var(--md-primary-bg-color--light)}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input:hover{background-color:rgba(255,255,255,.12)}[data-md-toggle=search]:checked~.md-header .md-search__input{color:var(--md-default-fg-color);text-overflow:clip;background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0}[data-md-toggle=search]:checked~.md-header .md-search__input::-webkit-input-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::-moz-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input::-ms-input-placeholder{color:var(--md-default-fg-color--light)}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:var(--md-default-fg-color--light)}}.md-search__icon{position:absolute;z-index:2;width:1.2rem;height:1.2rem;cursor:pointer;transition:color 250ms,opacity 250ms}.md-search__icon:hover{opacity:.7}.md-search__icon[for=__search]{top:.3rem;left:.5rem}[dir=rtl] .md-search__icon[for=__search]{right:.5rem;left:initial}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width: 59.9375em){.md-search__icon[for=__search]{top:.6rem;left:.8rem}[dir=rtl] .md-search__icon[for=__search]{right:.8rem;left:initial}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width: 60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}.md-search__icon[type=reset]{top:.3rem;right:.5rem;transform:scale(0.75);opacity:0;transition:transform 150ms cubic-bezier(0.1, 0.7, 0.1, 1),opacity 150ms;pointer-events:none}[dir=rtl] .md-search__icon[type=reset]{right:initial;left:.5rem}@media screen and (max-width: 59.9375em){.md-search__icon[type=reset]{top:.6rem;right:.8rem}[dir=rtl] .md-search__icon[type=reset]{right:initial;left:.8rem}}[data-md-toggle=search]:checked~.md-header .md-search__input:not(:-moz-placeholder-shown)~.md-search__icon[type=reset]{transform:scale(1);opacity:1;pointer-events:initial}[data-md-toggle=search]:checked~.md-header .md-search__input:not(:placeholder-shown)~.md-search__icon[type=reset]{transform:scale(1);opacity:1;pointer-events:initial}[data-md-toggle=search]:checked~.md-header .md-search__input:not(:-moz-placeholder-shown)~.md-search__icon[type=reset]:hover{opacity:.7}[data-md-toggle=search]:checked~.md-header .md-search__input:not(:placeholder-shown)~.md-search__icon[type=reset]:hover{opacity:.7}.md-search__output{position:absolute;z-index:1;width:100%;overflow:hidden;border-radius:0 0 .1rem .1rem}@media screen and (max-width: 59.9375em){.md-search__output{top:2.4rem;bottom:0}}@media screen and (min-width: 60em){.md-search__output{top:1.9rem;opacity:0;transition:opacity 400ms}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.4);opacity:1}}.md-search__scrollwrap{height:100%;overflow-y:auto;background-color:var(--md-default-bg-color);-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-scroll-snap-type:y mandatory;-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory;touch-action:pan-y}@media(-webkit-max-device-pixel-ratio: 1), (max-resolution: 1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width: 60em)and (max-width: 76.1875em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width: 76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width: 60em){.md-search__scrollwrap{max-height:0;scrollbar-width:thin;scrollbar-color:var(--md-default-fg-color--lighter) transparent}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-search__scrollwrap::-webkit-scrollbar{width:.2rem;height:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{padding:0 .8rem;color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;background-color:var(--md-default-fg-color--lightest);scroll-snap-align:start}@media screen and (min-width: 60em){.md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-right:2.2rem;padding-left:initial}}.md-search-result__list{margin:0;padding:0;list-style:none}.md-search-result__item{box-shadow:0 -0.05rem 0 var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;transition:background 250ms;scroll-snap-align:start}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:focus .md-search-result__article::before,.md-search-result__link:hover .md-search-result__article::before{opacity:.7}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more summary{display:block;padding:.75em .8rem;color:var(--md-typeset-a-color);font-size:.64rem;outline:0;cursor:pointer;transition:color 250ms,background-color 250ms;scroll-snap-align:start}.md-search-result__more summary:focus,.md-search-result__more summary:hover{color:var(--md-accent-fg-color);background-color:var(--md-accent-fg-color--transparent)}@media screen and (min-width: 60em){.md-search-result__more summary{padding-left:2.2rem}[dir=rtl] .md-search-result__more summary{padding-right:2.2rem;padding-left:.8rem}}.md-search-result__more summary::-webkit-details-marker{display:none}.md-search-result__more summary~*>*{opacity:.65}.md-search-result__article{position:relative;padding:0 .8rem;overflow:hidden}@media screen and (min-width: 60em){.md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-right:2.2rem;padding-left:.8rem}}.md-search-result__article--document .md-search-result__title{margin:.55rem 0;font-weight:400;font-size:.8rem;line-height:1.4}.md-search-result__icon{position:absolute;left:0;width:1.2rem;height:1.2rem;margin:.5rem;color:var(--md-default-fg-color--light)}.md-search-result__icon::after{display:inline-block;width:100%;height:100%;background-color:currentColor;-webkit-mask-image:var(--md-search-result-icon);mask-image:var(--md-search-result-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;content:""}[dir=rtl] .md-search-result__icon{right:0;left:initial}[dir=rtl] .md-search-result__icon::after{transform:scaleX(-1)}@media screen and (max-width: 59.9375em){.md-search-result__icon{display:none}}.md-search-result__title{margin:.5em 0;font-weight:700;font-size:.64rem;line-height:1.6}.md-search-result__teaser{display:-webkit-box;max-height:2rem;margin:.5em 0;overflow:hidden;color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.6;text-overflow:ellipsis;-webkit-box-orient:vertical;-webkit-line-clamp:2}@media screen and (max-width: 44.9375em){.md-search-result__teaser{max-height:3rem;-webkit-line-clamp:3}}@media screen and (min-width: 60em)and (max-width: 76.1875em){.md-search-result__teaser{max-height:3rem;-webkit-line-clamp:3}}.md-search-result__teaser mark{background-color:transparent;border-bottom:.05rem solid var(--md-accent-fg-color)}.md-search-result__terms{margin:.5em 0;font-size:.64rem;font-style:italic}.md-search-result mark{color:var(--md-accent-fg-color);background-color:transparent}@-webkit-keyframes md-sidebar__scrollwrap--hack{0%,99%{-webkit-scroll-snap-type:none;scroll-snap-type:none}100%{-webkit-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory}}@keyframes md-sidebar__scrollwrap--hack{0%,99%{-webkit-scroll-snap-type:none;-ms-scroll-snap-type:none;scroll-snap-type:none}100%{-webkit-scroll-snap-type:y mandatory;-ms-scroll-snap-type:y mandatory;scroll-snap-type:y mandatory}}.md-sidebar{position:-webkit-sticky;position:sticky;top:2.4rem;align-self:flex-start;width:12.1rem;padding:1.2rem 0;overflow:hidden}@media print{.md-sidebar{display:none}}@media screen and (max-width: 76.1875em){.md-sidebar--primary{position:fixed;top:0;left:-12.1rem;z-index:3;width:12.1rem;height:100%;background-color:var(--md-default-bg-color);transform:translateX(0);transition:transform 250ms cubic-bezier(0.4, 0, 0.2, 1),box-shadow 250ms}[dir=rtl] .md-sidebar--primary{right:-12.1rem;left:initial}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.4);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{overflow:hidden}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width: 60em){.md-sidebar--secondary{display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{max-height:100%;margin:0 .2rem;overflow-y:auto;-webkit-backface-visibility:hidden;backface-visibility:hidden;scrollbar-width:thin;scrollbar-color:var(--md-default-fg-color--lighter) transparent}.js .md-sidebar__scrollwrap{-webkit-animation:md-sidebar__scrollwrap--hack 400ms forwards;animation:md-sidebar__scrollwrap--hack 400ms forwards}@media screen and (max-width: 76.1875em){.md-sidebar--primary .md-sidebar__scrollwrap{position:absolute;top:0;right:0;bottom:0;left:0;margin:0;-webkit-scroll-snap-type:none;-ms-scroll-snap-type:none;scroll-snap-type:none}}.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-sidebar__scrollwrap::-webkit-scrollbar{width:.2rem;height:.2rem}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@-webkit-keyframes md-source__facts--done{0%{height:0}100%{height:.65rem}}@keyframes md-source__facts--done{0%{height:0}100%{height:.65rem}}@-webkit-keyframes md-source__fact--done{0%{transform:translateY(100%);opacity:0}50%{opacity:0}100%{transform:translateY(0%);opacity:1}}@keyframes md-source__fact--done{0%{transform:translateY(100%);opacity:0}50%{opacity:0}100%{transform:translateY(0%);opacity:1}}.md-source{display:block;font-size:.65rem;line-height:1.2;white-space:nowrap;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:opacity 250ms}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;width:2.4rem;height:2.4rem;vertical-align:middle}.md-source__icon svg{margin-top:.6rem;margin-left:.6rem}[dir=rtl] .md-source__icon svg{margin-right:.6rem;margin-left:initial}.md-source__icon+.md-source__repository{margin-left:-2rem;padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-2rem;margin-left:initial;padding-right:2rem;padding-left:initial}.md-source__repository{display:inline-block;max-width:calc(100% - 1.2rem);margin-left:.6rem;overflow:hidden;font-weight:700;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{margin:0;padding:0;overflow:hidden;font-weight:700;font-size:.55rem;list-style-type:none;opacity:.75}[data-md-state=done] .md-source__facts{-webkit-animation:md-source__facts--done 250ms ease-in;animation:md-source__facts--done 250ms ease-in}.md-source__fact{float:left}[dir=rtl] .md-source__fact{float:right}[data-md-state=done] .md-source__fact{-webkit-animation:md-source__fact--done 400ms ease-out;animation:md-source__fact--done 400ms ease-out}.md-source__fact::before{margin:0 .1rem;content:"·"}.md-source__fact:first-child::before{display:none}.md-tabs{width:100%;overflow:auto;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color);transition:background 250ms}.no-js .md-tabs{transition:none}@media screen and (max-width: 76.1875em){.md-tabs{display:none}}@media print{.md-tabs{display:none}}.md-tabs__list{margin:0;margin-left:.2rem;padding:0;white-space:nowrap;list-style:none;contain:content}[dir=rtl] .md-tabs__list{margin-right:.2rem;margin-left:initial}.md-tabs__item{display:inline-block;height:2.4rem;padding-right:.6rem;padding-left:.6rem}.md-tabs__link{display:block;margin-top:.8rem;font-size:.7rem;opacity:.7;transition:transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),opacity 250ms}.no-js .md-tabs__link{transition:none}.md-tabs__link--active,.md-tabs__link:hover{color:inherit;opacity:1}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:100ms}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:120ms}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:140ms}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:160ms}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:180ms}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:200ms}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:220ms}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:240ms}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:260ms}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:280ms}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:300ms}.md-tabs[data-md-state=hidden]{pointer-events:none}.md-tabs[data-md-state=hidden] .md-tabs__link{transform:translateY(50%);opacity:0;transition:color 250ms,transform 0ms 400ms,opacity 100ms}@media screen and (min-width: 76.25em){.md-tabs~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{display:none}.md-tabs--active~.md-main .md-nav--primary .md-nav__title{display:block;padding:0 .6rem;pointer-events:none;scroll-snap-align:start}.md-tabs--active~.md-main .md-nav--primary .md-nav__title[for=__drawer]{display:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item{display:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--active{display:block;padding:0}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--active>.md-nav__link{display:none}.md-tabs--active~.md-main .md-nav[data-md-level="1"]{display:block}.md-tabs--active~.md-main .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item{padding:0 .6rem}.md-tabs--active~.md-main .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item:last-child{padding-bottom:.6rem}.md-tabs--active~.md-main .md-nav[data-md-level="1"]>.md-nav__list>.md-nav__item:last-child .md-nav__item{padding-bottom:0}.md-tabs--active~.md-main .md-nav[data-md-level="1"] .md-nav .md-nav__title{display:none}}:root{--md-admonition-icon--note: url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--abstract: url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--info: url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--tip: url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--success: url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--question: url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--warning: url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--failure: url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--danger: url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--bug: url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--example: url('data:image/svg+xml;charset=utf-8,');--md-admonition-icon--quote: url('data:image/svg+xml;charset=utf-8,')}.md-typeset .admonition,.md-typeset details{margin:1.5625em 0;padding:0 .6rem;overflow:hidden;color:var(--md-admonition-fg-color);font-size:.64rem;page-break-inside:avoid;background-color:var(--md-admonition-bg-color);border-left:.2rem solid #448aff;border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .05rem rgba(0,0,0,.1)}[dir=rtl] .md-typeset .admonition,[dir=rtl] .md-typeset details{border-right:.2rem solid #448aff;border-left:none}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}.md-typeset .admonition .admonition,.md-typeset details .admonition,.md-typeset .admonition details,.md-typeset details details{margin:1em 0}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -0.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}.md-typeset .admonition-title,.md-typeset summary{position:relative;margin:0 -0.6rem 0 -0.8rem;padding:.4rem .6rem .4rem 2.2rem;font-weight:700;background-color:rgba(68,138,255,.1)}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{margin:0 -0.8rem 0 -0.6rem;padding:.4rem 2rem .4rem .6rem}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}.md-typeset .admonition-title::before,.md-typeset summary::before{position:absolute;left:.8rem;width:1rem;height:1rem;background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;content:""}[dir=rtl] .md-typeset .admonition-title::before,[dir=rtl] .md-typeset summary::before{right:.8rem;left:initial}.md-typeset .admonition-title code,.md-typeset summary code{margin:initial;padding:initial;color:currentColor;background-color:transparent;border-radius:initial;box-shadow:none}.md-typeset .admonition-title+.tabbed-set:last-child,.md-typeset summary+.tabbed-set:last-child{margin-top:0}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:rgba(68,138,255,.1)}.md-typeset .note>.admonition-title::before,.md-typeset .note>summary::before{background-color:#448aff;-webkit-mask-image:var(--md-admonition-icon--note);mask-image:var(--md-admonition-icon--note);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.md-typeset .admonition.abstract,.md-typeset details.abstract,.md-typeset .admonition.tldr,.md-typeset details.tldr,.md-typeset .admonition.summary,.md-typeset details.summary{border-color:#00b0ff}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary,.md-typeset .tldr>.admonition-title,.md-typeset .tldr>summary,.md-typeset .summary>.admonition-title,.md-typeset .summary>summary{background-color:rgba(0,176,255,.1)}.md-typeset .abstract>.admonition-title::before,.md-typeset .abstract>summary::before,.md-typeset .tldr>.admonition-title::before,.md-typeset .tldr>summary::before,.md-typeset .summary>.admonition-title::before,.md-typeset .summary>summary::before{background-color:#00b0ff;-webkit-mask-image:var(--md-admonition-icon--abstract);mask-image:var(--md-admonition-icon--abstract);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.md-typeset .admonition.info,.md-typeset details.info,.md-typeset .admonition.todo,.md-typeset details.todo{border-color:#00b8d4}.md-typeset .info>.admonition-title,.md-typeset .info>summary,.md-typeset .todo>.admonition-title,.md-typeset .todo>summary{background-color:rgba(0,184,212,.1)}.md-typeset .info>.admonition-title::before,.md-typeset .info>summary::before,.md-typeset .todo>.admonition-title::before,.md-typeset .todo>summary::before{background-color:#00b8d4;-webkit-mask-image:var(--md-admonition-icon--info);mask-image:var(--md-admonition-icon--info);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.md-typeset .admonition.tip,.md-typeset details.tip,.md-typeset .admonition.important,.md-typeset details.important,.md-typeset .admonition.hint,.md-typeset details.hint{border-color:#00bfa5}.md-typeset .tip>.admonition-title,.md-typeset .tip>summary,.md-typeset .important>.admonition-title,.md-typeset .important>summary,.md-typeset .hint>.admonition-title,.md-typeset .hint>summary{background-color:rgba(0,191,165,.1)}.md-typeset .tip>.admonition-title::before,.md-typeset .tip>summary::before,.md-typeset .important>.admonition-title::before,.md-typeset .important>summary::before,.md-typeset .hint>.admonition-title::before,.md-typeset .hint>summary::before{background-color:#00bfa5;-webkit-mask-image:var(--md-admonition-icon--tip);mask-image:var(--md-admonition-icon--tip);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.md-typeset .admonition.success,.md-typeset details.success,.md-typeset .admonition.done,.md-typeset details.done,.md-typeset .admonition.check,.md-typeset details.check{border-color:#00c853}.md-typeset .success>.admonition-title,.md-typeset .success>summary,.md-typeset .done>.admonition-title,.md-typeset .done>summary,.md-typeset .check>.admonition-title,.md-typeset .check>summary{background-color:rgba(0,200,83,.1)}.md-typeset .success>.admonition-title::before,.md-typeset .success>summary::before,.md-typeset .done>.admonition-title::before,.md-typeset .done>summary::before,.md-typeset .check>.admonition-title::before,.md-typeset .check>summary::before{background-color:#00c853;-webkit-mask-image:var(--md-admonition-icon--success);mask-image:var(--md-admonition-icon--success);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.md-typeset .admonition.question,.md-typeset details.question,.md-typeset .admonition.faq,.md-typeset details.faq,.md-typeset .admonition.help,.md-typeset details.help{border-color:#64dd17}.md-typeset .question>.admonition-title,.md-typeset .question>summary,.md-typeset .faq>.admonition-title,.md-typeset .faq>summary,.md-typeset .help>.admonition-title,.md-typeset .help>summary{background-color:rgba(100,221,23,.1)}.md-typeset .question>.admonition-title::before,.md-typeset .question>summary::before,.md-typeset .faq>.admonition-title::before,.md-typeset .faq>summary::before,.md-typeset .help>.admonition-title::before,.md-typeset .help>summary::before{background-color:#64dd17;-webkit-mask-image:var(--md-admonition-icon--question);mask-image:var(--md-admonition-icon--question);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.md-typeset .admonition.warning,.md-typeset details.warning,.md-typeset .admonition.attention,.md-typeset details.attention,.md-typeset .admonition.caution,.md-typeset details.caution{border-color:#ff9100}.md-typeset .warning>.admonition-title,.md-typeset .warning>summary,.md-typeset .attention>.admonition-title,.md-typeset .attention>summary,.md-typeset .caution>.admonition-title,.md-typeset .caution>summary{background-color:rgba(255,145,0,.1)}.md-typeset .warning>.admonition-title::before,.md-typeset .warning>summary::before,.md-typeset .attention>.admonition-title::before,.md-typeset .attention>summary::before,.md-typeset .caution>.admonition-title::before,.md-typeset .caution>summary::before{background-color:#ff9100;-webkit-mask-image:var(--md-admonition-icon--warning);mask-image:var(--md-admonition-icon--warning);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.md-typeset .admonition.failure,.md-typeset details.failure,.md-typeset .admonition.missing,.md-typeset details.missing,.md-typeset .admonition.fail,.md-typeset details.fail{border-color:#ff5252}.md-typeset .failure>.admonition-title,.md-typeset .failure>summary,.md-typeset .missing>.admonition-title,.md-typeset .missing>summary,.md-typeset .fail>.admonition-title,.md-typeset .fail>summary{background-color:rgba(255,82,82,.1)}.md-typeset .failure>.admonition-title::before,.md-typeset .failure>summary::before,.md-typeset .missing>.admonition-title::before,.md-typeset .missing>summary::before,.md-typeset .fail>.admonition-title::before,.md-typeset .fail>summary::before{background-color:#ff5252;-webkit-mask-image:var(--md-admonition-icon--failure);mask-image:var(--md-admonition-icon--failure);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.md-typeset .admonition.danger,.md-typeset details.danger,.md-typeset .admonition.error,.md-typeset details.error{border-color:#ff1744}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary,.md-typeset .error>.admonition-title,.md-typeset .error>summary{background-color:rgba(255,23,68,.1)}.md-typeset .danger>.admonition-title::before,.md-typeset .danger>summary::before,.md-typeset .error>.admonition-title::before,.md-typeset .error>summary::before{background-color:#ff1744;-webkit-mask-image:var(--md-admonition-icon--danger);mask-image:var(--md-admonition-icon--danger);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:rgba(245,0,87,.1)}.md-typeset .bug>.admonition-title::before,.md-typeset .bug>summary::before{background-color:#f50057;-webkit-mask-image:var(--md-admonition-icon--bug);mask-image:var(--md-admonition-icon--bug);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.md-typeset .admonition.example,.md-typeset details.example{border-color:#651fff}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:rgba(101,31,255,.1)}.md-typeset .example>.admonition-title::before,.md-typeset .example>summary::before{background-color:#651fff;-webkit-mask-image:var(--md-admonition-icon--example);mask-image:var(--md-admonition-icon--example);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.md-typeset .admonition.quote,.md-typeset details.quote,.md-typeset .admonition.cite,.md-typeset details.cite{border-color:#9e9e9e}.md-typeset .quote>.admonition-title,.md-typeset .quote>summary,.md-typeset .cite>.admonition-title,.md-typeset .cite>summary{background-color:rgba(158,158,158,.1)}.md-typeset .quote>.admonition-title::before,.md-typeset .quote>summary::before,.md-typeset .cite>.admonition-title::before,.md-typeset .cite>summary::before{background-color:#9e9e9e;-webkit-mask-image:var(--md-admonition-icon--quote);mask-image:var(--md-admonition-icon--quote);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat}.codehilite .o,.highlight .o,.codehilite .ow,.highlight .ow{color:var(--md-code-hl-operator-color)}.codehilite .p,.highlight .p{color:var(--md-code-hl-punctuation-color)}.codehilite .cpf,.highlight .cpf,.codehilite .l,.highlight .l,.codehilite .s,.highlight .s,.codehilite .sb,.highlight .sb,.codehilite .sc,.highlight .sc,.codehilite .s2,.highlight .s2,.codehilite .si,.highlight .si,.codehilite .s1,.highlight .s1,.codehilite .ss,.highlight .ss{color:var(--md-code-hl-string-color)}.codehilite .cp,.highlight .cp,.codehilite .se,.highlight .se,.codehilite .sh,.highlight .sh,.codehilite .sr,.highlight .sr,.codehilite .sx,.highlight .sx{color:var(--md-code-hl-special-color)}.codehilite .m,.highlight .m,.codehilite .mf,.highlight .mf,.codehilite .mh,.highlight .mh,.codehilite .mi,.highlight .mi,.codehilite .il,.highlight .il,.codehilite .mo,.highlight .mo{color:var(--md-code-hl-number-color)}.codehilite .k,.highlight .k,.codehilite .kd,.highlight .kd,.codehilite .kn,.highlight .kn,.codehilite .kp,.highlight .kp,.codehilite .kr,.highlight .kr,.codehilite .kt,.highlight .kt{color:var(--md-code-hl-keyword-color)}.codehilite .kc,.highlight .kc,.codehilite .n,.highlight .n{color:var(--md-code-hl-name-color)}.codehilite .no,.highlight .no,.codehilite .nb,.highlight .nb,.codehilite .bp,.highlight .bp{color:var(--md-code-hl-constant-color)}.codehilite .nc,.highlight .nc,.codehilite .ne,.highlight .ne,.codehilite .nf,.highlight .nf,.codehilite .nn,.highlight .nn{color:var(--md-code-hl-function-color)}.codehilite .nd,.highlight .nd,.codehilite .ni,.highlight .ni,.codehilite .nl,.highlight .nl,.codehilite .nt,.highlight .nt{color:var(--md-code-hl-keyword-color)}.codehilite .c,.highlight .c,.codehilite .cm,.highlight .cm,.codehilite .c1,.highlight .c1,.codehilite .ch,.highlight .ch,.codehilite .cs,.highlight .cs,.codehilite .sd,.highlight .sd{color:var(--md-code-hl-comment-color)}.codehilite .na,.highlight .na,.codehilite .nv,.highlight .nv,.codehilite .vc,.highlight .vc,.codehilite .vg,.highlight .vg,.codehilite .vi,.highlight .vi{color:var(--md-code-hl-variable-color)}.codehilite .ge,.highlight .ge,.codehilite .gr,.highlight .gr,.codehilite .gh,.highlight .gh,.codehilite .go,.highlight .go,.codehilite .gp,.highlight .gp,.codehilite .gs,.highlight .gs,.codehilite .gu,.highlight .gu,.codehilite .gt,.highlight .gt{color:var(--md-code-hl-generic-color)}.codehilite .gd,.highlight .gd,.codehilite .gi,.highlight .gi{margin:0 -0.125em;padding:0 .125em;border-radius:.1rem}.codehilite .gd,.highlight .gd{background-color:var(--md-typeset-del-color)}.codehilite .gi,.highlight .gi{background-color:var(--md-typeset-ins-color)}.codehilite .hll,.highlight .hll{display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em;background-color:var(--md-code-hl-color)}.codehilitetable,.highlighttable{display:block;overflow:hidden}.codehilitetable tbody,.highlighttable tbody,.codehilitetable td,.highlighttable td{display:block;padding:0}.codehilitetable tr,.highlighttable tr{display:flex}.codehilitetable pre,.highlighttable pre{margin:0}.codehilitetable .linenos,.highlighttable .linenos{padding:.525rem 1.1764705882em;padding-right:0;font-size:.85em;background-color:var(--md-code-bg-color);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.codehilitetable .linenodiv,.highlighttable .linenodiv{padding-right:.5882352941em;box-shadow:-0.05rem 0 var(--md-default-fg-color--lighter) inset}.codehilitetable .linenodiv pre,.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.codehilitetable .code,.highlighttable .code{flex:1;overflow:hidden}.md-typeset .codehilitetable,.md-typeset .highlighttable{margin:1em 0;direction:ltr;border-radius:.1rem}.md-typeset .codehilitetable code,.md-typeset .highlighttable code{border-radius:0}@media screen and (max-width: 44.9375em){.md-typeset>.codehilite,.md-typeset>.highlight{margin:1em -0.8rem}.md-typeset>.codehilite .hll,.md-typeset>.highlight .hll{margin:0 -0.8rem;padding:0 .8rem}.md-typeset>.codehilite code,.md-typeset>.highlight code{border-radius:0}.md-typeset>.codehilitetable,.md-typeset>.highlighttable{margin:1em -0.8rem;border-radius:0}.md-typeset>.codehilitetable .hll,.md-typeset>.highlighttable .hll{margin:0 -0.8rem;padding:0 .8rem}}:root{--md-footnotes-icon: url('data:image/svg+xml;charset=utf-8,')}.md-typeset [id^="fnref:"]{display:inline-block}.md-typeset [id^="fnref:"]:target{margin-top:-3.8rem;padding-top:3.8rem;pointer-events:none;scroll-margin-top:initial}.md-typeset [id^="fn:"]::before{display:none;height:0;content:""}.md-typeset [id^="fn:"]:target{scroll-margin-top:initial}.md-typeset [id^="fn:"]:target::before{display:block;margin-top:-3.5rem;padding-top:3.5rem;pointer-events:none}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}.md-typeset .footnote ol{margin-left:0}.md-typeset .footnote li{transition:color 125ms}.md-typeset .footnote li:target{color:var(--md-default-fg-color)}.md-typeset .footnote li :first-child{margin-top:0}.md-typeset .footnote li:hover .footnote-backref,.md-typeset .footnote li:target .footnote-backref{transform:translateX(0);opacity:1}.md-typeset .footnote li:hover .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-ref{display:inline-block;pointer-events:initial}.md-typeset .footnote-backref{display:inline-block;color:var(--md-typeset-a-color);font-size:0;vertical-align:text-bottom;transform:translateX(0.25rem);opacity:0;transition:color 250ms,transform 250ms 250ms,opacity 125ms 250ms}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-0.25rem)}.md-typeset .footnote-backref::before{display:inline-block;width:.8rem;height:.8rem;background-color:currentColor;-webkit-mask-image:var(--md-footnotes-icon);mask-image:var(--md-footnotes-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;content:""}[dir=rtl] .md-typeset .footnote-backref::before svg{transform:scaleX(-1)}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);transform:translateX(0);opacity:1}}.md-typeset .headerlink{display:inline-block;margin-left:.5rem;visibility:hidden;opacity:0;transition:color 250ms,visibility 0ms 500ms,opacity 125ms}[dir=rtl] .md-typeset .headerlink{margin-right:.5rem;margin-left:initial}html body .md-typeset .headerlink{color:var(--md-default-fg-color--lighter)}@media print{.md-typeset .headerlink{display:none}}.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink,.md-typeset .headerlink:focus{visibility:visible;opacity:1;transition:color 250ms,visibility 0ms,opacity 125ms}.md-typeset :target>.headerlink,.md-typeset .headerlink:focus,.md-typeset .headerlink:hover{color:var(--md-accent-fg-color)}.md-typeset :target{scroll-margin-top:3.6rem}.md-typeset h3[id]:target,.md-typeset h2[id]:target,.md-typeset h1[id]:target{scroll-margin-top:initial}.md-typeset h3[id]::before,.md-typeset h2[id]::before,.md-typeset h1[id]::before{display:block;margin-top:-0.4rem;padding-top:.4rem;content:""}.md-typeset h3[id]:target::before,.md-typeset h2[id]:target::before,.md-typeset h1[id]:target::before{margin-top:-3.4rem;padding-top:3.4rem}.md-typeset h4[id]:target{scroll-margin-top:initial}.md-typeset h4[id]::before{display:block;margin-top:-0.45rem;padding-top:.45rem;content:""}.md-typeset h4[id]:target::before{margin-top:-3.45rem;padding-top:3.45rem}.md-typeset h6[id]:target,.md-typeset h5[id]:target{scroll-margin-top:initial}.md-typeset h6[id]::before,.md-typeset h5[id]::before{display:block;margin-top:-0.6rem;padding-top:.6rem;content:""}.md-typeset h6[id]:target::before,.md-typeset h5[id]:target::before{margin-top:-3.6rem;padding-top:3.6rem}.md-typeset div.arithmatex{overflow-x:scroll}@media screen and (max-width: 44.9375em){.md-typeset div.arithmatex{margin:0 -0.8rem}}.md-typeset div.arithmatex>*{width:-webkit-min-content;width:-moz-min-content;width:min-content;margin:1em auto !important;padding:0 .8rem;overflow:auto;touch-action:auto}.md-typeset del.critic,.md-typeset ins.critic,.md-typeset .critic.comment{-webkit-box-decoration-break:clone;box-decoration-break:clone}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment::before{content:"/* "}.md-typeset .critic.comment::after{content:" */"}.md-typeset .critic.block{display:block;margin:1em 0;padding-right:.8rem;padding-left:.8rem;overflow:auto;box-shadow:none}.md-typeset .critic.block :first-child{margin-top:.5em}.md-typeset .critic.block :last-child{margin-bottom:.5em}:root{--md-details-icon: url('data:image/svg+xml;charset=utf-8,')}.md-typeset details{display:block;padding-top:0;overflow:visible}.md-typeset details[open]>summary::after{transform:rotate(90deg)}.md-typeset details:not([open]){padding-bottom:0}.md-typeset details:not([open])>summary{border-radius:.1rem}.md-typeset details::after{display:table;content:""}.md-typeset summary{display:block;min-height:1rem;padding:.4rem 1.8rem .4rem 2.2rem;border-top-left-radius:.1rem;border-top-right-radius:.1rem;cursor:pointer}.md-typeset summary:not(.focus-visible){outline:none;-webkit-tap-highlight-color:transparent}[dir=rtl] .md-typeset summary{padding:.4rem 2.2rem .4rem 1.8rem}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset summary::after{position:absolute;top:.4rem;right:.4rem;width:1rem;height:1rem;background-color:currentColor;-webkit-mask-image:var(--md-details-icon);mask-image:var(--md-details-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;transform:rotate(0deg);transition:transform 250ms;content:""}[dir=rtl] .md-typeset summary::after{right:initial;left:.4rem;transform:rotate(180deg)}.md-typeset img.emojione,.md-typeset img.twemoji,.md-typeset img.gemoji{width:1.125em;max-height:100%;vertical-align:-15%}.md-typeset span.twemoji{display:inline-block;height:1.125em;vertical-align:text-top}.md-typeset span.twemoji svg{width:1.125em;max-height:100%;fill:currentColor}.highlight [data-linenos]::before{position:-webkit-sticky;position:sticky;left:-1.1764705882em;float:left;margin-right:1.1764705882em;margin-left:-1.1764705882em;padding-left:1.1764705882em;color:var(--md-default-fg-color--light);background-color:var(--md-code-bg-color);box-shadow:-0.05rem 0 var(--md-default-fg-color--lighter) inset;content:attr(data-linenos);-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.md-typeset .keys kbd::before,.md-typeset .keys kbd::after{position:relative;margin:0;color:inherit;-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial}.md-typeset .keys span{padding:0 .2em;color:var(--md-default-fg-color--light)}.md-typeset .keys .key-alt::before{padding-right:.4em;content:"⎇"}.md-typeset .keys .key-left-alt::before{padding-right:.4em;content:"⎇"}.md-typeset .keys .key-right-alt::before{padding-right:.4em;content:"⎇"}.md-typeset .keys .key-command::before{padding-right:.4em;content:"⌘"}.md-typeset .keys .key-left-command::before{padding-right:.4em;content:"⌘"}.md-typeset .keys .key-right-command::before{padding-right:.4em;content:"⌘"}.md-typeset .keys .key-control::before{padding-right:.4em;content:"⌃"}.md-typeset .keys .key-left-control::before{padding-right:.4em;content:"⌃"}.md-typeset .keys .key-right-control::before{padding-right:.4em;content:"⌃"}.md-typeset .keys .key-meta::before{padding-right:.4em;content:"◆"}.md-typeset .keys .key-left-meta::before{padding-right:.4em;content:"◆"}.md-typeset .keys .key-right-meta::before{padding-right:.4em;content:"◆"}.md-typeset .keys .key-option::before{padding-right:.4em;content:"⌥"}.md-typeset .keys .key-left-option::before{padding-right:.4em;content:"⌥"}.md-typeset .keys .key-right-option::before{padding-right:.4em;content:"⌥"}.md-typeset .keys .key-shift::before{padding-right:.4em;content:"⇧"}.md-typeset .keys .key-left-shift::before{padding-right:.4em;content:"⇧"}.md-typeset .keys .key-right-shift::before{padding-right:.4em;content:"⇧"}.md-typeset .keys .key-super::before{padding-right:.4em;content:"❖"}.md-typeset .keys .key-left-super::before{padding-right:.4em;content:"❖"}.md-typeset .keys .key-right-super::before{padding-right:.4em;content:"❖"}.md-typeset .keys .key-windows::before{padding-right:.4em;content:"⊞"}.md-typeset .keys .key-left-windows::before{padding-right:.4em;content:"⊞"}.md-typeset .keys .key-right-windows::before{padding-right:.4em;content:"⊞"}.md-typeset .keys .key-arrow-down::before{padding-right:.4em;content:"↓"}.md-typeset .keys .key-arrow-left::before{padding-right:.4em;content:"←"}.md-typeset .keys .key-arrow-right::before{padding-right:.4em;content:"→"}.md-typeset .keys .key-arrow-up::before{padding-right:.4em;content:"↑"}.md-typeset .keys .key-backspace::before{padding-right:.4em;content:"⌫"}.md-typeset .keys .key-backtab::before{padding-right:.4em;content:"⇤"}.md-typeset .keys .key-caps-lock::before{padding-right:.4em;content:"⇪"}.md-typeset .keys .key-clear::before{padding-right:.4em;content:"⌧"}.md-typeset .keys .key-context-menu::before{padding-right:.4em;content:"☰"}.md-typeset .keys .key-delete::before{padding-right:.4em;content:"⌦"}.md-typeset .keys .key-eject::before{padding-right:.4em;content:"⏏"}.md-typeset .keys .key-end::before{padding-right:.4em;content:"⤓"}.md-typeset .keys .key-escape::before{padding-right:.4em;content:"⎋"}.md-typeset .keys .key-home::before{padding-right:.4em;content:"⤒"}.md-typeset .keys .key-insert::before{padding-right:.4em;content:"⎀"}.md-typeset .keys .key-page-down::before{padding-right:.4em;content:"⇟"}.md-typeset .keys .key-page-up::before{padding-right:.4em;content:"⇞"}.md-typeset .keys .key-print-screen::before{padding-right:.4em;content:"⎙"}.md-typeset .keys .key-tab::after{padding-left:.4em;content:"⇥"}.md-typeset .keys .key-num-enter::after{padding-left:.4em;content:"⌤"}.md-typeset .keys .key-enter::after{padding-left:.4em;content:"⏎"}.md-typeset .tabbed-content{display:none;order:99;width:100%;box-shadow:0 -0.05rem var(--md-default-fg-color--lightest)}.md-typeset .tabbed-content>pre:only-child,.md-typeset .tabbed-content>.codehilite:only-child pre,.md-typeset .tabbed-content>.codehilitetable:only-child,.md-typeset .tabbed-content>.highlight:only-child pre,.md-typeset .tabbed-content>.highlighttable:only-child{margin:0}.md-typeset .tabbed-content>pre:only-child>code,.md-typeset .tabbed-content>.codehilite:only-child pre>code,.md-typeset .tabbed-content>.codehilitetable:only-child>code,.md-typeset .tabbed-content>.highlight:only-child pre>code,.md-typeset .tabbed-content>.highlighttable:only-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-content>.tabbed-set{margin:0}.md-typeset .tabbed-set{position:relative;display:flex;flex-wrap:wrap;margin:1em 0;border-radius:.1rem}.md-typeset .tabbed-set>input{position:absolute;width:0;height:0;opacity:0}.md-typeset .tabbed-set>input:checked+label{color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color)}.md-typeset .tabbed-set>input:checked+label+.tabbed-content{display:block}.md-typeset .tabbed-set>input:focus+label{outline-style:auto}.md-typeset .tabbed-set>input:not(.focus-visible)+label{outline:none;-webkit-tap-highlight-color:transparent}.md-typeset .tabbed-set>label{z-index:1;width:auto;padding:.9375em 1.25em .78125em;color:var(--md-default-fg-color--light);font-weight:700;font-size:.64rem;border-bottom:.1rem solid transparent;cursor:pointer;transition:color 250ms}html .md-typeset .tabbed-set>label:hover{color:var(--md-accent-fg-color)}:root{--md-tasklist-icon: url( 'data:image/svg+xml;charset=utf-8,' );--md-tasklist-icon--checked: url( 'data:image/svg+xml;charset=utf-8,' )}.md-typeset .task-list-item{position:relative;list-style-type:none}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em;left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em;left:initial}.md-typeset .task-list-control .task-list-indicator::before{position:absolute;top:.15em;left:-1.5em;width:1.25em;height:1.25em;background-color:var(--md-default-fg-color--lightest);-webkit-mask-image:var(--md-tasklist-icon);mask-image:var(--md-tasklist-icon);-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;content:""}[dir=rtl] .md-typeset .task-list-control .task-list-indicator::before{right:-1.5em;left:initial}.md-typeset .task-list-control [type=checkbox]:checked+.task-list-indicator::before{background-color:#00e676;-webkit-mask-image:var(--md-tasklist-icon--checked);mask-image:var(--md-tasklist-icon--checked)}.md-typeset .task-list-control [type=checkbox]{z-index:-1;opacity:0} - -/*# sourceMappingURL=main.38780c08.min.css.map*/ \ No newline at end of file diff --git a/docs/v3/v2/assets/stylesheets/main.38780c08.min.css.map b/docs/v3/v2/assets/stylesheets/main.38780c08.min.css.map deleted file mode 100644 index 964fe38e7..000000000 --- a/docs/v3/v2/assets/stylesheets/main.38780c08.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///src/assets/stylesheets/main.scss","webpack:///src/assets/stylesheets/main/_reset.scss","webpack:///src/assets/stylesheets/main/_colors.scss","webpack:///src/assets/stylesheets/main/_icons.scss","webpack:///src/assets/stylesheets/main/_typeset.scss","webpack:///src/assets/stylesheets/utilities/_break.scss","webpack:///node_modules/material-shadows/material-shadows.scss","webpack:///src/assets/stylesheets/main/layout/_base.scss","webpack:///src/assets/stylesheets/main/layout/_announce.scss","webpack:///src/assets/stylesheets/main/layout/_button.scss","webpack:///src/assets/stylesheets/main/layout/_clipboard.scss","webpack:///src/assets/stylesheets/main/layout/_content.scss","webpack:///src/assets/stylesheets/main/layout/_dialog.scss","webpack:///src/assets/stylesheets/main/layout/_header.scss","webpack:///src/assets/stylesheets/main/layout/_footer.scss","webpack:///src/assets/stylesheets/main/layout/_nav.scss","webpack:///src/assets/stylesheets/main/layout/_search.scss","webpack:///src/assets/stylesheets/main/layout/_sidebar.scss","webpack:///src/assets/stylesheets/main/layout/_source.scss","webpack:///src/assets/stylesheets/main/layout/_tabs.scss","webpack:///src/assets/stylesheets/main/extensions/markdown/_admonition.scss","webpack:///node_modules/material-design-color/material-color.scss","webpack:///src/assets/stylesheets/main/extensions/markdown/_codehilite.scss","webpack:///src/assets/stylesheets/main/extensions/markdown/_footnotes.scss","webpack:///src/assets/stylesheets/main/extensions/markdown/_toc.scss","webpack:///src/assets/stylesheets/main/extensions/pymdownx/_arithmatex.scss","webpack:///src/assets/stylesheets/main/extensions/pymdownx/_critic.scss","webpack:///src/assets/stylesheets/main/extensions/pymdownx/_details.scss","webpack:///src/assets/stylesheets/main/extensions/pymdownx/_emoji.scss","webpack:///src/assets/stylesheets/main/extensions/pymdownx/_highlight.scss","webpack:///src/assets/stylesheets/main/extensions/pymdownx/_keys.scss","webpack:///src/assets/stylesheets/main/extensions/pymdownx/_tabbed.scss","webpack:///src/assets/stylesheets/main/extensions/pymdownx/_tasklist.scss"],"names":[],"mappings":"AAAA,KC6BA,qBACE,8BACA,CADA,0BACA,CADA,yBACA,CADA,qBACA,sBAIF,kBAGE,MAIF,QACE,IAIF,sBACE,iBACA,sBAIF,uCAIE,GAIF,aACE,qBACA,OAIF,aACE,SAIF,eAEE,KAIF,iBACE,OAIF,wBACE,iBACA,OAIF,kBAEE,mBACA,QAIF,QACE,UACA,kBACA,uBACA,SACA,OAIF,QACE,aACA,OChFF,4CAGE,oDACA,sDACA,uDACA,4CACA,qDACA,uDACA,yDACA,8CAGA,qDACA,oDACA,4CACA,qDACA,6CAGA,4DACA,2CACA,oDACA,SAGA,0CAGE,wCACA,6CAGA,gDACA,mDACA,oDACA,oDACA,mDACA,kDACA,iDACA,+DACA,kEACA,8DACA,8DACA,+DACA,+CAGA,iDACA,kDAGA,gDAGA,kDACA,4CAGA,sDACA,mDACA,qDAGA,qDACA,2CAGA,oDACA,sDACA,4CACA,kDACA,cCrEF,aACE,aACA,cACA,cACA,kBACA,MCRJ,kCACE,kCACA,YAIF,6BAEE,oCACA,wEACA,cAIF,6BAGE,6BACA,oDACA,OAQF,uNACE,0NACA,aASF,eACE,gBACA,iCACA,CADA,kBACA,cAIA,YAPF,gBAQI,qEAIF,YAIE,gBAIF,iBACE,wCACA,gBACA,cACA,gBACA,uBACA,gBAIF,oBACE,gBACA,mBACA,gBACA,uBACA,gBAIF,mBACE,gBACA,iBACA,gBACA,uBACA,mBAIF,eACE,gBAIF,YACE,gBACA,uBACA,+BAIF,eAEE,wCACA,gBACA,eACA,uBACA,gBAIF,wBACE,gBAIF,cACE,gEACA,eAIF,+BACE,sBACA,qCAGA,sBAEE,yCAIF,+BAEE,kDAKJ,6BAGE,cACA,cAGA,iDAPF,oBAQI,mBAKJ,uBACE,gBACA,sBACA,yCACA,oBACA,mCACA,CADA,0BACA,yHAIF,cAME,gBACA,6BACA,gBACA,oBAIF,kBACE,iBAIF,iBACE,aACA,gBACA,sBAGA,aACE,SACA,qCACA,cACA,kBACA,gBACA,mCACA,CADA,0BACA,kBACA,qBAEA,gEACA,4BAGA,qDACE,yCAIF,WACE,aACA,+CAIF,oDACE,qDAGA,0CACE,0CCpCN,gBD8CA,kBACE,sBAGA,eACE,kBAMN,oBACE,wBACA,iCACA,gBACA,wBACA,sBACA,6CACA,oBACA,qKAEE,kBAMJ,aACE,sBACA,8CACA,mCACA,CADA,0BACA,kBAIF,oBACE,8DACA,YACA,qBAGA,iBANF,iBAOI,2EAGA,gGE/QJ,kBFmRM,OACA,qBACA,WACA,8BACA,CADA,0BACA,CADA,qBACA,cACA,eACA,oBACA,iCACA,gBACA,sCACA,oBACA,oBACA,oBAON,WACE,iCAIF,qBAEE,qDAGA,sBACE,oBACA,wBAKJ,kBACE,wCACA,4DACA,kCAGA,mBACE,qBACA,6DACA,oBACA,gBAKJ,oBACE,+BAIF,kBAEE,UACA,mDAGA,mBACE,oBACA,qCAIF,2BACE,2CAGA,2BACE,qCAKJ,kBACE,mBACA,yDAGA,mBACE,oBACA,mGAIF,aAEE,2DAIF,eACE,qFAIF,yBAEE,6HAGA,mBACE,oBACA,gBAOR,0BACE,0BAGA,oBACE,oBACA,iCAKJ,cAEE,YACA,yDAGA,UACE,cACA,2DAIF,UACE,eACA,qEAIF,YACE,oBAKJ,yBACE,CADF,sBACE,CADF,iBACE,eACA,cACA,kBACA,wBAIF,eACE,qBACA,kBACA,oBAIF,cACE,gCAIF,oBACE,eACA,cACA,iBACA,sCACA,oBACA,mEAEE,kBAEF,cAGA,+BAbF,aAcI,mCAMF,gBACE,iGAQA,YACE,+FAIF,eACE,+FAKJ,eAEE,mHAGA,gBACE,mCAKJ,cACE,uBACA,iCACA,mBACA,mDACA,qCAGA,aACE,mCAKJ,sBACE,mBACA,6DACA,mCAIF,iCACE,yCAGA,iCACE,uDACA,kDAIF,YACE,kCAKJ,iBACE,yCAKJ,cACE,gDAGA,oBACE,YACA,aACA,iBACA,mBACA,8BACA,CADA,qBACA,YACA,qEAIF,6BACE,sDACA,CADA,6CACA,sEAIF,6BACE,uDACA,CADA,8CACA,yBAKJ,kBACE,gBACA,kBACA,oBAIF,oBACE,mBACA,gBACA,cAGA,mBANF,aAOI,gCAIF,aACE,WACA,SACA,gBACA,MGjkBN,WACE,kBAKA,eAOA,sCF0IE,KEvJJ,gBAiBI,uCFsIA,KEvJJ,cAsBI,OAKJ,iBACE,aACA,sBACA,WACA,gBACA,gBAGA,4CACA,0CFqIE,yBE/HA,cACE,eAMJ,KAtBF,aAuBI,KAKJ,aACE,cACA,UACA,SACA,UAIF,eACE,kBACA,iBACA,eAIF,YACE,sBACA,YACA,cAIA,cAPF,aAQI,WAKJ,WACE,iBAGA,YACE,YACA,kBACA,cAKJ,aACE,gBACA,mBACA,uBACA,YAQF,YACE,aAIF,cACE,MACA,UACA,QACA,SACA,iCACA,UACA,0DAEE,0CFgDA,4CExCA,UACE,YACA,UACA,8CAEE,WAYR,cACE,WAGA,aACA,oBACA,iCACA,iBACA,4CACA,oBACA,6BACA,UACA,gBAGA,UACE,wBACA,UACA,2EAEE,OAUN,WACE,cC1LF,aACE,2CACA,qBAGA,iBACE,gBACA,gCACA,gBACA,cAIF,aAbF,YAcI,yBCXF,oBACE,mBACA,iCACA,gBACA,gCACA,oBACA,iEAEE,iCAKF,gCACE,4CACA,wCACA,2DAIF,+BAEE,2CACA,uCACA,OC3BN,yPACE,eAMF,iBACE,SACA,WACA,UACA,YACA,aACA,2CACA,oBAEA,eACA,uBACA,cAGA,cAdF,YAeI,uBASF,aACE,cACA,eACA,cACA,8BACA,4CACA,CADA,mCACA,8BACA,CADA,qBACA,WACA,yBAIF,uCACE,iDAIF,+BAEE,aClDJ,MACE,eACA,+DNyII,YM3IN,8BAMI,yCN0JA,YMhKJ,kCAWI,qBAIF,qBACE,kBACA,wCN+IA,mBMjJF,mBAMI,mBACA,6BAKF,aACE,aACA,WACA,gCAIF,eACE,qBAKJ,WACE,eACA,kBACA,UACA,+BAGA,UACE,mBACA,oBACA,mCAGA,oBACE,iCAKJ,yCACE,yBAIF,cACE,mBACA,cAIF,oBA9BF,YA+BI,aCvEN,gGNFE,eMKA,YACA,aACA,aACA,UACA,cACA,kBACA,oBACA,iCACA,gBACA,sCACA,YACA,oBACA,2BACA,UACA,6CAEE,sBAIF,aACE,WACA,gCAIF,uBACE,UACA,6EAEE,cAKJ,WAtCF,YAuCI,aCvCJ,uBACE,CADF,eACE,MACA,QACA,OACA,UACA,cACA,iCACA,4CACA,+DAIE,8CAGA,mBAIF,eACE,gBACA,kCAIF,gEAEI,+DAGA,cAMJ,WApCF,YAqCI,iBAKJ,YACE,gBACA,wBAGA,iBACE,UACA,cACA,aACA,cACA,eACA,yBACA,sCAME,oBACE,2DAKJ,UAEE,gCAIF,YACE,cACA,uEAGA,aAEE,aACA,cACA,kBACA,6CAKJ,YACE,qCRwEF,qCQjEE,YACE,2CRkFJ,+BQ1EE,YACE,yCRuDJ,qCQ/CE,YACE,wBAMN,iBACE,WACA,wEAEE,6CAIF,UACE,8BACA,UACA,wEAEE,oBAEF,uDAGA,8BACE,8BAKJ,gBACE,oDAIF,YACE,uBAKJ,WACE,eACA,gBACA,mBACA,mEAGA,UACE,+BACA,UACA,wEAEE,oBAEF,6EAGA,6BACE,yFAIF,SACE,wBACA,UACA,wEAEE,uBAEF,gDAKJ,iBACE,WACA,YACA,wBAKJ,YACE,qCRtCA,uBQqCF,aAKI,cACA,kBACA,iBACA,kCAGA,iBACE,oBACA,yCRlDJ,uBQqCF,kBAmBI,kCAGA,mBACE,aC5NR,+BACE,2CACA,cAGA,WALF,YAMI,wBAQF,aACE,cACA,sBAIF,YACE,mBACA,qBACA,yBACA,qCTwIA,qBS5IF,SAQI,wDAIF,UAEE,4BAIF,UACE,sCAGA,WACE,0CAGA,oBACE,0CTmIN,2BS5IA,SAeI,kDAGA,YACE,6BAMN,WACE,iBACA,sCAGA,UACE,gBACA,0CAGA,oBACE,0CTwGN,2BSnHA,SAiBI,wBAMN,iBACE,YACA,8BACA,eACA,gBACA,mBACA,wBAIF,YACE,cACA,2BAIF,iBACE,QACA,OACA,iBACA,eACA,iBACA,WACA,iBAKJ,gDACE,wBAGA,YACE,eACA,8BACA,cACA,mCAIF,sCACE,iFAGA,+BAEE,sBAMN,UACE,kBACA,gBACA,yCACA,iBACA,qCTiBE,qBStBJ,UASI,kCAIF,sCACE,mBAKJ,cACE,sBACA,qCTCE,kBSHJ,eAMI,0BAIF,oBACE,aACA,cACA,kBACA,iCAGA,eACE,6BAIF,gBACE,oBACA,kBACA,OCtLN,2MACE,kMACA,2NACA,SAMF,eACE,gBACA,gBAGA,aACE,gBACA,gBACA,gBACA,uBACA,gCAGA,YACE,oCAGA,UACE,YACA,uFAOA,aAEE,aACA,cACA,4CAIF,iBACE,eAOR,QACE,UACA,gBACA,eAIF,eACE,0BAGA,oBACE,6BAIF,eACE,uCAGA,mBACE,eACA,wCAIF,gBACE,eAMN,aACE,kBACA,gBACA,uBACA,eACA,uBACA,wBACA,+BAIA,YACE,uCAGA,YACE,mCAKJ,uCACE,qCAIF,+BACE,qCAIF,aACE,yCAIF,+BAEE,iBAKJ,YACE,0CVkDA,QUzKJ,2CA4HI,2CAGA,iBAEE,MACA,QACA,OACA,UACA,aACA,sBACA,YACA,gEAOA,eAEE,gBACA,iCAIF,iBACE,cACA,yBACA,wCACA,gBACA,mBACA,mBACA,sDACA,eACA,+CAGA,iBACE,UACA,WACA,cACA,aACA,cACA,aACA,sDAGA,aACE,WACA,YACA,8BACA,4CACA,CADA,mCACA,8BACA,CADA,qBACA,WACA,yDAIF,WACE,aACA,+CAKJ,eACE,4CACA,iEAEE,qCACF,CADE,gCACF,CADE,4BACF,mBACA,yEAGA,YACE,+CAKJ,iBACE,iCACA,4CACA,+DAGA,iBACE,UACA,WACA,cACA,aACA,cACA,iBACA,8EASJ,WACE,aACA,gCAKJ,MACE,gCAIF,SACE,6DACA,0CAGA,SACE,sDAIF,oBACE,gEAGA,mBACE,oBACA,sDAKJ,+BACE,uHAGA,+BAEE,gCAMN,iBACE,aACA,oBACA,8CAGA,iBACE,QACA,YACA,aACA,cACA,mBACA,cACA,iBACA,qDAGA,aACE,WACA,YACA,8BACA,4CACA,CADA,mCACA,8BACA,CADA,qBACA,WACA,wDAIF,aACE,WACA,iDASJ,mBACE,mDAQF,eACE,6CAIF,eACE,6BACA,2DAGA,mBACE,qEAGA,oBACE,qBACA,mEAKJ,iBACE,6EAGA,kBACE,qBACA,2EAKJ,mBACE,qFAGA,oBACE,qBACA,mFAKJ,mBACE,6FAGA,oBACE,qBACA,yBAQV,YACE,2BACA,UACA,2EAEE,mCAIF,2BACE,iCAKJ,uBACE,UACA,4EAEE,+CAIF,kCACE,CADF,0BACE,2CVxOJ,8BUkPA,aACE,qBACA,6CAGA,YACE,+CAIF,aACE,WACA,YACA,sCACA,CADA,6BACA,8BACA,WACA,uCAIF,YACE,8BAKJ,mBACE,oBACA,iBAIF,aACE,gBACA,iCACA,kDACA,sCVxSF,6CUmTE,uBACE,iDAIF,YACE,yCVzTJ,QUvJJ,0DAudI,+CAME,uBACE,+CAIF,YACE,yBAKJ,YACE,iCAIF,aACE,8CAIF,YACE,eAIF,WACE,YACA,aACA,2BACA,yBAGA,UACE,yBACA,sBAIF,oBACE,WACA,YACA,uBACA,8BACA,4CACA,CADA,mCACA,8BACA,CADA,qBACA,WACA,2EAIF,uBACE,QClhBR,yfACE,YAMF,iBACE,mBAGA,YACE,qCX4IA,WWjJJ,eAUI,sBAIF,SACE,UACA,0CXmJA,oBWrJF,iBAMI,UACA,aACA,WACA,YACA,gBACA,4CACA,mBACA,wBACA,qDAEE,oBAEF,+BAGA,aACE,aACA,gEAIF,SACE,yCAEE,2CXuHN,+DWjHA,mBAII,gEXsEF,+DW1EF,mBASI,gEXiEF,+DW1EF,mBAcI,sCXiFJ,oBWnIF,cAwDI,MACA,OACA,QACA,SACA,iCACA,eACA,0DAEE,+BAKF,OACE,aACA,gEAIF,UACE,YACA,UACA,8CAEE,oBAQR,kCAEE,CAFF,0BAEE,0CX2DA,kBW7DF,cAMI,MACA,UACA,UACA,WACA,YACA,yBACA,UACA,iHAEE,8DAMF,MACE,wBACA,UACA,+GAEE,wEAMF,OACE,aACA,kCAKJ,UACE,aACA,0BACA,sCXCJ,kBW3CF,iBAgDI,YACA,cACA,gBACA,sDACA,6BAGA,UACE,gEXlCF,6DWuCF,aAII,yCXtBJ,6DWkBA,aASI,mBAMN,iBACE,qCXlCA,iBWiCF,mBAKI,oBAKJ,iBACE,UACA,0BACA,uBACA,4CACA,8CAEE,6BAIF,yBACE,8CAIF,8BACE,CADF,sBACE,CALA,oCAIF,2BACE,CADF,sBACE,CALA,yCAIF,0BACE,CADF,sBACE,CALA,+BAIF,sBACE,8CAIF,uCAEE,CANA,oCAIF,uCAEE,CANA,yCAIF,uCAEE,CANA,kEAIF,uCAEE,8BAIF,YACE,0CXrDF,kBWyBF,UAiCI,cACA,gBACA,sCX9EF,kBW2CF,UAwCI,cACA,oBACA,cACA,gBACA,iCACA,oBACA,6BAGA,oBACE,oCAIF,gCACE,8CAIF,uCACE,CALA,oCAIF,uCACE,CALA,yCAIF,uCACE,CALA,+BAIF,uCACE,yBAIF,sCACE,8DAIF,gCACE,mBACA,4CACA,8BACA,yFAGA,uCAEE,CALF,+EAGA,uCAEE,CALF,oFAGA,uCAEE,CALF,wJAGA,uCAEE,mBAOR,iBACE,UACA,aACA,cACA,eACA,qCAEE,wBAIF,UACE,gCAIF,SACE,WACA,0CAGA,WACE,aACA,8CAGA,oBACE,0CXzIN,+BW8HA,SAiBI,WACA,0CAGA,WACE,aACA,gDAIF,YACE,sCX5KN,+BWgJA,mBAkCI,+CAGA,YACE,+BAMN,SACE,YACA,sBACA,UACA,wEAEE,oBAEF,wCAGA,aACE,WACA,0CXvLJ,6BW0KA,SAkBI,YACA,wCAGA,aACE,WACA,yHAKJ,kBAEE,UACA,uBACA,CATE,kHAKJ,kBAEE,UACA,uBACA,8HAGA,UACE,CAJF,wHAGA,UACE,oBAOR,iBACE,UACA,WACA,gBACA,8BACA,0CX3NA,mBWsNF,UASI,SACA,sCXlPF,mBWwOF,UAeI,UACA,yBACA,+DAGA,kGV5YJ,UU+YM,yBAMN,WACE,gBACA,4CACA,mCAEA,CAFA,0BAEA,qCACA,CADA,gCACA,CADA,4BACA,mBACA,oEAGA,uBAVF,uBAWI,gEXrSA,uBW0RJ,aAgBI,yCXrRF,uBWqQF,aAqBI,sCX1RF,uBWqQF,YA0BI,qBAEA,gEACA,mEAGA,eACE,8BAIF,qDACE,2CAIF,WACE,aACA,iDAIF,oDACE,uDAGA,0CACE,oBAQV,gCACE,sBACA,yBAGA,eACE,wCACA,iBACA,mBACA,sDACA,wBACA,qCX7UA,wBWuUF,mBAUI,mCAGA,oBACE,qBACA,0BAMN,QACE,UACA,gBACA,yBAIF,4DACE,qCAGA,eACE,yBAKJ,aACE,aACA,4BACA,wBACA,6DAGA,uDAEE,mIAGA,UACE,iDAKJ,mBACE,iCAKJ,aACE,oBACA,gCACA,iBACA,UACA,eACA,8CAEE,wBAEF,6EAGA,+BAEE,wDACA,qCXrZF,gCWqYF,mBAqBI,2CAGA,oBACE,mBACA,0DAKJ,YACE,qCAOA,WACE,4BAMN,iBACE,gBACA,gBACA,qCXtbA,2BWmbF,mBAOI,sCAGA,oBACE,mBACA,gEAQF,eACE,gBACA,gBACA,gBACA,yBAMN,iBACE,OACA,aACA,cACA,aACA,wCACA,gCAGA,oBACE,WACA,YACA,8BACA,gDACA,CADA,uCACA,8BACA,CADA,qBACA,WACA,mCAIF,OACE,aACA,0CAGA,oBACE,0CXzdJ,wBW+bF,YAgCI,2BAKJ,aACE,gBACA,iBACA,gBACA,2BAIF,mBACE,gBACA,cACA,gBACA,wCACA,iBACA,gBACA,uBACA,4BACA,qBACA,0CXtfA,0BW4eF,eAcI,qBACA,gEXliBA,0BWmhBJ,eAoBI,qBACA,iCAIF,4BACE,qDACA,0BAKJ,aACE,iBACA,kBACA,wBAIF,+BACE,6BACA,iDC/rBJ,OACE,6BACE,CADF,qBACE,MAGF,oCACE,CADF,4BACE,EDyrBA,wCC/rBJ,OACE,6BACE,CADF,yBACE,CADF,qBACE,MAGF,oCACE,CADF,gCACE,CADF,4BACE,cASJ,uBACE,CADF,eACE,WACA,sBACA,cACA,iBACA,gBACA,cAGA,YATF,YAUI,2CZiJA,qBY1IA,cACE,MACA,cACA,UACA,cACA,YACA,4CACA,wBACA,yEAEE,gCAIF,cACE,aACA,oEAIF,sGXtCJ,8BWyCM,8EAGA,8BACE,8CAKJ,eACE,yBAMN,YACE,QACA,qCZ+EA,uBYjFF,aAMI,gDAGA,kBACE,0BAMN,eACE,eACA,gBACA,mCAEA,CAFA,0BAEA,qBAEA,gEACA,6BAMA,6DACE,CADF,qDACE,0CZoEF,6CY7DE,iBACE,MACA,QACA,SACA,OACA,SACA,8BACA,CADA,yBACA,CADA,qBACA,gCAKJ,qDACE,4CAIF,WACE,aACA,kDAIF,oDACE,wDAGA,0CACE,2CCjJR,GACE,QACE,MAGF,aACE,ED2II,kCCjJR,GACE,QACE,MAGF,aACE,2CAKJ,GACE,0BACE,UACA,KAGF,SACE,MAGF,wBACE,UACA,EAjBA,iCAKJ,GACE,0BACE,UACA,KAGF,SACE,MAGF,wBACE,UACA,aASJ,aACE,iBACA,gBACA,mBACA,mCAEA,CAFA,0BAEA,yBACA,kBAGA,UACE,kBAIF,oBACE,aACA,cACA,sBACA,sBAGA,gBACE,kBACA,gCAGA,kBACE,oBACA,yCAKJ,iBACE,kBACA,mDAGA,kBACE,oBACA,mBACA,qBACA,wBAMN,oBACE,8BACA,kBACA,gBACA,gBACA,uBACA,sBACA,mBAIF,QACE,UACA,gBACA,gBACA,iBACA,qBACA,YACA,wCAGA,sDACE,CADF,8CACE,kBAKJ,UACE,4BAGA,WACE,uCAIF,sDACE,CADF,8CACE,0BAIF,cACE,YACA,sCAIF,YACE,UCjIN,UACE,cACA,iCACA,4CACA,4BACA,iBAGA,eACE,0CdyKA,SclLJ,YAcI,eAIF,SAlBF,YAmBI,iBAIF,QACE,kBACA,UACA,mBACA,gBACA,gBACA,0BAGA,kBACE,oBACA,gBAKJ,oBACE,cACA,oBACA,mBACA,gBAKF,aACE,iBACA,gBACA,WACA,wEAEE,uBAIF,eACE,6CAIF,aAEE,UACA,4CAKA,qBACE,4CADF,qBACE,4CADF,qBACE,4CADF,qBACE,4CADF,sBACE,4CADF,sBACE,4CADF,sBACE,4CADF,sBACE,6CADF,sBACE,6CADF,sBACE,6CADF,sBACE,6CADF,sBACE,6CADF,sBACE,6CADF,sBACE,6CADF,sBACE,gCAMN,mBACE,+CAIA,yBACE,UACA,yDAEE,wCdyEJ,uEc/DA,YACE,2DAUE,aACE,gBACA,oBACA,wBACA,yEAGA,YACE,wEAKJ,YACE,gFAGA,aACE,UACA,8FAGA,YACE,sDAOR,aAGE,kFAGA,eACE,6FAGA,oBACE,2GAGA,gBACE,6EAMN,YACE,QC1IV,4RAMI,qwHAUF,iBACE,gBACA,gBACA,oCACA,iBACA,wBACA,+CACA,gCACA,oBACA,mEAEE,iEAIF,gCACE,iBACA,cAIF,4CArBF,eAsBI,gFAIF,mBACE,iIAIF,YACE,6FAIF,kBACE,mFAIF,eACE,2FAIF,YACE,mDAKJ,iBACE,2BACA,iCACA,gBACA,qCACA,uEAGA,0BACE,+BACA,mFAIF,eACE,mEAIF,iBACE,WACA,WACA,YACA,yBCwIU,mDDtIV,CCsIU,0CDtIV,8BACA,CADA,qBACA,WACA,uFAGA,WACE,aACA,6DAKJ,cACE,gBACA,mBACA,6BACA,sBACA,gBACA,iGAIF,YACE,uDAcJ,oBAHO,+DAQP,oCACE,+EAGA,wBAZK,mDAcH,CAdG,0CAcH,8BACA,CADA,qBACA,iLAZJ,oBAHO,yMAQP,mCACE,yPAGA,wBAZK,uDAcH,CAdG,8CAcH,8BACA,CADA,qBACA,6GAZJ,oBAHO,6HAQP,mCACE,6JAGA,wBAZK,mDAcH,CAdG,0CAcH,8BACA,CADA,qBACA,2KAZJ,oBAHO,mMAQP,mCACE,mPAGA,wBAZK,kDAcH,CAdG,yCAcH,8BACA,CADA,qBACA,2KAZJ,oBAHO,mMAQP,kCACE,mPAGA,wBAZK,sDAcH,CAdG,6CAcH,8BACA,CADA,qBACA,yKAZJ,oBAHO,iMAQP,oCACE,iPAGA,wBAZK,uDAcH,CAdG,8CAcH,8BACA,CADA,qBACA,yLAZJ,oBAHO,iNAQP,mCACE,iQAGA,wBAZK,sDAcH,CAdG,6CAcH,8BACA,CADA,qBACA,+KAZJ,oBAHO,uMAQP,mCACE,uPAGA,wBAZK,sDAcH,CAdG,6CAcH,8BACA,CADA,qBACA,mHAZJ,oBAHO,mIAQP,mCACE,mKAGA,wBAZK,qDAcH,CAdG,4CAcH,8BACA,CADA,qBACA,qDAZJ,oBAHO,6DAQP,kCACE,6EAGA,wBAZK,kDAcH,CAdG,yCAcH,8BACA,CADA,qBACA,6DAZJ,oBAHO,qEAQP,oCACE,qFAGA,wBAZK,sDAcH,CAdG,6CAcH,8BACA,CADA,qBACA,+GAZJ,oBAHO,+HAQP,qCACE,+JAGA,wBAZK,oDAcH,CAdG,2CAcH,8BACA,CADA,qBACA,6DElKJ,sCAEE,8BAGF,yCACE,sRAGF,oCASE,4JAGF,qCAKE,yLAGF,oCAME,yLAGF,qCAME,6DAGF,kCAEE,8FAGF,sCAGE,6HAGF,sCAIE,6HAGF,qCAIE,yLAGF,qCAME,4JAGF,sCAKE,yPAGF,qCAQE,+DAGF,iBAEE,iBACA,oBACA,gCAGF,4CACE,gCAGF,4CACE,kCAIF,aACE,yBACA,yBACA,yCACA,kCASJ,aACE,gBACA,qFAIA,aAEE,UACA,wCAKF,YACE,0CAKF,QACE,oDAKF,8BACE,gBACA,gBACA,yCACA,yBACA,CADA,qBACA,CADA,oBACA,CADA,gBACA,wDAIF,2BACE,gEACA,gEAGA,uCACE,iBACA,8CAMJ,MACE,gBACA,0DAQF,YACE,cACA,oBACA,oEAGA,eACE,0CjBlBF,+CiB0BA,kBACE,0DAGA,gBACE,gBACA,0DAIF,eACE,0DAKJ,kBACE,gBACA,oEAGA,gBACE,gBACA,QCnOR,yMACE,4BASA,oBACE,mCAGA,kBACE,mBACA,oBACA,0BACA,iCAQF,YACE,SACA,WACA,gCAIF,yBACE,wCAIF,aACE,mBACA,mBACA,oBACA,uBAKJ,uCACE,iBACA,0BAGA,aACE,0BAIF,sBACE,iCAGA,gCACE,uCAIF,YACE,oGAIF,uBAEE,UACA,wDAIF,+BACE,2BAMN,oBACE,uBACA,+BAIF,oBACE,gCACA,YAEA,2BACA,8BACA,UACA,iEAEE,yCAKF,8BACE,uCAIF,oBACE,YACA,aACA,8BACA,4CACA,CADA,mCACA,8BACA,CADA,qBACA,WACA,qDAME,oBACE,cAMN,8BAvCF,+BAwCI,wBACA,UACA,0BClIJ,oBACE,kBACA,kBAGA,UACA,0DAEE,mCAKF,kBACE,oBACA,mCAIF,yCACE,cAIF,wBAxBF,YAyBI,+FAKJ,kBAGE,UACA,oDAEE,6FAMJ,+BAGE,qBAMF,wBACE,+EAYE,yBACE,kFAIF,aACE,mBACA,kBACA,WACA,uGAIF,kBACE,mBACA,2BAfF,yBACE,4BAIF,aACE,oBACA,mBACA,WACA,mCAIF,mBACE,oBACA,qDAfF,yBACE,uDAIF,aACE,mBACA,kBACA,WACA,qEAIF,kBACE,mBACA,4BC/EN,iBACE,0CpB8KA,2BoB/KF,gBAKI,+BAIF,yBACE,CADF,sBACE,CADF,iBACE,2BACA,gBACA,cACA,kBACA,2ECdJ,kCAGE,CAHF,0BAGE,wBAIF,4CACE,wBAIF,4CACE,6BAIF,qCACE,qCAGA,aACE,oCAIF,aACE,2BAKJ,aACE,aACA,oBACA,mBACA,cACA,gBACA,wCAGA,eACE,uCAIF,kBACE,OClDN,+LACE,qBASA,aAGE,cACA,iBACA,0CAGA,uBACE,iCAIF,gBACE,yCAIA,mBACE,4BAKJ,aACE,WACA,qBAKJ,aAGE,gBACA,kCACA,6BACA,8BACA,eACA,yCAGA,YACE,wCACA,+BAIF,iCACE,6CAIF,YACE,4BAIF,iBACE,UACA,YACA,WACA,YACA,8BACA,0CACA,CADA,iCACA,8BACA,CADA,qBACA,uBACA,2BACA,WACA,sCAGA,aACE,WACA,yBACA,yEClFN,aAGE,gBACA,oBACA,0BAIF,oBACE,eACA,wBACA,8BAGA,aACE,gBACA,kBACA,mCCfJ,uBACE,CADF,eACE,qBACA,WACA,4BACA,4BACA,4BACA,wCACA,yCACA,gEACA,2BACA,yBACA,CADA,qBACA,CADA,oBACA,CADA,gBACA,4DCdF,iBAEE,SACA,cACA,gCACA,+BACA,wBAIF,cACE,wCACA,oCAqDE,kBACE,YAlDgB,yCAiDlB,kBACE,YAlDgB,0CAiDlB,kBACE,YAlDgB,wCAiDlB,kBACE,YAlDgB,6CAiDlB,kBACE,YAlDgB,8CAiDlB,kBACE,YAlDgB,wCAiDlB,kBACE,YAlDgB,6CAiDlB,kBACE,YAlDgB,8CAiDlB,kBACE,YAlDgB,qCAiDlB,kBACE,YAlDgB,0CAiDlB,kBACE,YAlDgB,2CAiDlB,kBACE,YAlDgB,uCAiDlB,kBACE,YAlDgB,4CAiDlB,kBACE,YAlDgB,6CAiDlB,kBACE,YAlDgB,sCAiDlB,kBACE,YAlDgB,2CAiDlB,kBACE,YAlDgB,4CAiDlB,kBACE,YAlDgB,sCAiDlB,kBACE,YAlDgB,2CAiDlB,kBACE,YAlDgB,4CAiDlB,kBACE,YAlDgB,wCAiDlB,kBACE,YAlDgB,6CAiDlB,kBACE,YAlDgB,8CAiDlB,kBACE,YAlDgB,2CAiDlB,kBACE,YAlDgB,2CAiDlB,kBACE,YAlDgB,4CAiDlB,kBACE,YAlDgB,yCAiDlB,kBACE,YAlDgB,0CAiDlB,kBACE,YAlDgB,wCAiDlB,kBACE,YAlDgB,0CAiDlB,kBACE,YAlDgB,sCAiDlB,kBACE,YAlDgB,6CAiDlB,kBACE,YAlDgB,uCAiDlB,kBACE,YAlDgB,sCAiDlB,kBACE,YAlDgB,oCAiDlB,kBACE,YAlDgB,uCAiDlB,kBACE,YAlDgB,qCAiDlB,kBACE,YAlDgB,uCAiDlB,kBACE,YAlDgB,0CAiDlB,kBACE,YAlDgB,wCAiDlB,kBACE,YAlDgB,6CAiDlB,kBACE,YAlDgB,mCA+DlB,iBACE,YAPgB,yCAMlB,iBACE,YAPgB,qCAMlB,iBACE,YAPgB,6BCzEtB,YACE,SACA,WACA,2DACA,wQAGA,QAKE,iSAGA,wBACE,0BACA,yCAKJ,QACE,yBAKJ,iBACE,aACA,eACA,aACA,oBACA,+BAGA,iBACE,QACA,SACA,UACA,6CAGA,+BACE,uCACA,6DAGA,aACE,2CAKJ,kBACE,yDAIF,YACE,wCACA,+BAKJ,SACE,WACA,gCACA,wCACA,gBACA,iBACA,sCACA,eACA,uBACA,0CAGA,+BACE,OClFR,kVACE,4VAGA,6BAWA,iBACE,qBACA,6CAIA,iBACE,UACA,UACA,uDAGA,UACE,aACA,6DASJ,iBACE,UACA,YACA,aACA,cACA,sDACA,2CACA,CADA,kCACA,8BACA,CADA,qBACA,WACA,uEAGA,YACE,aACA,qFAKJ,wBXiWa,oDW/VX,CX+VW,2CW/VX,gDAIF,UACE,UACA,C","file":"assets/stylesheets/main.38780c08.min.css","sourcesContent":["html{box-sizing:border-box;text-size-adjust:none}*,*::before,*::after{box-sizing:inherit}body{margin:0}hr{box-sizing:content-box;overflow:visible}a,button,label,input{-webkit-tap-highlight-color:transparent}a{color:inherit;text-decoration:none}small{font-size:80%}sub,sup{line-height:1em}img{border-style:none}table{border-collapse:separate;border-spacing:0}td,th{font-weight:normal;vertical-align:top}button{margin:0;padding:0;font-size:inherit;background:transparent;border:0}input{border:0;outline:none}:root{--md-default-fg-color: hsla(0, 0%, 0%, 0.87);--md-default-fg-color--light: hsla(0, 0%, 0%, 0.54);--md-default-fg-color--lighter: hsla(0, 0%, 0%, 0.32);--md-default-fg-color--lightest: hsla(0, 0%, 0%, 0.07);--md-default-bg-color: hsla(0, 0%, 100%, 1);--md-default-bg-color--light: hsla(0, 0%, 100%, 0.7);--md-default-bg-color--lighter: hsla(0, 0%, 100%, 0.3);--md-default-bg-color--lightest: hsla(0, 0%, 100%, 0.12);--md-primary-fg-color: hsla(231, 48%, 48%, 1);--md-primary-fg-color--light: hsla(230, 44%, 64%, 1);--md-primary-fg-color--dark: hsla(232, 54%, 41%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);--md-accent-fg-color: hsla(231, 99%, 66%, 1);--md-accent-fg-color--transparent: hsla(231, 99%, 66%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}:root>*{--md-code-fg-color: hsla(200, 18%, 26%, 1);--md-code-bg-color: hsla(0, 0%, 96%, 1);--md-code-hl-color: hsla(60, 100%, 50%, 0.5);--md-code-hl-number-color: hsla(0, 67%, 50%, 1);--md-code-hl-special-color: hsla(340, 83%, 47%, 1);--md-code-hl-function-color: hsla(291, 45%, 50%, 1);--md-code-hl-constant-color: hsla(250, 63%, 60%, 1);--md-code-hl-keyword-color: hsla(219, 54%, 51%, 1);--md-code-hl-string-color: hsla(150, 63%, 30%, 1);--md-code-hl-name-color: var(--md-code-fg-color);--md-code-hl-operator-color: var(--md-default-fg-color--light);--md-code-hl-punctuation-color: var(--md-default-fg-color--light);--md-code-hl-comment-color: var(--md-default-fg-color--light);--md-code-hl-generic-color: var(--md-default-fg-color--light);--md-code-hl-variable-color: var(--md-default-fg-color--light);--md-typeset-color: var(--md-default-fg-color);--md-typeset-a-color: var(--md-primary-fg-color);--md-typeset-mark-color: hsla(60, 100%, 50%, 0.5);--md-typeset-del-color: hsla(6, 90%, 60%, 0.15);--md-typeset-ins-color: hsla(150, 90%, 44%, 0.15);--md-typeset-kbd-color: hsla(0, 0%, 98%, 1);--md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);--md-typeset-kbd-border-color: hsla(0, 0%, 72%, 1);--md-admonition-fg-color: var(--md-default-fg-color);--md-admonition-bg-color: var(--md-default-bg-color);--md-footer-fg-color: hsla(0, 0%, 100%, 1);--md-footer-fg-color--light: hsla(0, 0%, 100%, 0.7);--md-footer-fg-color--lighter: hsla(0, 0%, 100%, 0.3);--md-footer-bg-color: hsla(0, 0%, 0%, 0.87);--md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.32)}.md-icon svg{display:block;width:1.2rem;height:1.2rem;margin:0 auto;fill:currentColor}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body,input{color:var(--md-typeset-color);font-feature-settings:\"kern\",\"liga\";font-family:-apple-system,BlinkMacSystemFont,Helvetica,Arial,sans-serif}code,pre,kbd{color:var(--md-typeset-color);font-feature-settings:\"kern\";font-family:SFMono-Regular,Consolas,Menlo,monospace}:root{--md-typeset-table--ascending: svg-load(\"@mdi/svg/svg/arrow-down.svg\");--md-typeset-table--descending: svg-load(\"@mdi/svg/svg/arrow-up.svg\")}.md-typeset{font-size:.8rem;line-height:1.6;color-adjust:exact}@media print{.md-typeset{font-size:.68rem}}.md-typeset p,.md-typeset ul,.md-typeset ol,.md-typeset blockquote{margin:1em 0}.md-typeset h1{margin:0 0 1.25em;color:var(--md-default-fg-color--light);font-weight:300;font-size:2em;line-height:1.3;letter-spacing:-0.01em}.md-typeset h2{margin:1.6em 0 .64em;font-weight:300;font-size:1.5625em;line-height:1.4;letter-spacing:-0.01em}.md-typeset h3{margin:1.6em 0 .8em;font-weight:400;font-size:1.25em;line-height:1.5;letter-spacing:-0.01em}.md-typeset h2+h3{margin-top:.8em}.md-typeset h4{margin:1em 0;font-weight:700;letter-spacing:-0.01em}.md-typeset h5,.md-typeset h6{margin:1.25em 0;color:var(--md-default-fg-color--light);font-weight:700;font-size:.8em;letter-spacing:-0.01em}.md-typeset h5{text-transform:uppercase}.md-typeset hr{margin:1.5em 0;border-bottom:.05rem dotted var(--md-default-fg-color--lighter)}.md-typeset a{color:var(--md-typeset-a-color);word-break:break-word}.md-typeset a,.md-typeset a::before{transition:color 125ms}.md-typeset a:focus,.md-typeset a:hover{color:var(--md-accent-fg-color)}.md-typeset code,.md-typeset pre,.md-typeset kbd{color:var(--md-code-fg-color);direction:ltr}@media print{.md-typeset code,.md-typeset pre,.md-typeset kbd{white-space:pre-wrap}}.md-typeset code{padding:0 .2941176471em;font-size:.85em;word-break:break-word;background-color:var(--md-code-bg-color);border-radius:.1rem;box-decoration-break:clone}.md-typeset h1 code,.md-typeset h2 code,.md-typeset h3 code,.md-typeset h4 code,.md-typeset h5 code,.md-typeset h6 code{margin:initial;padding:initial;background-color:transparent;box-shadow:none}.md-typeset a>code{color:currentColor}.md-typeset pre{position:relative;margin:1em 0;line-height:1.4}.md-typeset pre>code{display:block;margin:0;padding:.7720588235em 1.1764705882em;overflow:auto;word-break:normal;box-shadow:none;box-decoration-break:slice;touch-action:auto;scrollbar-width:thin;scrollbar-color:var(--md-default-fg-color--lighter) transparent}.md-typeset pre>code:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-typeset pre>code::-webkit-scrollbar{width:.2rem;height:.2rem}.md-typeset pre>code::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-typeset pre>code::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@media screen and (max-width: 44.9375em){.md-typeset>pre{margin:1em -0.8rem}.md-typeset>pre code{border-radius:0}}.md-typeset kbd{display:inline-block;padding:0 .6666666667em;color:var(--md-default-fg-color);font-size:.75em;vertical-align:text-top;word-break:break-word;background-color:var(--md-typeset-kbd-color);border-radius:.1rem;box-shadow:0 .1rem 0 .05rem var(--md-typeset-kbd-border-color),0 .1rem 0 var(--md-typeset-kbd-border-color),0 -0.1rem .2rem var(--md-typeset-kbd-accent-color) inset}.md-typeset mark{color:inherit;word-break:break-word;background-color:var(--md-typeset-mark-color);box-decoration-break:clone}.md-typeset abbr{text-decoration:none;border-bottom:.05rem dotted var(--md-default-fg-color--light);cursor:help}@media(hover: none){.md-typeset abbr{position:relative}.md-typeset abbr[title]:focus::after,.md-typeset abbr[title]:hover::after{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);position:absolute;left:0;display:inline-block;width:auto;min-width:max-content;max-width:80%;margin-top:2em;padding:.2rem .3rem;color:var(--md-default-bg-color);font-size:.7rem;background:var(--md-default-fg-color);border-radius:.1rem;content:attr(title)}}.md-typeset small{opacity:.75}.md-typeset sup,.md-typeset sub{margin-left:.078125em}[dir=rtl] .md-typeset sup,[dir=rtl] .md-typeset sub{margin-right:.078125em;margin-left:initial}.md-typeset blockquote{padding-left:.6rem;color:var(--md-default-fg-color--light);border-left:.2rem solid var(--md-default-fg-color--lighter)}[dir=rtl] .md-typeset blockquote{padding-right:.6rem;padding-left:initial;border-right:.2rem solid var(--md-default-fg-color--lighter);border-left:initial}.md-typeset ul{list-style-type:disc}.md-typeset ul,.md-typeset ol{margin-left:.625em;padding:0}[dir=rtl] .md-typeset ul,[dir=rtl] .md-typeset ol{margin-right:.625em;margin-left:initial}.md-typeset ul ol,.md-typeset ol ol{list-style-type:lower-alpha}.md-typeset ul ol ol,.md-typeset ol ol ol{list-style-type:lower-roman}.md-typeset ul li,.md-typeset ol li{margin-bottom:.5em;margin-left:1.25em}[dir=rtl] .md-typeset ul li,[dir=rtl] .md-typeset ol li{margin-right:1.25em;margin-left:initial}.md-typeset ul li p,.md-typeset ul li blockquote,.md-typeset ol li p,.md-typeset ol li blockquote{margin:.5em 0}.md-typeset ul li:last-child,.md-typeset ol li:last-child{margin-bottom:0}.md-typeset ul li ul,.md-typeset ul li ol,.md-typeset ol li ul,.md-typeset ol li ol{margin:.5em 0 .5em .625em}[dir=rtl] .md-typeset ul li ul,[dir=rtl] .md-typeset ul li ol,[dir=rtl] .md-typeset ol li ul,[dir=rtl] .md-typeset ol li ol{margin-right:.625em;margin-left:initial}.md-typeset dd{margin:1em 0 1.5em 1.875em}[dir=rtl] .md-typeset dd{margin-right:1.875em;margin-left:initial}.md-typeset img,.md-typeset svg{max-width:100%;height:auto}.md-typeset img[align=left],.md-typeset svg[align=left]{margin:1em;margin-left:0}.md-typeset img[align=right],.md-typeset svg[align=right]{margin:1em;margin-right:0}.md-typeset img[align]:only-child,.md-typeset svg[align]:only-child{margin-top:0}.md-typeset figure{width:fit-content;max-width:100%;margin:0 auto;text-align:center}.md-typeset figcaption{max-width:24rem;margin:.5em auto 2em;font-style:italic}.md-typeset iframe{max-width:100%}.md-typeset table:not([class]){display:inline-block;max-width:100%;overflow:auto;font-size:.64rem;background:var(--md-default-bg-color);border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .05rem rgba(0,0,0,.1);touch-action:auto}@media print{.md-typeset table:not([class]){display:table}}.md-typeset table:not([class])+*{margin-top:1.5em}.md-typeset table:not([class]) th>*:first-child,.md-typeset table:not([class]) td>*:first-child{margin-top:0}.md-typeset table:not([class]) th>*:last-child,.md-typeset table:not([class]) td>*:last-child{margin-bottom:0}.md-typeset table:not([class]) th:not([align]),.md-typeset table:not([class]) td:not([align]){text-align:left}[dir=rtl] .md-typeset table:not([class]) th:not([align]),[dir=rtl] .md-typeset table:not([class]) td:not([align]){text-align:right}.md-typeset table:not([class]) th{min-width:5rem;padding:.9375em 1.25em;color:var(--md-default-bg-color);vertical-align:top;background-color:var(--md-default-fg-color--light)}.md-typeset table:not([class]) th a{color:inherit}.md-typeset table:not([class]) td{padding:.9375em 1.25em;vertical-align:top;border-top:.05rem solid var(--md-default-fg-color--lightest)}.md-typeset table:not([class]) tr{transition:background-color 125ms}.md-typeset table:not([class]) tr:hover{background-color:rgba(0,0,0,.035);box-shadow:0 .05rem 0 var(--md-default-bg-color) inset}.md-typeset table:not([class]) tr:first-child td{border-top:0}.md-typeset table:not([class]) a{word-break:normal}.md-typeset table th[role=columnheader]{cursor:pointer}.md-typeset table th[role=columnheader]::after{display:inline-block;width:1.2em;height:1.2em;margin-left:.5em;vertical-align:sub;mask-repeat:no-repeat;content:\" \"}.md-typeset table th[role=columnheader][aria-sort=ascending]::after{background-color:currentColor;mask-image:var(--md-typeset-table--ascending)}.md-typeset table th[role=columnheader][aria-sort=descending]::after{background-color:currentColor;mask-image:var(--md-typeset-table--descending)}.md-typeset__scrollwrap{margin:1em -0.8rem;overflow-x:auto;touch-action:auto}.md-typeset__table{display:inline-block;margin-bottom:.5em;padding:0 .8rem}@media print{.md-typeset__table{display:block}}html .md-typeset__table table{display:table;width:100%;margin:0;overflow:hidden}html{height:100%;overflow-x:hidden;font-size:125%}@media screen and (min-width: 100em){html{font-size:137.5%}}@media screen and (min-width: 125em){html{font-size:150%}}body{position:relative;display:flex;flex-direction:column;width:100%;min-height:100%;font-size:.5rem;background-color:var(--md-default-bg-color)}@media screen and (max-width: 59.9375em){body[data-md-state=lock]{position:fixed}}@media print{body{display:block}}hr{display:block;height:.05rem;padding:0;border:0}.md-grid{max-width:61rem;margin-right:auto;margin-left:auto}.md-container{display:flex;flex-direction:column;flex-grow:1}@media print{.md-container{display:block}}.md-main{flex-grow:1}.md-main__inner{display:flex;height:100%;margin-top:1.5rem}.md-ellipsis{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.md-toggle{display:none}.md-overlay{position:fixed;top:0;z-index:3;width:0;height:0;background-color:rgba(0,0,0,.54);opacity:0;transition:width 0ms 250ms,height 0ms 250ms,opacity 250ms}@media screen and (max-width: 76.1875em){[data-md-toggle=drawer]:checked~.md-overlay{width:100%;height:100%;opacity:1;transition:width 0ms,height 0ms,opacity 250ms}}.md-skip{position:fixed;z-index:-1;margin:.5rem;padding:.3rem .5rem;color:var(--md-default-bg-color);font-size:.64rem;background-color:var(--md-default-fg-color);border-radius:.1rem;transform:translateY(0.4rem);opacity:0}.md-skip:focus{z-index:10;transform:translateY(0);opacity:1;transition:transform 250ms cubic-bezier(0.4, 0, 0.2, 1),opacity 175ms 75ms}@page{margin:25mm}.md-announce{overflow:auto;background-color:var(--md-footer-bg-color)}.md-announce__inner{margin:.6rem auto;padding:0 .8rem;color:var(--md-footer-fg-color);font-size:.7rem}@media print{.md-announce{display:none}}.md-typeset .md-button{display:inline-block;padding:.625em 2em;color:var(--md-primary-fg-color);font-weight:700;border:.1rem solid currentColor;border-radius:.1rem;transition:color 125ms,background-color 125ms,border-color 125ms}.md-typeset .md-button--primary{color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color);border-color:var(--md-primary-fg-color)}.md-typeset .md-button:focus,.md-typeset .md-button:hover{color:var(--md-accent-bg-color);background-color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color)}:root{--md-clipboard-icon: svg-load(\"@mdi/svg/svg/content-copy.svg\")}.md-clipboard{position:absolute;top:.5em;right:.5em;z-index:1;width:1.5em;height:1.5em;color:var(--md-default-fg-color--lightest);border-radius:.1rem;cursor:pointer;transition:color 125ms}@media print{.md-clipboard{display:none}}.md-clipboard::after{display:block;width:1.125em;height:1.125em;margin:0 auto;background-color:currentColor;mask-image:var(--md-clipboard-icon);mask-repeat:no-repeat;content:\"\"}pre:hover .md-clipboard{color:var(--md-default-fg-color--light)}pre .md-clipboard:focus,pre .md-clipboard:hover{color:var(--md-accent-fg-color)}.md-content{flex:1;max-width:100%}@media screen and (min-width: 60em)and (max-width: 76.1875em){.md-content{max-width:calc(100% - 12.1rem)}}@media screen and (min-width: 76.25em){.md-content{max-width:calc(100% - 12.1rem * 2)}}.md-content__inner{margin:0 .8rem 1.2rem;padding-top:.6rem}@media screen and (min-width: 76.25em){.md-content__inner{margin-right:1.2rem;margin-left:1.2rem}}.md-content__inner::before{display:block;height:.4rem;content:\"\"}.md-content__inner>:last-child{margin-bottom:0}.md-content__button{float:right;margin:.4rem 0;margin-left:.4rem;padding:0}[dir=rtl] .md-content__button{float:left;margin-right:.4rem;margin-left:initial}[dir=rtl] .md-content__button svg{transform:scaleX(-1)}.md-typeset .md-content__button{color:var(--md-default-fg-color--lighter)}.md-content__button svg{display:inline;vertical-align:top}@media print{.md-content__button{display:none}}.md-dialog{box-shadow:0 2px 2px 0 rgba(0,0,0,.14),0 1px 5px 0 rgba(0,0,0,.12),0 3px 1px -2px rgba(0,0,0,.2);position:fixed;right:.8rem;bottom:.8rem;left:initial;z-index:2;display:block;min-width:11.1rem;padding:.4rem .6rem;color:var(--md-default-bg-color);font-size:.7rem;background:var(--md-default-fg-color);border:none;border-radius:.1rem;transform:translateY(100%);opacity:0;transition:transform 0ms 400ms,opacity 400ms}[dir=rtl] .md-dialog{right:initial;left:.8rem}.md-dialog[data-md-state=open]{transform:translateY(0);opacity:1;transition:transform 400ms cubic-bezier(0.075, 0.85, 0.175, 1),opacity 400ms}@media print{.md-dialog{display:none}}.md-header{position:sticky;top:0;right:0;left:0;z-index:2;height:2.4rem;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color);box-shadow:0 0 .2rem rgba(0,0,0,0),0 .2rem .4rem rgba(0,0,0,0);transition:color 250ms,background-color 250ms}.no-js .md-header{box-shadow:none;transition:none}.md-header[data-md-state=shadow]{box-shadow:0 0 .2rem rgba(0,0,0,.1),0 .2rem .4rem rgba(0,0,0,.2);transition:color 250ms,background-color 250ms,box-shadow 250ms}@media print{.md-header{display:none}}.md-header-nav{display:flex;padding:0 .2rem}.md-header-nav__button{position:relative;z-index:1;display:block;margin:.2rem;padding:.4rem;cursor:pointer;transition:opacity 250ms}[dir=rtl] .md-header-nav__button svg{transform:scaleX(-1)}.md-header-nav__button:focus,.md-header-nav__button:hover{opacity:.7}.md-header-nav__button.md-logo{margin:.2rem;padding:.4rem}.md-header-nav__button.md-logo img,.md-header-nav__button.md-logo svg{display:block;width:1.2rem;height:1.2rem;fill:currentColor}.no-js .md-header-nav__button[for=__search]{display:none}@media screen and (min-width: 60em){.md-header-nav__button[for=__search]{display:none}}@media screen and (max-width: 76.1875em){.md-header-nav__button.md-logo{display:none}}@media screen and (min-width: 76.25em){.md-header-nav__button[for=__drawer]{display:none}}.md-header-nav__topic{position:absolute;width:100%;transition:transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),opacity 150ms}.md-header-nav__topic+.md-header-nav__topic{z-index:-1;transform:translateX(1.25rem);opacity:0;transition:transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),opacity 150ms;pointer-events:none}[dir=rtl] .md-header-nav__topic+.md-header-nav__topic{transform:translateX(-1.25rem)}.no-js .md-header-nav__topic{position:initial}.no-js .md-header-nav__topic+.md-header-nav__topic{display:none}.md-header-nav__title{flex-grow:1;padding:0 1rem;font-size:.9rem;line-height:2.4rem}.md-header-nav__title[data-md-state=active] .md-header-nav__topic{z-index:-1;transform:translateX(-1.25rem);opacity:0;transition:transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),opacity 150ms;pointer-events:none}[dir=rtl] .md-header-nav__title[data-md-state=active] .md-header-nav__topic{transform:translateX(1.25rem)}.md-header-nav__title[data-md-state=active] .md-header-nav__topic+.md-header-nav__topic{z-index:0;transform:translateX(0);opacity:1;transition:transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),opacity 150ms;pointer-events:initial}.md-header-nav__title>.md-header-nav__ellipsis{position:relative;width:100%;height:100%}.md-header-nav__source{display:none}@media screen and (min-width: 60em){.md-header-nav__source{display:block;width:11.7rem;max-width:11.7rem;margin-left:1rem}[dir=rtl] .md-header-nav__source{margin-right:1rem;margin-left:initial}}@media screen and (min-width: 76.25em){.md-header-nav__source{margin-left:1.4rem}[dir=rtl] .md-header-nav__source{margin-right:1.4rem}}.md-footer{color:var(--md-footer-fg-color);background-color:var(--md-footer-bg-color)}@media print{.md-footer{display:none}}.md-footer-nav__inner{padding:.2rem;overflow:auto}.md-footer-nav__link{display:flex;padding-top:1.4rem;padding-bottom:.4rem;transition:opacity 250ms}@media screen and (min-width: 45em){.md-footer-nav__link{width:50%}}.md-footer-nav__link:focus,.md-footer-nav__link:hover{opacity:.7}.md-footer-nav__link--prev{float:left}[dir=rtl] .md-footer-nav__link--prev{float:right}[dir=rtl] .md-footer-nav__link--prev svg{transform:scaleX(-1)}@media screen and (max-width: 44.9375em){.md-footer-nav__link--prev{width:25%}.md-footer-nav__link--prev .md-footer-nav__title{display:none}}.md-footer-nav__link--next{float:right;text-align:right}[dir=rtl] .md-footer-nav__link--next{float:left;text-align:left}[dir=rtl] .md-footer-nav__link--next svg{transform:scaleX(-1)}@media screen and (max-width: 44.9375em){.md-footer-nav__link--next{width:75%}}.md-footer-nav__title{position:relative;flex-grow:1;max-width:calc(100% - 2.4rem);padding:0 1rem;font-size:.9rem;line-height:2.4rem}.md-footer-nav__button{margin:.2rem;padding:.4rem}.md-footer-nav__direction{position:absolute;right:0;left:0;margin-top:-1rem;padding:0 1rem;font-size:.64rem;opacity:.7}.md-footer-meta{background-color:var(--md-footer-bg-color--dark)}.md-footer-meta__inner{display:flex;flex-wrap:wrap;justify-content:space-between;padding:.2rem}html .md-footer-meta.md-typeset a{color:var(--md-footer-fg-color--light)}html .md-footer-meta.md-typeset a:focus,html .md-footer-meta.md-typeset a:hover{color:var(--md-footer-fg-color)}.md-footer-copyright{width:100%;margin:auto .6rem;padding:.4rem 0;color:var(--md-footer-fg-color--lighter);font-size:.64rem}@media screen and (min-width: 45em){.md-footer-copyright{width:auto}}.md-footer-copyright__highlight{color:var(--md-footer-fg-color--light)}.md-footer-social{margin:0 .4rem;padding:.2rem 0 .6rem}@media screen and (min-width: 45em){.md-footer-social{padding:.6rem 0}}.md-footer-social__link{display:inline-block;width:1.6rem;height:1.6rem;text-align:center}.md-footer-social__link::before{line-height:1.9}.md-footer-social__link svg{max-height:.8rem;vertical-align:-25%;fill:currentColor}:root{--md-nav-icon--prev: svg-load(\"@mdi/svg/svg/arrow-left.svg\");--md-nav-icon--next: svg-load(\"@mdi/svg/svg/chevron-right.svg\");--md-toc-icon: svg-load(\"@mdi/svg/svg/table-of-contents.svg\")}.md-nav{font-size:.7rem;line-height:1.3}.md-nav__title{display:block;padding:0 .6rem;overflow:hidden;font-weight:700;text-overflow:ellipsis}.md-nav__title .md-nav__button{display:none}.md-nav__title .md-nav__button img{width:100%;height:auto}.md-nav__title .md-nav__button.md-logo img,.md-nav__title .md-nav__button.md-logo svg{display:block;width:2.4rem;height:2.4rem}.md-nav__title .md-nav__button.md-logo svg{fill:currentColor}.md-nav__list{margin:0;padding:0;list-style:none}.md-nav__item{padding:0 .6rem}.md-nav__item:last-child{padding-bottom:.6rem}.md-nav__item .md-nav__item{padding-right:0}[dir=rtl] .md-nav__item .md-nav__item{padding-right:.6rem;padding-left:0}.md-nav__item .md-nav__item:last-child{padding-bottom:0}.md-nav__link{display:block;margin-top:.625em;overflow:hidden;text-overflow:ellipsis;cursor:pointer;transition:color 125ms;scroll-snap-align:start}html .md-nav__link[for=__toc]{display:none}html .md-nav__link[for=__toc]~.md-nav{display:none}.md-nav__link[data-md-state=blur]{color:var(--md-default-fg-color--light)}.md-nav__item .md-nav__link--active{color:var(--md-typeset-a-color)}.md-nav__item--nested>.md-nav__link{color:inherit}.md-nav__link:focus,.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav__source{display:none}@media screen and (max-width: 76.1875em){.md-nav{background-color:var(--md-default-bg-color)}.md-nav--primary,.md-nav--primary .md-nav{position:absolute;top:0;right:0;left:0;z-index:1;display:flex;flex-direction:column;height:100%}.md-nav--primary .md-nav__title,.md-nav--primary .md-nav__item{font-size:.8rem;line-height:1.5}.md-nav--primary .md-nav__title{position:relative;height:5.6rem;padding:3rem .8rem .2rem;color:var(--md-default-fg-color--light);font-weight:400;line-height:2.4rem;white-space:nowrap;background-color:var(--md-default-fg-color--lightest);cursor:pointer}.md-nav--primary .md-nav__title .md-nav__icon{position:absolute;top:.4rem;left:.4rem;display:block;width:1.2rem;height:1.2rem;margin:.2rem}.md-nav--primary .md-nav__title .md-nav__icon::after{display:block;width:100%;height:100%;background-color:currentColor;mask-image:var(--md-nav-icon--prev);mask-repeat:no-repeat;content:\"\"}[dir=rtl] .md-nav--primary .md-nav__title .md-nav__icon{right:.4rem;left:initial}.md-nav--primary .md-nav__title~.md-nav__list{overflow-y:auto;background-color:var(--md-default-bg-color);box-shadow:0 .05rem 0 var(--md-default-fg-color--lightest) inset;scroll-snap-type:y mandatory;touch-action:pan-y}.md-nav--primary .md-nav__title~.md-nav__list>.md-nav__item:first-child{border-top:0}.md-nav--primary .md-nav__title[for=__drawer]{position:relative;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color)}.md-nav--primary .md-nav__title[for=__drawer] .md-nav__button{position:absolute;top:.2rem;left:.2rem;display:block;margin:.2rem;padding:.4rem;font-size:2.4rem}html [dir=rtl] .md-nav--primary .md-nav__title[for=__drawer] .md-nav__button{right:.2rem;left:initial}.md-nav--primary .md-nav__list{flex:1}.md-nav--primary .md-nav__item{padding:0;border-top:.05rem solid var(--md-default-fg-color--lightest)}[dir=rtl] .md-nav--primary .md-nav__item{padding:0}.md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:2.4rem}[dir=rtl] .md-nav--primary .md-nav__item--nested>.md-nav__link{padding-right:.8rem;padding-left:2.4rem}.md-nav--primary .md-nav__item--active>.md-nav__link{color:var(--md-typeset-a-color)}.md-nav--primary .md-nav__item--active>.md-nav__link:focus,.md-nav--primary .md-nav__item--active>.md-nav__link:hover{color:var(--md-accent-fg-color)}.md-nav--primary .md-nav__link{position:relative;margin-top:0;padding:.6rem .8rem}.md-nav--primary .md-nav__link .md-nav__icon{position:absolute;top:50%;right:.6rem;width:1.2rem;height:1.2rem;margin-top:-0.6rem;color:inherit;font-size:1.2rem}.md-nav--primary .md-nav__link .md-nav__icon::after{display:block;width:100%;height:100%;background-color:currentColor;mask-image:var(--md-nav-icon--next);mask-repeat:no-repeat;content:\"\"}[dir=rtl] .md-nav--primary .md-nav__link .md-nav__icon{right:initial;left:.6rem}[dir=rtl] .md-nav--primary .md-nav__icon::after{transform:scale(-1)}.md-nav--primary .md-nav--secondary .md-nav__link{position:static}.md-nav--primary .md-nav--secondary .md-nav{position:static;background-color:transparent}.md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-left:1.4rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav__link{padding-right:1.4rem;padding-left:initial}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-left:2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav__link{padding-right:2rem;padding-left:initial}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-left:2.6rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav__link{padding-right:2.6rem;padding-left:initial}.md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-left:3.2rem}[dir=rtl] .md-nav--primary .md-nav--secondary .md-nav .md-nav .md-nav .md-nav .md-nav__link{padding-right:3.2rem;padding-left:initial}.md-nav__toggle~.md-nav{display:flex;transform:translateX(100%);opacity:0;transition:transform 250ms cubic-bezier(0.8, 0, 0.6, 1),opacity 125ms 50ms}[dir=rtl] .md-nav__toggle~.md-nav{transform:translateX(-100%)}.md-nav__toggle:checked~.md-nav{transform:translateX(0);opacity:1;transition:transform 250ms cubic-bezier(0.4, 0, 0.2, 1),opacity 125ms 125ms}.md-nav__toggle:checked~.md-nav>.md-nav__list{backface-visibility:hidden}}@media screen and (max-width: 59.9375em){html .md-nav__link[for=__toc]{display:block;padding-right:2.4rem}html .md-nav__link[for=__toc]+.md-nav__link{display:none}html .md-nav__link[for=__toc] .md-icon::after{display:block;width:100%;height:100%;mask-image:var(--md-toc-icon);background-color:currentColor;content:\"\"}html .md-nav__link[for=__toc]~.md-nav{display:flex}html [dir=rtl] .md-nav__link{padding-right:.8rem;padding-left:2.4rem}.md-nav__source{display:block;padding:0 .2rem;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color--dark)}}@media screen and (min-width: 60em){.md-nav--secondary .md-nav__title[for=__toc]{scroll-snap-align:start}.md-nav--secondary .md-nav__title .md-nav__icon{display:none}}@media screen and (min-width: 76.25em){.md-nav{transition:max-height 250ms cubic-bezier(0.86, 0, 0.07, 1)}.md-nav--primary .md-nav__title[for=__drawer]{scroll-snap-align:start}.md-nav--primary .md-nav__title .md-nav__icon{display:none}.md-nav__toggle~.md-nav{display:none}.md-nav__toggle:checked~.md-nav{display:block}.md-nav__item--nested>.md-nav>.md-nav__title{display:none}.md-nav__icon{float:right;width:.9rem;height:.9rem;transition:transform 250ms}[dir=rtl] .md-nav__icon{float:left;transform:rotate(180deg)}.md-nav__icon::after{display:inline-block;width:100%;height:100%;vertical-align:-0.1rem;background-color:currentColor;mask-image:var(--md-nav-icon--next);mask-repeat:no-repeat;content:\"\"}.md-nav__item--nested .md-nav__toggle:checked~.md-nav__link .md-nav__icon{transform:rotate(90deg)}}:root{--md-search-result-icon: svg-load(\"@mdi/svg/svg/file-search-outline.svg\")}.md-search{position:relative}.no-js .md-search{display:none}@media screen and (min-width: 60em){.md-search{padding:.2rem 0}}.md-search__overlay{z-index:1;opacity:0}@media screen and (max-width: 59.9375em){.md-search__overlay{position:absolute;top:.2rem;left:-2.2rem;width:2rem;height:2rem;overflow:hidden;background-color:var(--md-default-bg-color);border-radius:1rem;transform-origin:center;transition:transform 300ms 100ms,opacity 200ms 200ms;pointer-events:none}[dir=rtl] .md-search__overlay{right:-2.2rem;left:initial}[data-md-toggle=search]:checked~.md-header .md-search__overlay{opacity:1;transition:transform 400ms,opacity 100ms}}@media screen and (max-width: 29.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(45)}}@media screen and (min-width: 30em)and (max-width: 44.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(60)}}@media screen and (min-width: 45em)and (max-width: 59.9375em){[data-md-toggle=search]:checked~.md-header .md-search__overlay{transform:scale(75)}}@media screen and (min-width: 60em){.md-search__overlay{position:fixed;top:0;left:0;width:0;height:0;background-color:rgba(0,0,0,.54);cursor:pointer;transition:width 0ms 250ms,height 0ms 250ms,opacity 250ms}[dir=rtl] .md-search__overlay{right:0;left:initial}[data-md-toggle=search]:checked~.md-header .md-search__overlay{width:100%;height:100%;opacity:1;transition:width 0ms,height 0ms,opacity 250ms}}.md-search__inner{backface-visibility:hidden}@media screen and (max-width: 59.9375em){.md-search__inner{position:fixed;top:0;left:100%;z-index:2;width:100%;height:100%;transform:translateX(5%);opacity:0;transition:right 0ms 300ms,left 0ms 300ms,transform 150ms 150ms cubic-bezier(0.4, 0, 0.2, 1),opacity 150ms 150ms}[data-md-toggle=search]:checked~.md-header .md-search__inner{left:0;transform:translateX(0);opacity:1;transition:right 0ms 0ms,left 0ms 0ms,transform 150ms 150ms cubic-bezier(0.1, 0.7, 0.1, 1),opacity 150ms 150ms}[dir=rtl] [data-md-toggle=search]:checked~.md-header .md-search__inner{right:0;left:initial}html [dir=rtl] .md-search__inner{right:100%;left:initial;transform:translateX(-5%)}}@media screen and (min-width: 60em){.md-search__inner{position:relative;float:right;width:11.7rem;padding:.1rem 0;transition:width 250ms cubic-bezier(0.1, 0.7, 0.1, 1)}[dir=rtl] .md-search__inner{float:left}}@media screen and (min-width: 60em)and (max-width: 76.1875em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:23.4rem}}@media screen and (min-width: 76.25em){[data-md-toggle=search]:checked~.md-header .md-search__inner{width:34.4rem}}.md-search__form{position:relative}@media screen and (min-width: 60em){.md-search__form{border-radius:.1rem}}.md-search__input{position:relative;z-index:2;padding:0 2.2rem 0 3.6rem;text-overflow:ellipsis;background-color:var(--md-default-bg-color);transition:color 250ms,background-color 250ms}[dir=rtl] .md-search__input{padding:0 3.6rem 0 2.2rem}.md-search__input::placeholder{transition:color 250ms}.md-search__input~.md-search__icon,.md-search__input::placeholder{color:var(--md-default-fg-color--light)}.md-search__input::-ms-clear{display:none}@media screen and (max-width: 59.9375em){.md-search__input{width:100%;height:2.4rem;font-size:.9rem}}@media screen and (min-width: 60em){.md-search__input{width:100%;height:1.8rem;padding-left:2.2rem;color:inherit;font-size:.8rem;background-color:rgba(0,0,0,.26);border-radius:.1rem}[dir=rtl] .md-search__input{padding-right:2.2rem}.md-search__input+.md-search__icon{color:var(--md-primary-bg-color)}.md-search__input::placeholder{color:var(--md-primary-bg-color--light)}.md-search__input:hover{background-color:rgba(255,255,255,.12)}[data-md-toggle=search]:checked~.md-header .md-search__input{color:var(--md-default-fg-color);text-overflow:clip;background-color:var(--md-default-bg-color);border-radius:.1rem .1rem 0 0}[data-md-toggle=search]:checked~.md-header .md-search__input+.md-search__icon,[data-md-toggle=search]:checked~.md-header .md-search__input::placeholder{color:var(--md-default-fg-color--light)}}.md-search__icon{position:absolute;z-index:2;width:1.2rem;height:1.2rem;cursor:pointer;transition:color 250ms,opacity 250ms}.md-search__icon:hover{opacity:.7}.md-search__icon[for=__search]{top:.3rem;left:.5rem}[dir=rtl] .md-search__icon[for=__search]{right:.5rem;left:initial}[dir=rtl] .md-search__icon[for=__search] svg{transform:scaleX(-1)}@media screen and (max-width: 59.9375em){.md-search__icon[for=__search]{top:.6rem;left:.8rem}[dir=rtl] .md-search__icon[for=__search]{right:.8rem;left:initial}.md-search__icon[for=__search] svg:first-child{display:none}}@media screen and (min-width: 60em){.md-search__icon[for=__search]{pointer-events:none}.md-search__icon[for=__search] svg:last-child{display:none}}.md-search__icon[type=reset]{top:.3rem;right:.5rem;transform:scale(0.75);opacity:0;transition:transform 150ms cubic-bezier(0.1, 0.7, 0.1, 1),opacity 150ms;pointer-events:none}[dir=rtl] .md-search__icon[type=reset]{right:initial;left:.5rem}@media screen and (max-width: 59.9375em){.md-search__icon[type=reset]{top:.6rem;right:.8rem}[dir=rtl] .md-search__icon[type=reset]{right:initial;left:.8rem}}[data-md-toggle=search]:checked~.md-header .md-search__input:not(:placeholder-shown)~.md-search__icon[type=reset]{transform:scale(1);opacity:1;pointer-events:initial}[data-md-toggle=search]:checked~.md-header .md-search__input:not(:placeholder-shown)~.md-search__icon[type=reset]:hover{opacity:.7}.md-search__output{position:absolute;z-index:1;width:100%;overflow:hidden;border-radius:0 0 .1rem .1rem}@media screen and (max-width: 59.9375em){.md-search__output{top:2.4rem;bottom:0}}@media screen and (min-width: 60em){.md-search__output{top:1.9rem;opacity:0;transition:opacity 400ms}[data-md-toggle=search]:checked~.md-header .md-search__output{box-shadow:0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.4);opacity:1}}.md-search__scrollwrap{height:100%;overflow-y:auto;background-color:var(--md-default-bg-color);backface-visibility:hidden;scroll-snap-type:y mandatory;touch-action:pan-y}@media(max-resolution: 1dppx){.md-search__scrollwrap{transform:translateZ(0)}}@media screen and (min-width: 60em)and (max-width: 76.1875em){.md-search__scrollwrap{width:23.4rem}}@media screen and (min-width: 76.25em){.md-search__scrollwrap{width:34.4rem}}@media screen and (min-width: 60em){.md-search__scrollwrap{max-height:0;scrollbar-width:thin;scrollbar-color:var(--md-default-fg-color--lighter) transparent}[data-md-toggle=search]:checked~.md-header .md-search__scrollwrap{max-height:75vh}.md-search__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-search__scrollwrap::-webkit-scrollbar{width:.2rem;height:.2rem}.md-search__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-search__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}}.md-search-result{color:var(--md-default-fg-color);word-break:break-word}.md-search-result__meta{padding:0 .8rem;color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.8rem;background-color:var(--md-default-fg-color--lightest);scroll-snap-align:start}@media screen and (min-width: 60em){.md-search-result__meta{padding-left:2.2rem}[dir=rtl] .md-search-result__meta{padding-right:2.2rem;padding-left:initial}}.md-search-result__list{margin:0;padding:0;list-style:none}.md-search-result__item{box-shadow:0 -0.05rem 0 var(--md-default-fg-color--lightest)}.md-search-result__item:first-child{box-shadow:none}.md-search-result__link{display:block;outline:none;transition:background 250ms;scroll-snap-align:start}.md-search-result__link:focus,.md-search-result__link:hover{background-color:var(--md-accent-fg-color--transparent)}.md-search-result__link:focus .md-search-result__article::before,.md-search-result__link:hover .md-search-result__article::before{opacity:.7}.md-search-result__link:last-child p:last-child{margin-bottom:.6rem}.md-search-result__more summary{display:block;padding:.75em .8rem;color:var(--md-typeset-a-color);font-size:.64rem;outline:0;cursor:pointer;transition:color 250ms,background-color 250ms;scroll-snap-align:start}.md-search-result__more summary:focus,.md-search-result__more summary:hover{color:var(--md-accent-fg-color);background-color:var(--md-accent-fg-color--transparent)}@media screen and (min-width: 60em){.md-search-result__more summary{padding-left:2.2rem}[dir=rtl] .md-search-result__more summary{padding-right:2.2rem;padding-left:.8rem}}.md-search-result__more summary::-webkit-details-marker{display:none}.md-search-result__more summary~*>*{opacity:.65}.md-search-result__article{position:relative;padding:0 .8rem;overflow:hidden}@media screen and (min-width: 60em){.md-search-result__article{padding-left:2.2rem}[dir=rtl] .md-search-result__article{padding-right:2.2rem;padding-left:.8rem}}.md-search-result__article--document .md-search-result__title{margin:.55rem 0;font-weight:400;font-size:.8rem;line-height:1.4}.md-search-result__icon{position:absolute;left:0;width:1.2rem;height:1.2rem;margin:.5rem;color:var(--md-default-fg-color--light)}.md-search-result__icon::after{display:inline-block;width:100%;height:100%;background-color:currentColor;mask-image:var(--md-search-result-icon);mask-repeat:no-repeat;content:\"\"}[dir=rtl] .md-search-result__icon{right:0;left:initial}[dir=rtl] .md-search-result__icon::after{transform:scaleX(-1)}@media screen and (max-width: 59.9375em){.md-search-result__icon{display:none}}.md-search-result__title{margin:.5em 0;font-weight:700;font-size:.64rem;line-height:1.6}.md-search-result__teaser{display:-webkit-box;max-height:2rem;margin:.5em 0;overflow:hidden;color:var(--md-default-fg-color--light);font-size:.64rem;line-height:1.6;text-overflow:ellipsis;-webkit-box-orient:vertical;-webkit-line-clamp:2}@media screen and (max-width: 44.9375em){.md-search-result__teaser{max-height:3rem;-webkit-line-clamp:3}}@media screen and (min-width: 60em)and (max-width: 76.1875em){.md-search-result__teaser{max-height:3rem;-webkit-line-clamp:3}}.md-search-result__teaser mark{background-color:transparent;border-bottom:.05rem solid var(--md-accent-fg-color)}.md-search-result__terms{margin:.5em 0;font-size:.64rem;font-style:italic}.md-search-result mark{color:var(--md-accent-fg-color);background-color:transparent}@keyframes md-sidebar__scrollwrap--hack{0%,99%{scroll-snap-type:none}100%{scroll-snap-type:y mandatory}}.md-sidebar{position:sticky;top:2.4rem;align-self:flex-start;width:12.1rem;padding:1.2rem 0;overflow:hidden}@media print{.md-sidebar{display:none}}@media screen and (max-width: 76.1875em){.md-sidebar--primary{position:fixed;top:0;left:-12.1rem;z-index:3;width:12.1rem;height:100%;background-color:var(--md-default-bg-color);transform:translateX(0);transition:transform 250ms cubic-bezier(0.4, 0, 0.2, 1),box-shadow 250ms}[dir=rtl] .md-sidebar--primary{right:-12.1rem;left:initial}[data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{box-shadow:0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12),0 5px 5px -3px rgba(0,0,0,.4);transform:translateX(12.1rem)}[dir=rtl] [data-md-toggle=drawer]:checked~.md-container .md-sidebar--primary{transform:translateX(-12.1rem)}.md-sidebar--primary .md-sidebar__scrollwrap{overflow:hidden}}.md-sidebar--secondary{display:none;order:2}@media screen and (min-width: 60em){.md-sidebar--secondary{display:block}.md-sidebar--secondary .md-sidebar__scrollwrap{touch-action:pan-y}}.md-sidebar__scrollwrap{max-height:100%;margin:0 .2rem;overflow-y:auto;backface-visibility:hidden;scrollbar-width:thin;scrollbar-color:var(--md-default-fg-color--lighter) transparent}.js .md-sidebar__scrollwrap{animation:md-sidebar__scrollwrap--hack 400ms forwards}@media screen and (max-width: 76.1875em){.md-sidebar--primary .md-sidebar__scrollwrap{position:absolute;top:0;right:0;bottom:0;left:0;margin:0;scroll-snap-type:none}}.md-sidebar__scrollwrap:hover{scrollbar-color:var(--md-accent-fg-color) transparent}.md-sidebar__scrollwrap::-webkit-scrollbar{width:.2rem;height:.2rem}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb{background-color:var(--md-default-fg-color--lighter)}.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover{background-color:var(--md-accent-fg-color)}@keyframes md-source__facts--done{0%{height:0}100%{height:.65rem}}@keyframes md-source__fact--done{0%{transform:translateY(100%);opacity:0}50%{opacity:0}100%{transform:translateY(0%);opacity:1}}.md-source{display:block;font-size:.65rem;line-height:1.2;white-space:nowrap;backface-visibility:hidden;transition:opacity 250ms}.md-source:hover{opacity:.7}.md-source__icon{display:inline-block;width:2.4rem;height:2.4rem;vertical-align:middle}.md-source__icon svg{margin-top:.6rem;margin-left:.6rem}[dir=rtl] .md-source__icon svg{margin-right:.6rem;margin-left:initial}.md-source__icon+.md-source__repository{margin-left:-2rem;padding-left:2rem}[dir=rtl] .md-source__icon+.md-source__repository{margin-right:-2rem;margin-left:initial;padding-right:2rem;padding-left:initial}.md-source__repository{display:inline-block;max-width:calc(100% - 1.2rem);margin-left:.6rem;overflow:hidden;font-weight:700;text-overflow:ellipsis;vertical-align:middle}.md-source__facts{margin:0;padding:0;overflow:hidden;font-weight:700;font-size:.55rem;list-style-type:none;opacity:.75}[data-md-state=done] .md-source__facts{animation:md-source__facts--done 250ms ease-in}.md-source__fact{float:left}[dir=rtl] .md-source__fact{float:right}[data-md-state=done] .md-source__fact{animation:md-source__fact--done 400ms ease-out}.md-source__fact::before{margin:0 .1rem;content:\"·\"}.md-source__fact:first-child::before{display:none}.md-tabs{width:100%;overflow:auto;color:var(--md-primary-bg-color);background-color:var(--md-primary-fg-color);transition:background 250ms}.no-js .md-tabs{transition:none}@media screen and (max-width: 76.1875em){.md-tabs{display:none}}@media print{.md-tabs{display:none}}.md-tabs__list{margin:0;margin-left:.2rem;padding:0;white-space:nowrap;list-style:none;contain:content}[dir=rtl] .md-tabs__list{margin-right:.2rem;margin-left:initial}.md-tabs__item{display:inline-block;height:2.4rem;padding-right:.6rem;padding-left:.6rem}.md-tabs__link{display:block;margin-top:.8rem;font-size:.7rem;opacity:.7;transition:transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),opacity 250ms}.no-js .md-tabs__link{transition:none}.md-tabs__link--active,.md-tabs__link:hover{color:inherit;opacity:1}.md-tabs__item:nth-child(2) .md-tabs__link{transition-delay:20ms}.md-tabs__item:nth-child(3) .md-tabs__link{transition-delay:40ms}.md-tabs__item:nth-child(4) .md-tabs__link{transition-delay:60ms}.md-tabs__item:nth-child(5) .md-tabs__link{transition-delay:80ms}.md-tabs__item:nth-child(6) .md-tabs__link{transition-delay:100ms}.md-tabs__item:nth-child(7) .md-tabs__link{transition-delay:120ms}.md-tabs__item:nth-child(8) .md-tabs__link{transition-delay:140ms}.md-tabs__item:nth-child(9) .md-tabs__link{transition-delay:160ms}.md-tabs__item:nth-child(10) .md-tabs__link{transition-delay:180ms}.md-tabs__item:nth-child(11) .md-tabs__link{transition-delay:200ms}.md-tabs__item:nth-child(12) .md-tabs__link{transition-delay:220ms}.md-tabs__item:nth-child(13) .md-tabs__link{transition-delay:240ms}.md-tabs__item:nth-child(14) .md-tabs__link{transition-delay:260ms}.md-tabs__item:nth-child(15) .md-tabs__link{transition-delay:280ms}.md-tabs__item:nth-child(16) .md-tabs__link{transition-delay:300ms}.md-tabs[data-md-state=hidden]{pointer-events:none}.md-tabs[data-md-state=hidden] .md-tabs__link{transform:translateY(50%);opacity:0;transition:color 250ms,transform 0ms 400ms,opacity 100ms}@media screen and (min-width: 76.25em){.md-tabs~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--nested{display:none}.md-tabs--active~.md-main .md-nav--primary .md-nav__title{display:block;padding:0 .6rem;pointer-events:none;scroll-snap-align:start}.md-tabs--active~.md-main .md-nav--primary .md-nav__title[for=__drawer]{display:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item{display:none}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--active{display:block;padding:0}.md-tabs--active~.md-main .md-nav--primary>.md-nav__list>.md-nav__item--active>.md-nav__link{display:none}.md-tabs--active~.md-main .md-nav[data-md-level=\"1\"]{display:block}.md-tabs--active~.md-main .md-nav[data-md-level=\"1\"]>.md-nav__list>.md-nav__item{padding:0 .6rem}.md-tabs--active~.md-main .md-nav[data-md-level=\"1\"]>.md-nav__list>.md-nav__item:last-child{padding-bottom:.6rem}.md-tabs--active~.md-main .md-nav[data-md-level=\"1\"]>.md-nav__list>.md-nav__item:last-child .md-nav__item{padding-bottom:0}.md-tabs--active~.md-main .md-nav[data-md-level=\"1\"] .md-nav .md-nav__title{display:none}}:root{--md-admonition-icon--note: svg-load(\"@mdi/svg/svg/pencil.svg\");--md-admonition-icon--abstract: svg-load(\"@mdi/svg/svg/text-subject.svg\");--md-admonition-icon--info: svg-load(\"@mdi/svg/svg/information.svg\");--md-admonition-icon--tip: svg-load(\"@mdi/svg/svg/fire.svg\");--md-admonition-icon--success: svg-load(\"@mdi/svg/svg/check-circle.svg\");--md-admonition-icon--question: svg-load(\"@mdi/svg/svg/help-circle.svg\");--md-admonition-icon--warning: svg-load(\"@mdi/svg/svg/alert.svg\");--md-admonition-icon--failure: svg-load(\"@mdi/svg/svg/close-circle.svg\");--md-admonition-icon--danger: svg-load(\"@mdi/svg/svg/flash-circle.svg\");--md-admonition-icon--bug: svg-load(\"@mdi/svg/svg/bug.svg\");--md-admonition-icon--example: svg-load(\"@mdi/svg/svg/format-list-numbered.svg\");--md-admonition-icon--quote: svg-load(\"@mdi/svg/svg/format-quote-close.svg\")}.md-typeset .admonition,.md-typeset details{margin:1.5625em 0;padding:0 .6rem;overflow:hidden;color:var(--md-admonition-fg-color);font-size:.64rem;page-break-inside:avoid;background-color:var(--md-admonition-bg-color);border-left:.2rem solid #448aff;border-radius:.1rem;box-shadow:0 .2rem .5rem rgba(0,0,0,.05),0 0 .05rem rgba(0,0,0,.1)}[dir=rtl] .md-typeset .admonition,[dir=rtl] .md-typeset details{border-right:.2rem solid #448aff;border-left:none}@media print{.md-typeset .admonition,.md-typeset details{box-shadow:none}}html .md-typeset .admonition>:last-child,html .md-typeset details>:last-child{margin-bottom:.6rem}.md-typeset .admonition .admonition,.md-typeset details .admonition,.md-typeset .admonition details,.md-typeset details details{margin:1em 0}.md-typeset .admonition .md-typeset__scrollwrap,.md-typeset details .md-typeset__scrollwrap{margin:1em -0.6rem}.md-typeset .admonition .md-typeset__table,.md-typeset details .md-typeset__table{padding:0 .6rem}.md-typeset .admonition>.tabbed-set:only-child,.md-typeset details>.tabbed-set:only-child{margin-top:0}.md-typeset .admonition-title,.md-typeset summary{position:relative;margin:0 -0.6rem 0 -0.8rem;padding:.4rem .6rem .4rem 2.2rem;font-weight:700;background-color:rgba(68,138,255,.1)}[dir=rtl] .md-typeset .admonition-title,[dir=rtl] .md-typeset summary{margin:0 -0.8rem 0 -0.6rem;padding:.4rem 2rem .4rem .6rem}html .md-typeset .admonition-title:last-child,html .md-typeset summary:last-child{margin-bottom:0}.md-typeset .admonition-title::before,.md-typeset summary::before{position:absolute;left:.8rem;width:1rem;height:1rem;background-color:#448aff;mask-image:var(--md-admonition-icon--note);mask-repeat:no-repeat;content:\"\"}[dir=rtl] .md-typeset .admonition-title::before,[dir=rtl] .md-typeset summary::before{right:.8rem;left:initial}.md-typeset .admonition-title code,.md-typeset summary code{margin:initial;padding:initial;color:currentColor;background-color:transparent;border-radius:initial;box-shadow:none}.md-typeset .admonition-title+.tabbed-set:last-child,.md-typeset summary+.tabbed-set:last-child{margin-top:0}.md-typeset .admonition.note,.md-typeset details.note{border-color:#448aff}.md-typeset .note>.admonition-title,.md-typeset .note>summary{background-color:rgba(68,138,255,.1)}.md-typeset .note>.admonition-title::before,.md-typeset .note>summary::before{background-color:#448aff;mask-image:var(--md-admonition-icon--note);mask-repeat:no-repeat}.md-typeset .admonition.abstract,.md-typeset details.abstract,.md-typeset .admonition.tldr,.md-typeset details.tldr,.md-typeset .admonition.summary,.md-typeset details.summary{border-color:#00b0ff}.md-typeset .abstract>.admonition-title,.md-typeset .abstract>summary,.md-typeset .tldr>.admonition-title,.md-typeset .tldr>summary,.md-typeset .summary>.admonition-title,.md-typeset .summary>summary{background-color:rgba(0,176,255,.1)}.md-typeset .abstract>.admonition-title::before,.md-typeset .abstract>summary::before,.md-typeset .tldr>.admonition-title::before,.md-typeset .tldr>summary::before,.md-typeset .summary>.admonition-title::before,.md-typeset .summary>summary::before{background-color:#00b0ff;mask-image:var(--md-admonition-icon--abstract);mask-repeat:no-repeat}.md-typeset .admonition.info,.md-typeset details.info,.md-typeset .admonition.todo,.md-typeset details.todo{border-color:#00b8d4}.md-typeset .info>.admonition-title,.md-typeset .info>summary,.md-typeset .todo>.admonition-title,.md-typeset .todo>summary{background-color:rgba(0,184,212,.1)}.md-typeset .info>.admonition-title::before,.md-typeset .info>summary::before,.md-typeset .todo>.admonition-title::before,.md-typeset .todo>summary::before{background-color:#00b8d4;mask-image:var(--md-admonition-icon--info);mask-repeat:no-repeat}.md-typeset .admonition.tip,.md-typeset details.tip,.md-typeset .admonition.important,.md-typeset details.important,.md-typeset .admonition.hint,.md-typeset details.hint{border-color:#00bfa5}.md-typeset .tip>.admonition-title,.md-typeset .tip>summary,.md-typeset .important>.admonition-title,.md-typeset .important>summary,.md-typeset .hint>.admonition-title,.md-typeset .hint>summary{background-color:rgba(0,191,165,.1)}.md-typeset .tip>.admonition-title::before,.md-typeset .tip>summary::before,.md-typeset .important>.admonition-title::before,.md-typeset .important>summary::before,.md-typeset .hint>.admonition-title::before,.md-typeset .hint>summary::before{background-color:#00bfa5;mask-image:var(--md-admonition-icon--tip);mask-repeat:no-repeat}.md-typeset .admonition.success,.md-typeset details.success,.md-typeset .admonition.done,.md-typeset details.done,.md-typeset .admonition.check,.md-typeset details.check{border-color:#00c853}.md-typeset .success>.admonition-title,.md-typeset .success>summary,.md-typeset .done>.admonition-title,.md-typeset .done>summary,.md-typeset .check>.admonition-title,.md-typeset .check>summary{background-color:rgba(0,200,83,.1)}.md-typeset .success>.admonition-title::before,.md-typeset .success>summary::before,.md-typeset .done>.admonition-title::before,.md-typeset .done>summary::before,.md-typeset .check>.admonition-title::before,.md-typeset .check>summary::before{background-color:#00c853;mask-image:var(--md-admonition-icon--success);mask-repeat:no-repeat}.md-typeset .admonition.question,.md-typeset details.question,.md-typeset .admonition.faq,.md-typeset details.faq,.md-typeset .admonition.help,.md-typeset details.help{border-color:#64dd17}.md-typeset .question>.admonition-title,.md-typeset .question>summary,.md-typeset .faq>.admonition-title,.md-typeset .faq>summary,.md-typeset .help>.admonition-title,.md-typeset .help>summary{background-color:rgba(100,221,23,.1)}.md-typeset .question>.admonition-title::before,.md-typeset .question>summary::before,.md-typeset .faq>.admonition-title::before,.md-typeset .faq>summary::before,.md-typeset .help>.admonition-title::before,.md-typeset .help>summary::before{background-color:#64dd17;mask-image:var(--md-admonition-icon--question);mask-repeat:no-repeat}.md-typeset .admonition.warning,.md-typeset details.warning,.md-typeset .admonition.attention,.md-typeset details.attention,.md-typeset .admonition.caution,.md-typeset details.caution{border-color:#ff9100}.md-typeset .warning>.admonition-title,.md-typeset .warning>summary,.md-typeset .attention>.admonition-title,.md-typeset .attention>summary,.md-typeset .caution>.admonition-title,.md-typeset .caution>summary{background-color:rgba(255,145,0,.1)}.md-typeset .warning>.admonition-title::before,.md-typeset .warning>summary::before,.md-typeset .attention>.admonition-title::before,.md-typeset .attention>summary::before,.md-typeset .caution>.admonition-title::before,.md-typeset .caution>summary::before{background-color:#ff9100;mask-image:var(--md-admonition-icon--warning);mask-repeat:no-repeat}.md-typeset .admonition.failure,.md-typeset details.failure,.md-typeset .admonition.missing,.md-typeset details.missing,.md-typeset .admonition.fail,.md-typeset details.fail{border-color:#ff5252}.md-typeset .failure>.admonition-title,.md-typeset .failure>summary,.md-typeset .missing>.admonition-title,.md-typeset .missing>summary,.md-typeset .fail>.admonition-title,.md-typeset .fail>summary{background-color:rgba(255,82,82,.1)}.md-typeset .failure>.admonition-title::before,.md-typeset .failure>summary::before,.md-typeset .missing>.admonition-title::before,.md-typeset .missing>summary::before,.md-typeset .fail>.admonition-title::before,.md-typeset .fail>summary::before{background-color:#ff5252;mask-image:var(--md-admonition-icon--failure);mask-repeat:no-repeat}.md-typeset .admonition.danger,.md-typeset details.danger,.md-typeset .admonition.error,.md-typeset details.error{border-color:#ff1744}.md-typeset .danger>.admonition-title,.md-typeset .danger>summary,.md-typeset .error>.admonition-title,.md-typeset .error>summary{background-color:rgba(255,23,68,.1)}.md-typeset .danger>.admonition-title::before,.md-typeset .danger>summary::before,.md-typeset .error>.admonition-title::before,.md-typeset .error>summary::before{background-color:#ff1744;mask-image:var(--md-admonition-icon--danger);mask-repeat:no-repeat}.md-typeset .admonition.bug,.md-typeset details.bug{border-color:#f50057}.md-typeset .bug>.admonition-title,.md-typeset .bug>summary{background-color:rgba(245,0,87,.1)}.md-typeset .bug>.admonition-title::before,.md-typeset .bug>summary::before{background-color:#f50057;mask-image:var(--md-admonition-icon--bug);mask-repeat:no-repeat}.md-typeset .admonition.example,.md-typeset details.example{border-color:#651fff}.md-typeset .example>.admonition-title,.md-typeset .example>summary{background-color:rgba(101,31,255,.1)}.md-typeset .example>.admonition-title::before,.md-typeset .example>summary::before{background-color:#651fff;mask-image:var(--md-admonition-icon--example);mask-repeat:no-repeat}.md-typeset .admonition.quote,.md-typeset details.quote,.md-typeset .admonition.cite,.md-typeset details.cite{border-color:#9e9e9e}.md-typeset .quote>.admonition-title,.md-typeset .quote>summary,.md-typeset .cite>.admonition-title,.md-typeset .cite>summary{background-color:rgba(158,158,158,.1)}.md-typeset .quote>.admonition-title::before,.md-typeset .quote>summary::before,.md-typeset .cite>.admonition-title::before,.md-typeset .cite>summary::before{background-color:#9e9e9e;mask-image:var(--md-admonition-icon--quote);mask-repeat:no-repeat}.codehilite .o,.highlight .o,.codehilite .ow,.highlight .ow{color:var(--md-code-hl-operator-color)}.codehilite .p,.highlight .p{color:var(--md-code-hl-punctuation-color)}.codehilite .cpf,.highlight .cpf,.codehilite .l,.highlight .l,.codehilite .s,.highlight .s,.codehilite .sb,.highlight .sb,.codehilite .sc,.highlight .sc,.codehilite .s2,.highlight .s2,.codehilite .si,.highlight .si,.codehilite .s1,.highlight .s1,.codehilite .ss,.highlight .ss{color:var(--md-code-hl-string-color)}.codehilite .cp,.highlight .cp,.codehilite .se,.highlight .se,.codehilite .sh,.highlight .sh,.codehilite .sr,.highlight .sr,.codehilite .sx,.highlight .sx{color:var(--md-code-hl-special-color)}.codehilite .m,.highlight .m,.codehilite .mf,.highlight .mf,.codehilite .mh,.highlight .mh,.codehilite .mi,.highlight .mi,.codehilite .il,.highlight .il,.codehilite .mo,.highlight .mo{color:var(--md-code-hl-number-color)}.codehilite .k,.highlight .k,.codehilite .kd,.highlight .kd,.codehilite .kn,.highlight .kn,.codehilite .kp,.highlight .kp,.codehilite .kr,.highlight .kr,.codehilite .kt,.highlight .kt{color:var(--md-code-hl-keyword-color)}.codehilite .kc,.highlight .kc,.codehilite .n,.highlight .n{color:var(--md-code-hl-name-color)}.codehilite .no,.highlight .no,.codehilite .nb,.highlight .nb,.codehilite .bp,.highlight .bp{color:var(--md-code-hl-constant-color)}.codehilite .nc,.highlight .nc,.codehilite .ne,.highlight .ne,.codehilite .nf,.highlight .nf,.codehilite .nn,.highlight .nn{color:var(--md-code-hl-function-color)}.codehilite .nd,.highlight .nd,.codehilite .ni,.highlight .ni,.codehilite .nl,.highlight .nl,.codehilite .nt,.highlight .nt{color:var(--md-code-hl-keyword-color)}.codehilite .c,.highlight .c,.codehilite .cm,.highlight .cm,.codehilite .c1,.highlight .c1,.codehilite .ch,.highlight .ch,.codehilite .cs,.highlight .cs,.codehilite .sd,.highlight .sd{color:var(--md-code-hl-comment-color)}.codehilite .na,.highlight .na,.codehilite .nv,.highlight .nv,.codehilite .vc,.highlight .vc,.codehilite .vg,.highlight .vg,.codehilite .vi,.highlight .vi{color:var(--md-code-hl-variable-color)}.codehilite .ge,.highlight .ge,.codehilite .gr,.highlight .gr,.codehilite .gh,.highlight .gh,.codehilite .go,.highlight .go,.codehilite .gp,.highlight .gp,.codehilite .gs,.highlight .gs,.codehilite .gu,.highlight .gu,.codehilite .gt,.highlight .gt{color:var(--md-code-hl-generic-color)}.codehilite .gd,.highlight .gd,.codehilite .gi,.highlight .gi{margin:0 -0.125em;padding:0 .125em;border-radius:.1rem}.codehilite .gd,.highlight .gd{background-color:var(--md-typeset-del-color)}.codehilite .gi,.highlight .gi{background-color:var(--md-typeset-ins-color)}.codehilite .hll,.highlight .hll{display:block;margin:0 -1.1764705882em;padding:0 1.1764705882em;background-color:var(--md-code-hl-color)}.codehilitetable,.highlighttable{display:block;overflow:hidden}.codehilitetable tbody,.highlighttable tbody,.codehilitetable td,.highlighttable td{display:block;padding:0}.codehilitetable tr,.highlighttable tr{display:flex}.codehilitetable pre,.highlighttable pre{margin:0}.codehilitetable .linenos,.highlighttable .linenos{padding:.525rem 1.1764705882em;padding-right:0;font-size:.85em;background-color:var(--md-code-bg-color);user-select:none}.codehilitetable .linenodiv,.highlighttable .linenodiv{padding-right:.5882352941em;box-shadow:-0.05rem 0 var(--md-default-fg-color--lighter) inset}.codehilitetable .linenodiv pre,.highlighttable .linenodiv pre{color:var(--md-default-fg-color--light);text-align:right}.codehilitetable .code,.highlighttable .code{flex:1;overflow:hidden}.md-typeset .codehilitetable,.md-typeset .highlighttable{margin:1em 0;direction:ltr;border-radius:.1rem}.md-typeset .codehilitetable code,.md-typeset .highlighttable code{border-radius:0}@media screen and (max-width: 44.9375em){.md-typeset>.codehilite,.md-typeset>.highlight{margin:1em -0.8rem}.md-typeset>.codehilite .hll,.md-typeset>.highlight .hll{margin:0 -0.8rem;padding:0 .8rem}.md-typeset>.codehilite code,.md-typeset>.highlight code{border-radius:0}.md-typeset>.codehilitetable,.md-typeset>.highlighttable{margin:1em -0.8rem;border-radius:0}.md-typeset>.codehilitetable .hll,.md-typeset>.highlighttable .hll{margin:0 -0.8rem;padding:0 .8rem}}:root{--md-footnotes-icon: svg-load(\"@mdi/svg/svg/keyboard-return.svg\")}.md-typeset [id^=\"fnref:\"]{display:inline-block}.md-typeset [id^=\"fnref:\"]:target{margin-top:-3.8rem;padding-top:3.8rem;pointer-events:none;scroll-margin-top:initial}.md-typeset [id^=\"fn:\"]::before{display:none;height:0;content:\"\"}.md-typeset [id^=\"fn:\"]:target{scroll-margin-top:initial}.md-typeset [id^=\"fn:\"]:target::before{display:block;margin-top:-3.5rem;padding-top:3.5rem;pointer-events:none}.md-typeset .footnote{color:var(--md-default-fg-color--light);font-size:.64rem}.md-typeset .footnote ol{margin-left:0}.md-typeset .footnote li{transition:color 125ms}.md-typeset .footnote li:target{color:var(--md-default-fg-color)}.md-typeset .footnote li :first-child{margin-top:0}.md-typeset .footnote li:hover .footnote-backref,.md-typeset .footnote li:target .footnote-backref{transform:translateX(0);opacity:1}.md-typeset .footnote li:hover .footnote-backref:hover{color:var(--md-accent-fg-color)}.md-typeset .footnote-ref{display:inline-block;pointer-events:initial}.md-typeset .footnote-backref{display:inline-block;color:var(--md-typeset-a-color);font-size:0;vertical-align:text-bottom;transform:translateX(0.25rem);opacity:0;transition:color 250ms,transform 250ms 250ms,opacity 125ms 250ms}[dir=rtl] .md-typeset .footnote-backref{transform:translateX(-0.25rem)}.md-typeset .footnote-backref::before{display:inline-block;width:.8rem;height:.8rem;background-color:currentColor;mask-image:var(--md-footnotes-icon);mask-repeat:no-repeat;content:\"\"}[dir=rtl] .md-typeset .footnote-backref::before svg{transform:scaleX(-1)}@media print{.md-typeset .footnote-backref{color:var(--md-typeset-a-color);transform:translateX(0);opacity:1}}.md-typeset .headerlink{display:inline-block;margin-left:.5rem;visibility:hidden;opacity:0;transition:color 250ms,visibility 0ms 500ms,opacity 125ms}[dir=rtl] .md-typeset .headerlink{margin-right:.5rem;margin-left:initial}html body .md-typeset .headerlink{color:var(--md-default-fg-color--lighter)}@media print{.md-typeset .headerlink{display:none}}.md-typeset :hover>.headerlink,.md-typeset :target>.headerlink,.md-typeset .headerlink:focus{visibility:visible;opacity:1;transition:color 250ms,visibility 0ms,opacity 125ms}.md-typeset :target>.headerlink,.md-typeset .headerlink:focus,.md-typeset .headerlink:hover{color:var(--md-accent-fg-color)}.md-typeset :target{scroll-margin-top:3.6rem}.md-typeset h3[id]:target,.md-typeset h2[id]:target,.md-typeset h1[id]:target{scroll-margin-top:initial}.md-typeset h3[id]::before,.md-typeset h2[id]::before,.md-typeset h1[id]::before{display:block;margin-top:-0.4rem;padding-top:.4rem;content:\"\"}.md-typeset h3[id]:target::before,.md-typeset h2[id]:target::before,.md-typeset h1[id]:target::before{margin-top:-3.4rem;padding-top:3.4rem}.md-typeset h4[id]:target{scroll-margin-top:initial}.md-typeset h4[id]::before{display:block;margin-top:-0.45rem;padding-top:.45rem;content:\"\"}.md-typeset h4[id]:target::before{margin-top:-3.45rem;padding-top:3.45rem}.md-typeset h6[id]:target,.md-typeset h5[id]:target{scroll-margin-top:initial}.md-typeset h6[id]::before,.md-typeset h5[id]::before{display:block;margin-top:-0.6rem;padding-top:.6rem;content:\"\"}.md-typeset h6[id]:target::before,.md-typeset h5[id]:target::before{margin-top:-3.6rem;padding-top:3.6rem}.md-typeset div.arithmatex{overflow-x:scroll}@media screen and (max-width: 44.9375em){.md-typeset div.arithmatex{margin:0 -0.8rem}}.md-typeset div.arithmatex>*{width:min-content;margin:1em auto !important;padding:0 .8rem;overflow:auto;touch-action:auto}.md-typeset del.critic,.md-typeset ins.critic,.md-typeset .critic.comment{box-decoration-break:clone}.md-typeset del.critic{background-color:var(--md-typeset-del-color)}.md-typeset ins.critic{background-color:var(--md-typeset-ins-color)}.md-typeset .critic.comment{color:var(--md-code-hl-comment-color)}.md-typeset .critic.comment::before{content:\"/* \"}.md-typeset .critic.comment::after{content:\" */\"}.md-typeset .critic.block{display:block;margin:1em 0;padding-right:.8rem;padding-left:.8rem;overflow:auto;box-shadow:none}.md-typeset .critic.block :first-child{margin-top:.5em}.md-typeset .critic.block :last-child{margin-bottom:.5em}:root{--md-details-icon: svg-load(\"@mdi/svg/svg/chevron-right.svg\")}.md-typeset details{display:block;padding-top:0;overflow:visible}.md-typeset details[open]>summary::after{transform:rotate(90deg)}.md-typeset details:not([open]){padding-bottom:0}.md-typeset details:not([open])>summary{border-radius:.1rem}.md-typeset details::after{display:table;content:\"\"}.md-typeset summary{display:block;min-height:1rem;padding:.4rem 1.8rem .4rem 2.2rem;border-top-left-radius:.1rem;border-top-right-radius:.1rem;cursor:pointer}.md-typeset summary:not(.focus-visible){outline:none;-webkit-tap-highlight-color:transparent}[dir=rtl] .md-typeset summary{padding:.4rem 2.2rem .4rem 1.8rem}.md-typeset summary::-webkit-details-marker{display:none}.md-typeset summary::after{position:absolute;top:.4rem;right:.4rem;width:1rem;height:1rem;background-color:currentColor;mask-image:var(--md-details-icon);mask-repeat:no-repeat;transform:rotate(0deg);transition:transform 250ms;content:\"\"}[dir=rtl] .md-typeset summary::after{right:initial;left:.4rem;transform:rotate(180deg)}.md-typeset img.emojione,.md-typeset img.twemoji,.md-typeset img.gemoji{width:1.125em;max-height:100%;vertical-align:-15%}.md-typeset span.twemoji{display:inline-block;height:1.125em;vertical-align:text-top}.md-typeset span.twemoji svg{width:1.125em;max-height:100%;fill:currentColor}.highlight [data-linenos]::before{position:sticky;left:-1.1764705882em;float:left;margin-right:1.1764705882em;margin-left:-1.1764705882em;padding-left:1.1764705882em;color:var(--md-default-fg-color--light);background-color:var(--md-code-bg-color);box-shadow:-0.05rem 0 var(--md-default-fg-color--lighter) inset;content:attr(data-linenos);user-select:none}.md-typeset .keys kbd::before,.md-typeset .keys kbd::after{position:relative;margin:0;color:inherit;-moz-osx-font-smoothing:initial;-webkit-font-smoothing:initial}.md-typeset .keys span{padding:0 .2em;color:var(--md-default-fg-color--light)}.md-typeset .keys .key-alt::before{padding-right:.4em;content:\"⎇\"}.md-typeset .keys .key-left-alt::before{padding-right:.4em;content:\"⎇\"}.md-typeset .keys .key-right-alt::before{padding-right:.4em;content:\"⎇\"}.md-typeset .keys .key-command::before{padding-right:.4em;content:\"⌘\"}.md-typeset .keys .key-left-command::before{padding-right:.4em;content:\"⌘\"}.md-typeset .keys .key-right-command::before{padding-right:.4em;content:\"⌘\"}.md-typeset .keys .key-control::before{padding-right:.4em;content:\"⌃\"}.md-typeset .keys .key-left-control::before{padding-right:.4em;content:\"⌃\"}.md-typeset .keys .key-right-control::before{padding-right:.4em;content:\"⌃\"}.md-typeset .keys .key-meta::before{padding-right:.4em;content:\"◆\"}.md-typeset .keys .key-left-meta::before{padding-right:.4em;content:\"◆\"}.md-typeset .keys .key-right-meta::before{padding-right:.4em;content:\"◆\"}.md-typeset .keys .key-option::before{padding-right:.4em;content:\"⌥\"}.md-typeset .keys .key-left-option::before{padding-right:.4em;content:\"⌥\"}.md-typeset .keys .key-right-option::before{padding-right:.4em;content:\"⌥\"}.md-typeset .keys .key-shift::before{padding-right:.4em;content:\"⇧\"}.md-typeset .keys .key-left-shift::before{padding-right:.4em;content:\"⇧\"}.md-typeset .keys .key-right-shift::before{padding-right:.4em;content:\"⇧\"}.md-typeset .keys .key-super::before{padding-right:.4em;content:\"❖\"}.md-typeset .keys .key-left-super::before{padding-right:.4em;content:\"❖\"}.md-typeset .keys .key-right-super::before{padding-right:.4em;content:\"❖\"}.md-typeset .keys .key-windows::before{padding-right:.4em;content:\"⊞\"}.md-typeset .keys .key-left-windows::before{padding-right:.4em;content:\"⊞\"}.md-typeset .keys .key-right-windows::before{padding-right:.4em;content:\"⊞\"}.md-typeset .keys .key-arrow-down::before{padding-right:.4em;content:\"↓\"}.md-typeset .keys .key-arrow-left::before{padding-right:.4em;content:\"←\"}.md-typeset .keys .key-arrow-right::before{padding-right:.4em;content:\"→\"}.md-typeset .keys .key-arrow-up::before{padding-right:.4em;content:\"↑\"}.md-typeset .keys .key-backspace::before{padding-right:.4em;content:\"⌫\"}.md-typeset .keys .key-backtab::before{padding-right:.4em;content:\"⇤\"}.md-typeset .keys .key-caps-lock::before{padding-right:.4em;content:\"⇪\"}.md-typeset .keys .key-clear::before{padding-right:.4em;content:\"⌧\"}.md-typeset .keys .key-context-menu::before{padding-right:.4em;content:\"☰\"}.md-typeset .keys .key-delete::before{padding-right:.4em;content:\"⌦\"}.md-typeset .keys .key-eject::before{padding-right:.4em;content:\"⏏\"}.md-typeset .keys .key-end::before{padding-right:.4em;content:\"⤓\"}.md-typeset .keys .key-escape::before{padding-right:.4em;content:\"⎋\"}.md-typeset .keys .key-home::before{padding-right:.4em;content:\"⤒\"}.md-typeset .keys .key-insert::before{padding-right:.4em;content:\"⎀\"}.md-typeset .keys .key-page-down::before{padding-right:.4em;content:\"⇟\"}.md-typeset .keys .key-page-up::before{padding-right:.4em;content:\"⇞\"}.md-typeset .keys .key-print-screen::before{padding-right:.4em;content:\"⎙\"}.md-typeset .keys .key-tab::after{padding-left:.4em;content:\"⇥\"}.md-typeset .keys .key-num-enter::after{padding-left:.4em;content:\"⌤\"}.md-typeset .keys .key-enter::after{padding-left:.4em;content:\"⏎\"}.md-typeset .tabbed-content{display:none;order:99;width:100%;box-shadow:0 -0.05rem var(--md-default-fg-color--lightest)}.md-typeset .tabbed-content>pre:only-child,.md-typeset .tabbed-content>.codehilite:only-child pre,.md-typeset .tabbed-content>.codehilitetable:only-child,.md-typeset .tabbed-content>.highlight:only-child pre,.md-typeset .tabbed-content>.highlighttable:only-child{margin:0}.md-typeset .tabbed-content>pre:only-child>code,.md-typeset .tabbed-content>.codehilite:only-child pre>code,.md-typeset .tabbed-content>.codehilitetable:only-child>code,.md-typeset .tabbed-content>.highlight:only-child pre>code,.md-typeset .tabbed-content>.highlighttable:only-child>code{border-top-left-radius:0;border-top-right-radius:0}.md-typeset .tabbed-content>.tabbed-set{margin:0}.md-typeset .tabbed-set{position:relative;display:flex;flex-wrap:wrap;margin:1em 0;border-radius:.1rem}.md-typeset .tabbed-set>input{position:absolute;width:0;height:0;opacity:0}.md-typeset .tabbed-set>input:checked+label{color:var(--md-accent-fg-color);border-color:var(--md-accent-fg-color)}.md-typeset .tabbed-set>input:checked+label+.tabbed-content{display:block}.md-typeset .tabbed-set>input:focus+label{outline-style:auto}.md-typeset .tabbed-set>input:not(.focus-visible)+label{outline:none;-webkit-tap-highlight-color:transparent}.md-typeset .tabbed-set>label{z-index:1;width:auto;padding:.9375em 1.25em .78125em;color:var(--md-default-fg-color--light);font-weight:700;font-size:.64rem;border-bottom:.1rem solid transparent;cursor:pointer;transition:color 250ms}html .md-typeset .tabbed-set>label:hover{color:var(--md-accent-fg-color)}:root{--md-tasklist-icon: svg-load( \"@primer/octicons/build/svg/check-circle-fill-24.svg\" );--md-tasklist-icon--checked: svg-load( \"@primer/octicons/build/svg/check-circle-fill-24.svg\" )}.md-typeset .task-list-item{position:relative;list-style-type:none}.md-typeset .task-list-item [type=checkbox]{position:absolute;top:.45em;left:-2em}[dir=rtl] .md-typeset .task-list-item [type=checkbox]{right:-2em;left:initial}.md-typeset .task-list-control .task-list-indicator::before{position:absolute;top:.15em;left:-1.5em;width:1.25em;height:1.25em;background-color:var(--md-default-fg-color--lightest);mask-image:var(--md-tasklist-icon);mask-repeat:no-repeat;content:\"\"}[dir=rtl] .md-typeset .task-list-control .task-list-indicator::before{right:-1.5em;left:initial}.md-typeset .task-list-control [type=checkbox]:checked+.task-list-indicator::before{background-color:#00e676;mask-image:var(--md-tasklist-icon--checked)}.md-typeset .task-list-control [type=checkbox]{z-index:-1;opacity:0}","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// stylelint-disable no-duplicate-selectors\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Enforce correct box model and prevent adjustments of font size after orientation changes in IE and iOS\nhtml {\n box-sizing: border-box;\n text-size-adjust: none;\n}\n\n// All elements shall inherit the document default\n*,\n*::before,\n*::after {\n box-sizing: inherit;\n}\n\n// Remove margin in all browsers\nbody {\n margin: 0;\n}\n\n// Reset horizontal rules in FF\nhr {\n box-sizing: content-box;\n overflow: visible;\n}\n\n// Reset tap outlines on iOS and Android\na,\nbutton,\nlabel,\ninput {\n -webkit-tap-highlight-color: transparent;\n}\n\n// Reset link styles\na {\n color: inherit;\n text-decoration: none;\n}\n\n// Normalize font-size in all browsers\nsmall {\n font-size: 80%;\n}\n\n// Prevent subscript and superscript from affecting line-height\nsub,\nsup {\n line-height: 1em;\n}\n\n// Remove borders on images\nimg {\n border-style: none;\n}\n\n// Reset table styles\ntable {\n border-collapse: separate;\n border-spacing: 0;\n}\n\n// Reset table cell styles\ntd,\nth {\n font-weight: normal; // stylelint-disable-line\n vertical-align: top;\n}\n\n// Reset button styles\nbutton {\n margin: 0;\n padding: 0;\n font-size: inherit;\n background: transparent;\n border: 0;\n}\n\n// Reset input styles\ninput {\n border: 0;\n outline: none;\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Color definitions\n:root {\n\n // Default color shades\n --md-default-fg-color: hsla(0, 0%, 0%, 0.87);\n --md-default-fg-color--light: hsla(0, 0%, 0%, 0.54);\n --md-default-fg-color--lighter: hsla(0, 0%, 0%, 0.32);\n --md-default-fg-color--lightest: hsla(0, 0%, 0%, 0.07);\n --md-default-bg-color: hsla(0, 0%, 100%, 1);\n --md-default-bg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-default-bg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-default-bg-color--lightest: hsla(0, 0%, 100%, 0.12);\n\n // Primary color shades\n --md-primary-fg-color: hsla(#{hex2hsl($clr-indigo-500)}, 1);\n --md-primary-fg-color--light: hsla(#{hex2hsl($clr-indigo-300)}, 1);\n --md-primary-fg-color--dark: hsla(#{hex2hsl($clr-indigo-700)}, 1);\n --md-primary-bg-color: hsla(0, 0%, 100%, 1);\n --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);\n\n // Accent color shades\n --md-accent-fg-color: hsla(#{hex2hsl($clr-indigo-a200)}, 1);\n --md-accent-fg-color--transparent: hsla(#{hex2hsl($clr-indigo-a200)}, 0.1);\n --md-accent-bg-color: hsla(0, 0%, 100%, 1);\n --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7);\n\n // Light theme (default)\n > * {\n\n // Code color shades\n --md-code-fg-color: hsla(200, 18%, 26%, 1);\n --md-code-bg-color: hsla(0, 0%, 96%, 1);\n\n // Code highlighting color shades\n --md-code-hl-color: hsla(#{hex2hsl($clr-yellow-a200)}, 0.5);\n --md-code-hl-number-color: hsla(0, 67%, 50%, 1);\n --md-code-hl-special-color: hsla(340, 83%, 47%, 1);\n --md-code-hl-function-color: hsla(291, 45%, 50%, 1);\n --md-code-hl-constant-color: hsla(250, 63%, 60%, 1);\n --md-code-hl-keyword-color: hsla(219, 54%, 51%, 1);\n --md-code-hl-string-color: hsla(150, 63%, 30%, 1);\n --md-code-hl-name-color: var(--md-code-fg-color);\n --md-code-hl-operator-color: var(--md-default-fg-color--light);\n --md-code-hl-punctuation-color: var(--md-default-fg-color--light);\n --md-code-hl-comment-color: var(--md-default-fg-color--light);\n --md-code-hl-generic-color: var(--md-default-fg-color--light);\n --md-code-hl-variable-color: var(--md-default-fg-color--light);\n\n // Typeset color shades\n --md-typeset-color: var(--md-default-fg-color);\n --md-typeset-a-color: var(--md-primary-fg-color);\n\n // Typeset `mark` color shades\n --md-typeset-mark-color: hsla(#{hex2hsl($clr-yellow-a200)}, 0.5);\n\n // Typeset `del` and `ins` color shades\n --md-typeset-del-color: hsla(6, 90%, 60%, 0.15);\n --md-typeset-ins-color: hsla(150, 90%, 44%, 0.15);\n\n // Typeset `kbd` color shades\n --md-typeset-kbd-color: hsla(0, 0%, 98%, 1);\n --md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);\n --md-typeset-kbd-border-color: hsla(0, 0%, 72%, 1);\n\n // Admonition color shades\n --md-admonition-fg-color: var(--md-default-fg-color);\n --md-admonition-bg-color: var(--md-default-bg-color);\n\n // Footer color shades\n --md-footer-fg-color: hsla(0, 0%, 100%, 1);\n --md-footer-fg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-footer-fg-color--lighter: hsla(0, 0%, 100%, 0.3);\n --md-footer-bg-color: hsla(0, 0%, 0%, 0.87);\n --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.32);\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon\n.md-icon {\n\n // SVG defaults\n svg {\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n margin: 0 auto;\n fill: currentColor;\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: font definitions\n// ----------------------------------------------------------------------------\n\n// Enable font-smoothing in Webkit and FF\nbody {\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n// Default fonts\nbody,\ninput {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\", \"liga\";\n font-family: -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif;\n}\n\n// Proportionally spaced fonts\ncode,\npre,\nkbd {\n color: var(--md-typeset-color);\n font-feature-settings: \"kern\";\n font-family: SFMono-Regular, Consolas, Menlo, monospace;\n}\n\n// ----------------------------------------------------------------------------\n// Rules: typesetted content\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-typeset-table--ascending: svg-load(\"@mdi/svg/svg/arrow-down.svg\");\n --md-typeset-table--descending: svg-load(\"@mdi/svg/svg/arrow-up.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Content that is typeset - if possible, all margins, paddings and font sizes\n// should be set in ems, so nested blocks (e.g. admonitions) render correctly,\n// except headlines that should only appear on the top level and need to have\n// consistent spacing due to layout constraints.\n.md-typeset {\n font-size: px2rem(16px);\n line-height: 1.6;\n color-adjust: exact;\n\n // We'll use a smaller font-size for printing, so code examples don't break\n // too early, and 16px looks too big anyway.\n @media print {\n font-size: px2rem(13.6px);\n }\n\n // Default spacing\n p,\n ul,\n ol,\n blockquote {\n margin: 1em 0;\n }\n\n // 1st level headline\n h1 {\n margin: 0 0 px2em(40px, 32px);\n color: var(--md-default-fg-color--light);\n font-weight: 300;\n font-size: px2em(32px);\n line-height: 1.3;\n letter-spacing: -0.01em;\n }\n\n // 2nd level headline\n h2 {\n margin: px2em(40px, 25px) 0 px2em(16px, 25px);\n font-weight: 300;\n font-size: px2em(25px);\n line-height: 1.4;\n letter-spacing: -0.01em;\n }\n\n // 3rd level headline\n h3 {\n margin: px2em(32px, 20px) 0 px2em(16px, 20px);\n font-weight: 400;\n font-size: px2em(20px);\n line-height: 1.5;\n letter-spacing: -0.01em;\n }\n\n // 3rd level headline following an 2nd level headline\n h2 + h3 {\n margin-top: px2em(16px, 20px);\n }\n\n // 4th level headline\n h4 {\n margin: px2em(16px) 0;\n font-weight: 700;\n letter-spacing: -0.01em;\n }\n\n // 5th and 6th level headline\n h5,\n h6 {\n margin: px2em(16px, 12.8px) 0;\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: px2em(12.8px);\n letter-spacing: -0.01em;\n }\n\n // Overrides for 5th level headline\n h5 {\n text-transform: uppercase;\n }\n\n // Horizontal separators\n hr {\n margin: 1.5em 0;\n border-bottom: px2rem(1px) dotted var(--md-default-fg-color--lighter);\n }\n\n // Links\n a {\n color: var(--md-typeset-a-color);\n word-break: break-word;\n\n // Also enable color transition on pseudo elements\n &,\n &::before {\n transition: color 125ms;\n }\n\n // Focused or hovered links\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n\n // Code blocks\n code,\n pre,\n kbd {\n color: var(--md-code-fg-color);\n direction: ltr;\n\n // Wrap text and hide scollbars\n @media print {\n white-space: pre-wrap;\n }\n }\n\n // Inline code blocks\n code {\n padding: 0 px2em(4px, 13.6px);\n font-size: px2em(13.6px);\n word-break: break-word;\n background-color: var(--md-code-bg-color);\n border-radius: px2rem(2px);\n box-decoration-break: clone;\n }\n\n // Disable containing block inside headlines\n h1 code,\n h2 code,\n h3 code,\n h4 code,\n h5 code,\n h6 code {\n margin: initial;\n padding: initial;\n background-color: transparent;\n box-shadow: none;\n }\n\n // Ensure link color in code blocks\n a > code {\n color: currentColor;\n }\n\n // Unformatted code blocks\n pre {\n position: relative;\n margin: 1em 0;\n line-height: 1.4;\n\n // Actual container with code, overflowing\n > code {\n display: block;\n margin: 0;\n padding: px2em(10.5px, 13.6px) px2em(16px, 13.6px);\n overflow: auto;\n word-break: normal;\n box-shadow: none;\n box-decoration-break: slice;\n touch-action: auto;\n // Override Firefox scrollbar style\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Override Firefox scrollbar hover color\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Override native scrollbar styles\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Hovered scrollbar thumb\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n }\n\n // [mobile -]: Stretch to whole width\n @include break-to-device(mobile) {\n\n // Stretch top-level containers\n > pre {\n margin: 1em px2rem(-16px);\n\n // Remove rounded borders\n code {\n border-radius: 0;\n }\n }\n }\n\n // Keyboard key\n kbd {\n display: inline-block;\n padding: 0 px2em(8px, 12px);\n color: var(--md-default-fg-color);\n font-size: px2em(12px);\n vertical-align: text-top;\n word-break: break-word;\n background-color: var(--md-typeset-kbd-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(2px) 0 px2rem(1px) var(--md-typeset-kbd-border-color),\n 0 px2rem(2px) 0 var(--md-typeset-kbd-border-color),\n 0 px2rem(-2px) px2rem(4px) var(--md-typeset-kbd-accent-color) inset;\n }\n\n // Text highlighting marker\n mark {\n color: inherit;\n word-break: break-word;\n background-color: var(--md-typeset-mark-color);\n box-decoration-break: clone;\n }\n\n // Abbreviations\n abbr {\n text-decoration: none;\n border-bottom: px2rem(1px) dotted var(--md-default-fg-color--light);\n cursor: help;\n\n // Render a tooltip for touch devices\n @media (hover: none) {\n position: relative;\n\n // Tooltip\n &[title]:focus::after,\n &[title]:hover::after {\n @include z-depth(2);\n\n position: absolute;\n left: 0;\n display: inline-block;\n width: auto;\n min-width: max-content;\n max-width: 80%;\n margin-top: 2em;\n padding: px2rem(4px) px2rem(6px);\n color: var(--md-default-bg-color);\n font-size: px2rem(14px);\n background: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n content: attr(title);\n }\n }\n\n }\n\n // Small text\n small {\n opacity: 0.75;\n }\n\n // Superscript and subscript\n sup,\n sub {\n margin-left: px2em(1px, 12.8px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(1px, 12.8px);\n margin-left: initial;\n }\n }\n\n // Blockquotes, possibly nested\n blockquote {\n padding-left: px2rem(12px);\n color: var(--md-default-fg-color--light);\n border-left: px2rem(4px) solid var(--md-default-fg-color--lighter);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: initial;\n border-right: px2rem(4px) solid var(--md-default-fg-color--lighter);\n border-left: initial;\n }\n }\n\n // Unordered lists\n ul {\n list-style-type: disc;\n }\n\n // Unordered and ordered lists\n ul,\n ol {\n margin-left: px2em(10px);\n padding: 0;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(10px);\n margin-left: initial;\n }\n\n // Nested ordered lists\n ol {\n list-style-type: lower-alpha;\n\n // Triply nested ordered list\n ol {\n list-style-type: lower-roman;\n }\n }\n\n // List elements\n li {\n margin-bottom: 0.5em;\n margin-left: px2em(20px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(20px);\n margin-left: initial;\n }\n\n // Decrease vertical spacing\n p,\n blockquote {\n margin: 0.5em 0;\n }\n\n // Remove margin on last element\n &:last-child {\n margin-bottom: 0;\n }\n\n // Nested lists\n ul,\n ol {\n margin: 0.5em 0 0.5em px2em(10px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(10px);\n margin-left: initial;\n }\n }\n }\n }\n\n // Definition lists\n dd {\n margin: 1em 0 1.5em px2em(30px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2em(30px);\n margin-left: initial;\n }\n }\n\n // Limit width to container, scale height proportionally\n img,\n svg {\n max-width: 100%;\n height: auto;\n\n // Left-aligned\n &[align=\"left\"] {\n margin: 1em;\n margin-left: 0;\n }\n\n // Right-aligned\n &[align=\"right\"] {\n margin: 1em;\n margin-right: 0;\n }\n\n // Remove top spacing of sole children\n &[align]:only-child {\n margin-top: 0;\n }\n }\n\n // Figures\n figure {\n width: fit-content;\n max-width: 100%;\n margin: 0 auto;\n text-align: center;\n }\n\n // Figure captions\n figcaption {\n max-width: px2rem(480px);\n margin: 0.5em auto 2em;\n font-style: italic;\n }\n\n // Limit width to container\n iframe {\n max-width: 100%;\n }\n\n // Data tables\n table:not([class]) {\n display: inline-block;\n max-width: 100%;\n overflow: auto;\n font-size: px2rem(12.8px);\n background: var(--md-default-bg-color);\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.05),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.1);\n touch-action: auto;\n\n // Reset display mode so table header wraps correctly when printing\n @media print {\n display: table;\n }\n\n // Due to margin collapse because of the necessary inline-block hack, we\n // cannot increase the bottom margin on the table, so we just increase the\n // top margin on the following element\n & + * {\n margin-top: 1.5em;\n }\n\n // Elements inside cells\n th > *,\n td > * {\n\n // Remove top spacing of first child\n &:first-child {\n margin-top: 0;\n }\n\n // Remove bottom spacing of last child\n &:last-child {\n margin-bottom: 0;\n }\n }\n\n // Table headings and cells\n th:not([align]),\n td:not([align]) {\n text-align: left;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n text-align: right;\n }\n }\n\n // Table headings\n th {\n min-width: px2rem(100px);\n padding: px2em(12px, 12.8px) px2em(16px, 12.8px);\n color: var(--md-default-bg-color);\n vertical-align: top;\n background-color: var(--md-default-fg-color--light);\n\n // Links in table headings\n a {\n color: inherit;\n }\n }\n\n // Table cells\n td {\n padding: px2em(12px, 12.8px) px2em(16px, 12.8px);\n vertical-align: top;\n border-top: px2rem(1px) solid var(--md-default-fg-color--lightest);\n }\n\n // Table rows\n tr {\n transition: background-color 125ms;\n\n // Add background on hover\n &:hover {\n background-color: rgba(0, 0, 0, 0.035);\n box-shadow: 0 px2rem(1px) 0 var(--md-default-bg-color) inset;\n }\n\n // Remove top border on first row\n &:first-child td {\n border-top: 0;\n }\n }\n\n // Do not wrap links in tables\n a {\n word-break: normal;\n }\n }\n\n // Sortable tables\n table th[role=\"columnheader\"] {\n cursor: pointer;\n\n // Sort icon\n &::after {\n display: inline-block;\n width: 1.2em;\n height: 1.2em;\n margin-left: 0.5em;\n vertical-align: sub;\n mask-repeat: no-repeat;\n content: \" \";\n }\n\n // Sort ascending\n &[aria-sort=\"ascending\"]::after {\n background-color: currentColor;\n mask-image: var(--md-typeset-table--ascending);\n }\n\n // Sort descending\n &[aria-sort=\"descending\"]::after {\n background-color: currentColor;\n mask-image: var(--md-typeset-table--descending);\n }\n }\n\n // Wrapper for scrolling on overflow\n &__scrollwrap {\n margin: 1em px2rem(-16px);\n overflow-x: auto;\n touch-action: auto;\n }\n\n // Data table wrapper, in case JavaScript is available\n &__table {\n display: inline-block;\n margin-bottom: 0.5em;\n padding: 0 px2rem(16px);\n\n // Reset display mode so table header wraps correctly when printing\n @media print {\n display: block;\n }\n\n // Data tables\n html & table {\n display: table;\n width: 100%;\n margin: 0;\n overflow: hidden;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n///\n/// Device-specific breakpoints\n///\n/// @example\n/// $break-devices: (\n/// mobile: (\n/// portrait: 220px 479px,\n/// landscape: 480px 719px\n/// ),\n/// tablet: (\n/// portrait: 720px 959px,\n/// landscape: 960px 1219px\n/// ),\n/// screen: (\n/// small: 1220px 1599px,\n/// medium: 1600px 1999px,\n/// large: 2000px\n/// )\n/// );\n///\n$break-devices: () !default;\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\n///\n/// Choose minimum and maximum device widths\n///\n@function break-select-min-max($devices) {\n $min: 1000000;\n $max: 0;\n @each $key, $value in $devices {\n @while type-of($value) == map {\n $value: break-select-min-max($value);\n }\n @if type-of($value) == list {\n @each $number in $value {\n @if type-of($number) == number {\n $min: min($number, $min);\n @if $max != null {\n $max: max($number, $max);\n }\n } @else {\n @error \"Invalid number: #{$number}\";\n }\n }\n } @else if type-of($value) == number {\n $min: min($value, $min);\n $max: null;\n } @else {\n @error \"Invalid value: #{$value}\";\n }\n }\n @return $min, $max;\n}\n\n///\n/// Select minimum and maximum widths for a device breakpoint\n///\n@function break-select-device($device) {\n $current: $break-devices;\n @for $n from 1 through length($device) {\n @if type-of($current) == map {\n $current: map-get($current, nth($device, $n));\n } @else {\n @error \"Invalid device map: #{$devices}\";\n }\n }\n @if type-of($current) == list or type-of($current) == number {\n $current: (default: $current);\n }\n @return break-select-min-max($current);\n}\n\n// ----------------------------------------------------------------------------\n// Mixins\n// ----------------------------------------------------------------------------\n\n///\n/// A minimum-maximum media query breakpoint\n///\n@mixin break-at($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (min-width: $breakpoint) {\n @content;\n }\n } @else if type-of($breakpoint) == list {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n @if type-of($min) == number and type-of($max) == number {\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// An orientation media query breakpoint\n///\n@mixin break-at-orientation($breakpoint) {\n @if type-of($breakpoint) == string {\n @media screen and (orientation: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A maximum-aspect-ratio media query breakpoint\n///\n@mixin break-at-ratio($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (max-aspect-ratio: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A minimum-maximum media query device breakpoint\n///\n@mixin break-at-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n @if nth($breakpoint, 2) != null {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A minimum media query device breakpoint\n///\n@mixin break-from-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $min: nth($breakpoint, 1);\n @media screen and (min-width: $min) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A maximum media query device breakpoint\n///\n@mixin break-to-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $max: nth($breakpoint, 2);\n @media screen and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n","//\n// Name: Material Shadows\n// Description: Mixins for Material Design Shadows.\n// Version: 3.0.1\n//\n// Author: Denis Malinochkin\n// Git: https://github.com/mrmlnc/material-shadows\n//\n// twitter: @mrmlnc\n//\n// ------------------------------------\n\n\n// Mixins\n// ------------------------------------\n\n@mixin z-depth-transition() {\n transition: box-shadow .28s cubic-bezier(.4, 0, .2, 1);\n}\n\n@mixin z-depth-focus() {\n box-shadow: 0 0 8px rgba(0, 0, 0, .18), 0 8px 16px rgba(0, 0, 0, .36);\n}\n\n@mixin z-depth-2dp() {\n box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14),\n 0 1px 5px 0 rgba(0, 0, 0, .12),\n 0 3px 1px -2px rgba(0, 0, 0, .2);\n}\n\n@mixin z-depth-3dp() {\n box-shadow: 0 3px 4px 0 rgba(0, 0, 0, .14),\n 0 1px 8px 0 rgba(0, 0, 0, .12),\n 0 3px 3px -2px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-4dp() {\n box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14),\n 0 1px 10px 0 rgba(0, 0, 0, .12),\n 0 2px 4px -1px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-6dp() {\n box-shadow: 0 6px 10px 0 rgba(0, 0, 0, .14),\n 0 1px 18px 0 rgba(0, 0, 0, .12),\n 0 3px 5px -1px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-8dp() {\n box-shadow: 0 8px 10px 1px rgba(0, 0, 0, .14),\n 0 3px 14px 2px rgba(0, 0, 0, .12),\n 0 5px 5px -3px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-16dp() {\n box-shadow: 0 16px 24px 2px rgba(0, 0, 0, .14),\n 0 6px 30px 5px rgba(0, 0, 0, .12),\n 0 8px 10px -5px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth-24dp() {\n box-shadow: 0 9px 46px 8px rgba(0, 0, 0, .14),\n 0 24px 38px 3px rgba(0, 0, 0, .12),\n 0 11px 15px -7px rgba(0, 0, 0, .4);\n}\n\n@mixin z-depth($dp: 2) {\n @if $dp == 2 {\n @include z-depth-2dp();\n } @else if $dp == 3 {\n @include z-depth-3dp();\n } @else if $dp == 4 {\n @include z-depth-4dp();\n } @else if $dp == 6 {\n @include z-depth-6dp();\n } @else if $dp == 8 {\n @include z-depth-8dp();\n } @else if $dp == 16 {\n @include z-depth-16dp();\n } @else if $dp == 24 {\n @include z-depth-24dp();\n }\n}\n\n\n// Class generator\n// ------------------------------------\n\n@mixin z-depth-classes($transition: false, $focus: false) {\n @if $transition == true {\n &-transition {\n @include z-depth-transition();\n }\n }\n\n @if $focus == true {\n &-focus {\n @include z-depth-focus();\n }\n }\n\n // The available values for the shadow depth\n @each $depth in 2, 3, 4, 6, 8, 16, 24 {\n &-#{$depth}dp {\n @include z-depth($depth);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n// Active (toggled) drawer\n$md-toggle__drawer--checked:\n \"[data-md-toggle=\\\"drawer\\\"]:checked ~\";\n\n// ----------------------------------------------------------------------------\n// Rules: base grid and containers\n// ----------------------------------------------------------------------------\n\n// Stretch container to viewport and set base font-sizefor simple calculations\n// based on relative ems (rems)\nhtml {\n height: 100%;\n // Hack: some browsers on some operating systems don't account for scroll\n // bars when firing media queries, so we need to do this for safety. This\n // currently impacts the table of contents component between 1220 and 1234px\n // and is to current knowledge not fixable.\n overflow-x: hidden;\n // Hack: normally, we would set the base font-size to 62.5%, so we can base\n // all calculations on 10px, but Chromium and Chrome define a minimal font\n // size of 12 if the system language is set to Chinese. For this reason we\n // just double the font-size, set it to 20px which seems to do the trick.\n //\n // See https://github.com/squidfunk/mkdocs-material/issues/911\n font-size: 125%;\n\n // [screen medium +]: Set base font-size to 11px\n @include break-from-device(screen medium) {\n font-size: 137.50%;\n }\n\n // [screen large +]: Set base font-size to 12px\n @include break-from-device(screen large) {\n font-size: 150%;\n }\n}\n\n// Stretch body to container and leave room for footer\nbody {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 100%;\n min-height: 100%;\n // Hack: reset font-size to 10px, so the spacing for all inline elements is\n // correct again. Otherwise the spacing would be based on 20px.\n font-size: 0.5rem; // stylelint-disable-line unit-allowed-list\n background-color: var(--md-default-bg-color);\n\n // [tablet portrait -]: Lock body to disable scroll bubbling\n @include break-to-device(tablet portrait) {\n\n // Lock body to viewport height (e.g. in search mode)\n &[data-md-state=\"lock\"] {\n position: fixed;\n }\n }\n\n // Hack: we must not use flex, or Firefox will only print the first page\n // see https://mzl.la/39DgR3m\n @media print {\n display: block;\n }\n}\n\n// Horizontal separators\nhr {\n display: block;\n height: px2rem(1px);\n padding: 0;\n border: 0;\n}\n\n// Template-wide grid\n.md-grid {\n max-width: px2rem(1220px);\n margin-right: auto;\n margin-left: auto;\n}\n\n// Content wrapper\n.md-container {\n display: flex;\n flex-direction: column;\n flex-grow: 1;\n\n // Hack: we must not use flex, or Firefox will only print the first page\n // see https://mzl.la/39DgR3m\n @media print {\n display: block;\n }\n}\n\n// The main content should stretch to maximum height in the table\n.md-main {\n flex-grow: 1;\n\n // Increase top spacing of content area to give typography more room\n &__inner {\n display: flex;\n height: 100%;\n margin-top: px2rem(24px + 6px);\n }\n}\n\n// Apply ellipsis in case of overflowing text\n.md-ellipsis {\n display: block;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n\n// ----------------------------------------------------------------------------\n// Rules: navigational elements\n// ----------------------------------------------------------------------------\n\n// Toggle checkbox\n.md-toggle {\n display: none;\n}\n\n// Overlay below expanded drawer\n.md-overlay {\n position: fixed;\n top: 0;\n z-index: 3;\n width: 0;\n height: 0;\n background-color: hsla(0, 0%, 0%, 0.54);\n opacity: 0;\n transition:\n width 0ms 250ms,\n height 0ms 250ms,\n opacity 250ms;\n\n // [tablet -]: Trigger overlay\n @include break-to-device(tablet) {\n\n // Expanded drawer\n #{$md-toggle__drawer--checked} & {\n width: 100%;\n height: 100%;\n opacity: 1;\n transition:\n width 0ms,\n height 0ms,\n opacity 250ms;\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: skip link\n// ----------------------------------------------------------------------------\n\n// Skip link\n.md-skip {\n position: fixed;\n // Hack: if we don't set the negative z-index, the skip link will induce the\n // creation of new layers when code blocks are near the header on scrolling\n z-index: -1;\n margin: px2rem(10px);\n padding: px2rem(6px) px2rem(10px);\n color: var(--md-default-bg-color);\n font-size: px2rem(12.8px);\n background-color: var(--md-default-fg-color);\n border-radius: px2rem(2px);\n transform: translateY(px2rem(8px));\n opacity: 0;\n\n // Show skip link on focus\n &:focus {\n z-index: 10;\n transform: translateY(0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 175ms 75ms;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: print styles\n// ----------------------------------------------------------------------------\n\n// Add margins to page\n@page {\n margin: 25mm;\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Announcement bar\n.md-announce {\n overflow: auto;\n background-color: var(--md-footer-bg-color);\n\n // Actual content\n &__inner {\n margin: px2rem(12px) auto;\n padding: 0 px2rem(16px);\n color: var(--md-footer-fg-color);\n font-size: px2rem(14px);\n }\n\n // Hide for print\n @media print {\n display: none;\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Button\n .md-button {\n display: inline-block;\n padding: px2em(10px) px2em(32px);\n color: var(--md-primary-fg-color);\n font-weight: 700;\n border: px2rem(2px) solid currentColor;\n border-radius: px2rem(2px);\n transition:\n color 125ms,\n background-color 125ms,\n border-color 125ms;\n\n // Primary button\n &--primary {\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n border-color: var(--md-primary-fg-color);\n }\n\n // Focused or hovered button\n &:focus,\n &:hover {\n color: var(--md-accent-bg-color);\n background-color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-clipboard-icon: svg-load(\"@mdi/svg/svg/content-copy.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Copy to clipboard\n.md-clipboard {\n position: absolute;\n top: px2em(8px);\n right: px2em(8px);\n z-index: 1;\n width: px2em(24px);\n height: px2em(24px);\n color: var(--md-default-fg-color--lightest);\n // background-color: var(--md-code-bg-color);\n border-radius: px2rem(2px);\n cursor: pointer;\n transition: color 125ms;\n\n // Hide for print\n @media print {\n display: none;\n }\n\n // // Make room for clipboard button in case of overflowing code\n // .md-typeset & + code {\n // padding-right: px2em(24px + 2 * 8px, 13.6px);\n // }\n\n // Slightly smaller icon\n &::after {\n display: block;\n width: px2em(18px);\n height: px2em(18px);\n margin: 0 auto;\n background-color: currentColor;\n mask-image: var(--md-clipboard-icon);\n mask-repeat: no-repeat;\n content: \"\";\n }\n\n // Show on container hover\n pre:hover & {\n color: var(--md-default-fg-color--light);\n }\n\n // Focused or hovered icon\n pre &:focus,\n pre &:hover {\n color: var(--md-accent-fg-color);\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Content container\n.md-content {\n flex: 1;\n max-width: 100%;\n\n // [tablet landscape]: Decrease horizontal width\n @include break-at-device(tablet landscape) {\n max-width: calc(100% - #{px2rem(242px)});\n }\n\n // [screen +]: Decrease horizontal width\n @include break-from-device(screen) {\n max-width: calc(100% - #{px2rem(242px)} * 2);\n }\n\n // Define spacing\n &__inner {\n margin: 0 px2rem(16px) px2rem(24px);\n padding-top: px2rem(12px);\n\n // [screen +]: Increase horizontal spacing\n @include break-from-device(screen) {\n margin-right: px2rem(24px);\n margin-left: px2rem(24px);\n }\n\n // Hack: add pseudo element for spacing, as the overflow of the content\n // container may not be hidden due to an imminent offset error on targets\n &::before {\n display: block;\n height: px2rem(8px);\n content: \"\";\n }\n\n // Hack: remove bottom spacing of last element, due to margin collapse\n > :last-child {\n margin-bottom: 0;\n }\n }\n\n // Button next to the title\n &__button {\n float: right;\n margin: px2rem(8px) 0;\n margin-left: px2rem(8px);\n padding: 0;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n margin-right: px2rem(8px);\n margin-left: initial;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n\n // Override default link color for icons\n .md-typeset & {\n color: var(--md-default-fg-color--lighter);\n }\n\n // Align text with icon\n svg {\n display: inline;\n vertical-align: top;\n }\n\n // Hide for print\n @media print {\n display: none;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Dialog rendered as snackbar\n.md-dialog {\n @include z-depth(2);\n\n position: fixed;\n right: px2rem(16px);\n bottom: px2rem(16px);\n left: initial;\n z-index: 2;\n display: block;\n min-width: px2rem(222px);\n padding: px2rem(8px) px2rem(12px);\n color: var(--md-default-bg-color);\n font-size: px2rem(14px);\n background: var(--md-default-fg-color);\n border: none;\n border-radius: px2rem(2px);\n transform: translateY(100%);\n opacity: 0;\n transition:\n transform 0ms 400ms,\n opacity 400ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(16px);\n }\n\n // Show open dialog\n &[data-md-state=\"open\"] {\n transform: translateY(0);\n opacity: 1;\n transition:\n transform 400ms cubic-bezier(0.075, 0.85, 0.175, 1),\n opacity 400ms;\n }\n\n // Hide for print\n @media print {\n display: none;\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Application header (stays always on top)\n.md-header {\n position: sticky;\n top: 0;\n right: 0;\n left: 0;\n z-index: 2;\n height: px2rem(48px);\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n // Hack: reduce jitter by adding a transparent box shadow of the same size\n // so the size of the layer doesn't change during animation\n box-shadow:\n 0 0 px2rem(4px) rgba(0, 0, 0, 0),\n 0 px2rem(4px) px2rem(8px) rgba(0, 0, 0, 0);\n transition:\n color 250ms,\n background-color 250ms;\n\n // Always hide shadow, in case JavaScript is not available\n .no-js & {\n box-shadow: none;\n transition: none;\n }\n\n // Show and animate shadow\n &[data-md-state=\"shadow\"] {\n box-shadow:\n 0 0 px2rem(4px) rgba(0, 0, 0, 0.1),\n 0 px2rem(4px) px2rem(8px) rgba(0, 0, 0, 0.2);\n transition:\n color 250ms,\n background-color 250ms,\n box-shadow 250ms;\n }\n\n // Hide for print\n @media print {\n display: none;\n }\n}\n\n// Navigation within header\n.md-header-nav {\n display: flex;\n padding: 0 px2rem(4px);\n\n // Icon buttons\n &__button {\n position: relative;\n z-index: 1;\n display: block;\n margin: px2rem(4px);\n padding: px2rem(8px);\n cursor: pointer;\n transition: opacity 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n\n // Focused or hovered icon\n &:focus,\n &:hover {\n opacity: 0.7;\n }\n\n // Logo\n &.md-logo {\n margin: px2rem(4px);\n padding: px2rem(8px);\n\n // Image or icon\n img,\n svg {\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n fill: currentColor;\n }\n }\n\n // Hide search icon, if JavaScript is not available.\n .no-js &[for=\"__search\"] {\n display: none;\n }\n\n // [tablet landscape +]: Hide the search button\n @include break-from-device(tablet landscape) {\n\n // Search button\n &[for=\"__search\"] {\n display: none;\n }\n }\n\n // [tablet -]: Hide the logo\n @include break-to-device(tablet) {\n\n // Logo\n &.md-logo {\n display: none;\n }\n }\n\n // [screen +]: Hide the menu button\n @include break-from-device(screen) {\n\n // Menu button\n &[for=\"__drawer\"] {\n display: none;\n }\n }\n }\n\n // Header topics\n &__topic {\n position: absolute;\n width: 100%;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n\n // Page title\n & + & {\n z-index: -1;\n transform: translateX(px2rem(25px));\n opacity: 0;\n transition:\n transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),\n opacity 150ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-25px));\n }\n }\n\n // Induce ellipsis, if no JavaScript is available\n .no-js & {\n position: initial;\n }\n\n // Hide page title as it is invisible anyway and will overflow the header\n .no-js & + & {\n display: none;\n }\n }\n\n // Header title - set line height to match icon for correct alignment\n &__title {\n flex-grow: 1;\n padding: 0 px2rem(20px);\n font-size: px2rem(18px);\n line-height: px2rem(48px);\n\n // Show page title\n &[data-md-state=\"active\"] .md-header-nav__topic {\n z-index: -1;\n transform: translateX(px2rem(-25px));\n opacity: 0;\n transition:\n transform 400ms cubic-bezier(1, 0.7, 0.1, 0.1),\n opacity 150ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(25px));\n }\n\n // Page title\n & + .md-header-nav__topic {\n z-index: 0;\n transform: translateX(0);\n opacity: 1;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n pointer-events: initial;\n }\n }\n\n // Patch ellipsis\n > .md-header-nav__ellipsis {\n position: relative;\n width: 100%;\n height: 100%;\n }\n }\n\n // Repository containing source\n &__source {\n display: none;\n\n // [tablet landscape +]: Show the reposistory from tablet\n @include break-from-device(tablet landscape) {\n display: block;\n width: px2rem(234px);\n max-width: px2rem(234px);\n margin-left: px2rem(20px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(20px);\n margin-left: initial;\n }\n }\n\n // [screen +]: Increase spacing of search bar\n @include break-from-device(screen) {\n margin-left: px2rem(28px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(28px);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Application footer\n.md-footer {\n color: var(--md-footer-fg-color);\n background-color: var(--md-footer-bg-color);\n\n // Hide for print\n @media print {\n display: none;\n }\n}\n\n// Navigation within footer\n.md-footer-nav {\n\n // Set spacing\n &__inner {\n padding: px2rem(4px);\n overflow: auto;\n }\n\n // Links to previous and next page\n &__link {\n display: flex;\n padding-top: px2rem(28px);\n padding-bottom: px2rem(8px);\n transition: opacity 250ms;\n\n // [tablet +]: Set proportional width\n @include break-from-device(tablet) {\n width: 50%;\n }\n\n // Focused or hovered links\n &:focus,\n &:hover {\n opacity: 0.7;\n }\n\n // Link to previous page\n &--prev {\n float: left;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: right;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n\n // [mobile -]: Hide title for previous page\n @include break-to-device(mobile) {\n width: 25%;\n\n // Title\n .md-footer-nav__title {\n display: none;\n }\n }\n }\n\n // Link to next page\n &--next {\n float: right;\n text-align: right;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n text-align: left;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n\n // [mobile -]: Hide title for previous page\n @include break-to-device(mobile) {\n width: 75%;\n }\n }\n }\n\n // Link title - set line height to match icon for correct alignment\n &__title {\n position: relative;\n flex-grow: 1;\n max-width: calc(100% - #{px2rem(48px)});\n padding: 0 px2rem(20px);\n font-size: px2rem(18px);\n line-height: px2rem(48px);\n }\n\n // Link button\n &__button {\n margin: px2rem(4px);\n padding: px2rem(8px);\n }\n\n // Link direction\n &__direction {\n position: absolute;\n right: 0;\n left: 0;\n margin-top: px2rem(-20px);\n padding: 0 px2rem(20px);\n font-size: px2rem(12.8px);\n opacity: 0.7;\n }\n}\n\n// Non-navigational information\n.md-footer-meta {\n background-color: var(--md-footer-bg-color--dark);\n\n // Set spacing\n &__inner {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n padding: px2rem(4px);\n }\n\n // Use a decent color for non-hovered links and ensure specificity\n html &.md-typeset a {\n color: var(--md-footer-fg-color--light);\n\n // Focused or hovered link\n &:focus,\n &:hover {\n color: var(--md-footer-fg-color);\n }\n }\n}\n\n// Copyright and theme information\n.md-footer-copyright {\n width: 100%;\n margin: auto px2rem(12px);\n padding: px2rem(8px) 0;\n color: var(--md-footer-fg-color--lighter);\n font-size: px2rem(12.8px);\n\n // [tablet portrait +]: Show next to social media links\n @include break-from-device(tablet portrait) {\n width: auto;\n }\n\n // Highlight copyright information\n &__highlight {\n color: var(--md-footer-fg-color--light);\n }\n}\n\n// Social links\n.md-footer-social {\n margin: 0 px2rem(8px);\n padding: px2rem(4px) 0 px2rem(12px);\n\n // [tablet portrait +]: Show next to copyright information\n @include break-from-device(tablet portrait) {\n padding: px2rem(12px) 0;\n }\n\n // Link with icon\n &__link {\n display: inline-block;\n width: px2rem(32px);\n height: px2rem(32px);\n text-align: center;\n\n // Adjust line-height to match height for correct alignment\n &::before {\n line-height: 1.9;\n }\n\n // Social icon\n svg {\n max-height: px2rem(16px);\n vertical-align: -25%;\n fill: currentColor;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-nav-icon--prev: svg-load(\"@mdi/svg/svg/arrow-left.svg\");\n --md-nav-icon--next: svg-load(\"@mdi/svg/svg/chevron-right.svg\");\n --md-toc-icon: svg-load(\"@mdi/svg/svg/table-of-contents.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Navigation container\n.md-nav {\n font-size: px2rem(14px);\n line-height: 1.3;\n\n // List title\n &__title {\n display: block;\n padding: 0 px2rem(12px);\n overflow: hidden;\n font-weight: 700;\n text-overflow: ellipsis;\n\n // Hide buttons by default\n .md-nav__button {\n display: none;\n\n // Stretch images\n img {\n width: 100%;\n height: auto;\n }\n\n // Logo\n &.md-logo {\n\n // Image or icon\n img,\n svg {\n display: block;\n width: px2rem(48px);\n height: px2rem(48px);\n }\n\n // Icon\n svg {\n fill: currentColor;\n }\n }\n }\n }\n\n // List of items\n &__list {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n // List item\n &__item {\n padding: 0 px2rem(12px);\n\n // Add bottom spacing to last item\n &:last-child {\n padding-bottom: px2rem(12px);\n }\n\n // 2nd+ level items\n & & {\n padding-right: 0;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(12px);\n padding-left: 0;\n }\n\n // Remove bottom spacing for nested items\n &:last-child {\n padding-bottom: 0;\n }\n }\n }\n\n // Link inside item\n &__link {\n display: block;\n margin-top: 0.625em;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: pointer;\n transition: color 125ms;\n scroll-snap-align: start;\n\n // Hide link to table of contents by default - this will only match the\n // table of contents inside the drawer below and including tablet portrait\n html &[for=\"__toc\"] {\n display: none;\n\n // Hide table of contents by default\n & ~ .md-nav {\n display: none;\n }\n }\n\n // Blurred link\n &[data-md-state=\"blur\"] {\n color: var(--md-default-fg-color--light);\n }\n\n // Active link\n .md-nav__item &--active {\n color: var(--md-typeset-a-color);\n }\n\n // Reset active color for nested list titles\n .md-nav__item--nested > & {\n color: inherit;\n }\n\n // Focused or hovered link\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n\n // Repository containing source\n &__source {\n display: none;\n }\n\n // [tablet -]: Layered navigation\n @include break-to-device(tablet) {\n background-color: var(--md-default-bg-color);\n\n // Stretch primary navigation to drawer\n &--primary,\n &--primary .md-nav {\n position: absolute;\n top: 0;\n right: 0;\n left: 0;\n z-index: 1;\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n // Adjust styles for primary navigation\n &--primary {\n\n // List title and item\n .md-nav__title,\n .md-nav__item {\n font-size: px2rem(16px);\n line-height: 1.5;\n }\n\n // List title\n .md-nav__title {\n position: relative;\n height: px2rem(112px);\n padding: px2rem(60px) px2rem(16px) px2rem(4px);\n color: var(--md-default-fg-color--light);\n font-weight: 400;\n line-height: px2rem(48px);\n white-space: nowrap;\n background-color: var(--md-default-fg-color--lightest);\n cursor: pointer;\n\n // Navigation icon\n .md-nav__icon {\n position: absolute;\n top: px2rem(8px);\n left: px2rem(8px);\n display: block;\n width: px2rem(24px);\n height: px2rem(24px);\n margin: px2rem(4px);\n\n // Previous navigation item icon\n &::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--prev);\n mask-repeat: no-repeat;\n content: \"\";\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(8px);\n left: initial;\n }\n }\n\n // Main lists\n ~ .md-nav__list {\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n box-shadow:\n 0 px2rem(1px) 0 var(--md-default-fg-color--lightest) inset;\n scroll-snap-type: y mandatory;\n touch-action: pan-y;\n\n // Remove border for first list item\n > .md-nav__item:first-child {\n border-top: 0;\n }\n }\n\n // Site title in main navigation\n &[for=\"__drawer\"] {\n position: relative;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n\n // Site logo\n .md-nav__button {\n position: absolute;\n top: px2rem(4px);\n left: px2rem(4px);\n display: block;\n margin: px2rem(4px);\n padding: px2rem(8px);\n font-size: px2rem(48px);\n }\n }\n }\n\n // Adjust for right-to-left languages\n html [dir=\"rtl\"] & .md-nav__title {\n\n // Site title in main navigation\n &[for=\"__drawer\"] .md-nav__button {\n right: px2rem(4px);\n left: initial;\n }\n }\n\n // List of items\n .md-nav__list {\n flex: 1;\n }\n\n // List item\n .md-nav__item {\n padding: 0;\n border-top: px2rem(1px) solid var(--md-default-fg-color--lightest);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding: 0;\n }\n\n // Increase spacing to account for icon\n &--nested > .md-nav__link {\n padding-right: px2rem(48px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(16px);\n padding-left: px2rem(48px);\n }\n }\n\n // Active parent item\n &--active > .md-nav__link {\n color: var(--md-typeset-a-color);\n\n // Focused or hovered linl\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n }\n\n // Link inside item\n .md-nav__link {\n position: relative;\n margin-top: 0;\n padding: px2rem(12px) px2rem(16px);\n\n // Navigation icon\n .md-nav__icon {\n position: absolute;\n top: 50%;\n right: px2rem(12px);\n width: px2rem(24px);\n height: px2rem(24px);\n margin-top: px2rem(-12px);\n color: inherit;\n font-size: px2rem(24px);\n\n // Next navigation item icon\n &::after {\n display: block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n content: \"\";\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(12px);\n }\n }\n }\n\n // Flip icon vertically\n .md-nav__icon::after {\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: scale(-1);\n }\n }\n\n // Table of contents inside navigation\n .md-nav--secondary {\n\n // Set links to static to avoid unnecessary layering\n .md-nav__link {\n position: static;\n }\n\n // Set nested navigation for table of contents to static\n .md-nav {\n position: static;\n background-color: transparent;\n\n // 3rd level link\n .md-nav__link {\n padding-left: px2rem(28px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(28px);\n padding-left: initial;\n }\n }\n\n // 4th level link\n .md-nav .md-nav__link {\n padding-left: px2rem(40px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(40px);\n padding-left: initial;\n }\n }\n\n // 5th level link\n .md-nav .md-nav .md-nav__link {\n padding-left: px2rem(52px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(52px);\n padding-left: initial;\n }\n }\n\n // 6th level link\n .md-nav .md-nav .md-nav .md-nav__link {\n padding-left: px2rem(64px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(64px);\n padding-left: initial;\n }\n }\n }\n }\n }\n\n // Hide nested navigation by default\n .md-nav__toggle ~ & {\n display: flex;\n transform: translateX(100%);\n opacity: 0;\n transition:\n transform 250ms cubic-bezier(0.8, 0, 0.6, 1),\n opacity 125ms 50ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(-100%);\n }\n }\n\n // Expand nested navigation, if toggle is checked\n .md-nav__toggle:checked ~ & {\n transform: translateX(0);\n opacity: 1;\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 125ms 125ms;\n\n // Hack: reduce jitter\n > .md-nav__list {\n backface-visibility: hidden;\n }\n }\n }\n\n // [tablet portrait -]: Show table of contents in drawer\n @include break-to-device(tablet portrait) {\n\n // Show link to table of contents - higher specificity is necessary to\n // display the table of contents inside the drawer\n html &__link[for=\"__toc\"] {\n display: block;\n padding-right: px2rem(48px);\n\n // Hide link to current item\n + .md-nav__link {\n display: none;\n }\n\n // Table of contents icon\n .md-icon::after {\n display: block;\n width: 100%;\n height: 100%;\n mask-image: var(--md-toc-icon);\n background-color: currentColor;\n content: \"\";\n }\n\n // Show table of contents\n & ~ .md-nav {\n display: flex;\n }\n }\n\n // Adjust for right-to-left languages\n html [dir=\"rtl\"] &__link {\n padding-right: px2rem(16px);\n padding-left: px2rem(48px);\n }\n\n // Repository containing source\n &__source {\n display: block;\n padding: 0 px2rem(4px);\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color--dark);\n }\n }\n\n // [tablet landscape +]: Tree-like navigation\n @include break-from-device(tablet landscape) {\n\n // List title\n &--secondary .md-nav__title {\n\n // Snap to table of contents title\n &[for=\"__toc\"] {\n scroll-snap-align: start;\n }\n\n // Hide icon\n .md-nav__icon {\n display: none;\n }\n }\n }\n\n // [screen +]: Tree-like navigation\n @include break-from-device(screen) {\n transition: max-height 250ms cubic-bezier(0.86, 0, 0.07, 1);\n\n // List title\n &--primary .md-nav__title {\n\n // Snap to site title\n &[for=\"__drawer\"] {\n scroll-snap-align: start;\n }\n\n // Hide icon\n .md-nav__icon {\n display: none;\n }\n }\n\n // Hide nested navigation by default\n .md-nav__toggle ~ & {\n display: none;\n }\n\n // Show nested navigation, if toggle is checked\n .md-nav__toggle:checked ~ & {\n display: block;\n }\n\n // Hide titles for nested navigation\n &__item--nested > .md-nav > &__title {\n display: none;\n }\n\n // Navigation icon\n &__icon {\n float: right;\n width: px2rem(18px);\n height: px2rem(18px);\n transition: transform 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n transform: rotate(180deg);\n }\n\n // Inline icon and adjust icon to match font size\n &::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n vertical-align: px2rem(-2px);\n background-color: currentColor;\n mask-image: var(--md-nav-icon--next);\n mask-repeat: no-repeat;\n content: \"\";\n }\n\n // Rotate icon for expanded lists\n .md-nav__item--nested .md-nav__toggle:checked ~ .md-nav__link & {\n transform: rotate(90deg);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n// Active (toggled) search\n$md-toggle__search--checked:\n \"[data-md-toggle=\\\"search\\\"]:checked ~ .md-header\";\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-search-result-icon: svg-load(\"@mdi/svg/svg/file-search-outline.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Search container\n.md-search {\n position: relative;\n\n // Hide search, if JavaScript is not available.\n .no-js & {\n display: none;\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n padding: px2rem(4px) 0;\n }\n\n // Search modal overlay\n &__overlay {\n z-index: 1;\n opacity: 0;\n\n // [tablet portrait -]: Full-screen search bar\n @include break-to-device(tablet portrait) {\n position: absolute;\n top: px2rem(4px);\n left: px2rem(-44px);\n width: px2rem(40px);\n height: px2rem(40px);\n overflow: hidden;\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(20px);\n transform-origin: center;\n transition:\n transform 300ms 100ms,\n opacity 200ms 200ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(-44px);\n left: initial;\n }\n\n // Expanded overlay\n #{$md-toggle__search--checked} & {\n opacity: 1;\n transition:\n transform 400ms,\n opacity 100ms;\n }\n }\n\n // Set scale factors\n #{$md-toggle__search--checked} & {\n\n // [mobile portrait -]: Scale up 45 times\n @include break-to-device(mobile portrait) {\n transform: scale(45);\n }\n\n // [mobile landscape]: Scale up 60 times\n @include break-at-device(mobile landscape) {\n transform: scale(60);\n }\n\n // [tablet portrait]: Scale up 75 times\n @include break-at-device(tablet portrait) {\n transform: scale(75);\n }\n }\n\n // [tablet landscape +]: Overlay for better focus on search\n @include break-from-device(tablet landscape) {\n position: fixed;\n top: 0;\n left: 0;\n width: 0;\n height: 0;\n background-color: hsla(0, 0%, 0%, 0.54);\n cursor: pointer;\n transition:\n width 0ms 250ms,\n height 0ms 250ms,\n opacity 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n }\n\n // Expanded overlay\n #{$md-toggle__search--checked} & {\n width: 100%;\n height: 100%;\n opacity: 1;\n transition:\n width 0ms,\n height 0ms,\n opacity 250ms;\n }\n }\n }\n\n // Search modal wrapper\n &__inner {\n // Hack: reduce jitter\n backface-visibility: hidden;\n\n // [tablet portrait -]: Put search modal off-canvas by default\n @include break-to-device(tablet portrait) {\n position: fixed;\n top: 0;\n left: 100%;\n z-index: 2;\n width: 100%;\n height: 100%;\n transform: translateX(5%);\n opacity: 0;\n transition:\n right 0ms 300ms,\n left 0ms 300ms,\n transform 150ms 150ms cubic-bezier(0.4, 0, 0.2, 1),\n opacity 150ms 150ms;\n\n // Active search modal\n #{$md-toggle__search--checked} & {\n left: 0;\n transform: translateX(0);\n opacity: 1;\n transition:\n right 0ms 0ms,\n left 0ms 0ms,\n transform 150ms 150ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms 150ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n }\n }\n\n // Adjust for right-to-left languages\n html [dir=\"rtl\"] & {\n right: 100%;\n left: initial;\n transform: translateX(-5%);\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n position: relative;\n float: right;\n width: px2rem(234px);\n padding: px2rem(2px) 0;\n transition: width 250ms cubic-bezier(0.1, 0.7, 0.1, 1);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: left;\n }\n }\n\n // Set maximum width\n #{$md-toggle__search--checked} & {\n\n // [tablet landscape]: Do not overlay title\n @include break-at-device(tablet landscape) {\n width: px2rem(468px);\n }\n\n // [screen +]: Match content width\n @include break-from-device(screen) {\n width: px2rem(688px);\n }\n }\n }\n\n // Search form\n &__form {\n position: relative;\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n border-radius: px2rem(2px);\n }\n }\n\n // Search input\n &__input {\n position: relative;\n z-index: 2;\n padding: 0 px2rem(44px) 0 px2rem(72px);\n text-overflow: ellipsis;\n background-color: var(--md-default-bg-color);\n transition:\n color 250ms,\n background-color 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding: 0 px2rem(72px) 0 px2rem(44px);\n }\n\n // Transition on placeholder\n &::placeholder {\n transition: color 250ms;\n }\n\n // Placeholder and icon color in active state\n ~ .md-search__icon,\n &::placeholder {\n color: var(--md-default-fg-color--light);\n }\n\n // Remove the \"x\" rendered by Internet Explorer\n &::-ms-clear {\n display: none;\n }\n\n // [tablet portrait -]: Full-screen search bar\n @include break-to-device(tablet portrait) {\n width: 100%;\n height: px2rem(48px);\n font-size: px2rem(18px);\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n width: 100%;\n height: px2rem(36px);\n padding-left: px2rem(44px);\n color: inherit;\n font-size: px2rem(16px);\n background-color: hsla(0, 0%, 0%, 0.26);\n border-radius: px2rem(2px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n }\n\n // Search icon color\n + .md-search__icon {\n color: var(--md-primary-bg-color);\n }\n\n // Placeholder color\n &::placeholder {\n color: var(--md-primary-bg-color--light);\n }\n\n // Hovered search field\n &:hover {\n background-color: hsla(0, 0%, 100%, 0.12);\n }\n\n // Set light background on active search field\n #{$md-toggle__search--checked} & {\n color: var(--md-default-fg-color);\n text-overflow: clip;\n background-color: var(--md-default-bg-color);\n border-radius: px2rem(2px) px2rem(2px) 0 0;\n\n // Search icon and placeholder color in active state\n + .md-search__icon,\n &::placeholder {\n color: var(--md-default-fg-color--light);\n }\n }\n }\n }\n\n // Search icon\n &__icon {\n position: absolute;\n z-index: 2;\n width: px2rem(24px);\n height: px2rem(24px);\n cursor: pointer;\n transition:\n color 250ms,\n opacity 250ms;\n\n // Hovered icon\n &:hover {\n opacity: 0.7;\n }\n\n // Search icon\n &[for=\"__search\"] {\n top: px2rem(6px);\n left: px2rem(10px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(10px);\n left: initial;\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1);\n }\n }\n\n // [tablet portrait -]: Full-screen search bar\n @include break-to-device(tablet portrait) {\n top: px2rem(12px);\n left: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(16px);\n left: initial;\n }\n\n // Hide the magnifying glass (1st icon)\n svg:first-child {\n display: none;\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n pointer-events: none;\n\n // Hide the arrow (2nd icon)\n svg:last-child {\n display: none;\n }\n }\n }\n\n // Reset button\n &[type=\"reset\"] {\n top: px2rem(6px);\n right: px2rem(10px);\n transform: scale(0.75);\n opacity: 0;\n transition:\n transform 150ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 150ms;\n pointer-events: none;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(10px);\n }\n\n // [tablet portrait -]: Full-screen search bar\n @include break-to-device(tablet portrait) {\n top: px2rem(12px);\n right: px2rem(16px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(16px);\n }\n }\n\n // Show reset button if search is active and input non-empty\n #{$md-toggle__search--checked}\n .md-search__input:not(:placeholder-shown) ~ & {\n transform: scale(1);\n opacity: 1;\n pointer-events: initial;\n\n // Hovered icon\n &:hover {\n opacity: 0.7;\n }\n }\n }\n }\n\n // Search output container\n &__output {\n position: absolute;\n z-index: 1;\n width: 100%;\n overflow: hidden;\n border-radius: 0 0 px2rem(2px) px2rem(2px);\n\n // [tablet portrait -]: Full-screen search bar\n @include break-to-device(tablet portrait) {\n top: px2rem(48px);\n bottom: 0;\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n top: px2rem(38px);\n opacity: 0;\n transition: opacity 400ms;\n\n // Show search output in active state\n #{$md-toggle__search--checked} & {\n @include z-depth(6);\n\n opacity: 1;\n }\n }\n }\n\n // Wrapper for scrolling on overflow\n &__scrollwrap {\n height: 100%;\n overflow-y: auto;\n background-color: var(--md-default-bg-color);\n // Hack: reduce jitter\n backface-visibility: hidden;\n scroll-snap-type: y mandatory;\n touch-action: pan-y;\n\n // Mitigiate excessive repaints on non-retina devices\n @media (max-resolution: 1dppx) {\n transform: translateZ(0);\n }\n\n // [tablet landscape]: Set absolute width to omit unnecessary reflow\n @include break-at-device(tablet landscape) {\n width: px2rem(468px);\n }\n\n // [screen +]: Set absolute width to omit unnecessary reflow\n @include break-from-device(screen) {\n width: px2rem(688px);\n }\n\n // [tablet landscape +]: Limit height to viewport\n @include break-from-device(tablet landscape) {\n max-height: 0;\n // Override Firefox scrollbar style\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Expand in active state\n #{$md-toggle__search--checked} & {\n max-height: 75vh;\n }\n\n // Override Firefox scrollbar hover color\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Override native scrollbar styles\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Hovered scrollbar thumb\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n }\n}\n\n// Search result\n.md-search-result {\n color: var(--md-default-fg-color);\n word-break: break-word;\n\n // Search metadata\n &__meta {\n padding: 0 px2rem(16px);\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n line-height: px2rem(36px);\n background-color: var(--md-default-fg-color--lightest);\n scroll-snap-align: start;\n\n // [tablet landscape +]: Increase left indent\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: initial;\n }\n }\n }\n\n // List of items\n &__list {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n\n // List item\n &__item {\n box-shadow: 0 px2rem(-1px) 0 var(--md-default-fg-color--lightest);\n\n // No border for first item\n &:first-child {\n box-shadow: none;\n }\n }\n\n // Link inside item\n &__link {\n display: block;\n outline: none;\n transition: background 250ms;\n scroll-snap-align: start;\n\n // Focused or hovered link\n &:focus,\n &:hover {\n background-color: var(--md-accent-fg-color--transparent);\n\n // Slightly transparent icon\n .md-search-result__article::before {\n opacity: 0.7;\n }\n }\n\n // Add a little spacing on the last element of the last link\n &:last-child p:last-child {\n margin-bottom: px2rem(12px);\n }\n }\n\n // Search result container\n &__more summary {\n display: block;\n padding: px2em(12px) px2rem(16px);\n color: var(--md-typeset-a-color);\n font-size: px2rem(12.8px);\n outline: 0;\n cursor: pointer;\n transition:\n color 250ms,\n background-color 250ms;\n scroll-snap-align: start;\n\n // Focused or hovered button\n &:focus,\n &:hover {\n color: var(--md-accent-fg-color);\n background-color: var(--md-accent-fg-color--transparent);\n }\n\n // [tablet landscape +]: Increase left indent\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: px2rem(16px);\n }\n }\n\n // Remove default details marker\n &::-webkit-details-marker {\n display: none;\n }\n\n // All following elements\n & ~ * {\n\n // Make less relevant terms more transparent\n > * {\n opacity: 0.65;\n }\n }\n }\n\n // Article - document or section\n &__article {\n position: relative;\n padding: 0 px2rem(16px);\n overflow: hidden;\n\n // [tablet landscape +]: Increase left indent\n @include break-from-device(tablet landscape) {\n padding-left: px2rem(44px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding-right: px2rem(44px);\n padding-left: px2rem(16px);\n }\n }\n\n // Document\n &--document {\n\n // Title\n .md-search-result__title {\n margin: px2rem(11px) 0;\n font-weight: 400;\n font-size: px2rem(16px);\n line-height: 1.4;\n }\n }\n }\n\n // Search result icon\n &__icon {\n position: absolute;\n left: 0;\n width: px2rem(24px);\n height: px2rem(24px);\n margin: px2rem(10px);\n color: var(--md-default-fg-color--light);\n\n // Inline icon and adjust icon to match font size\n &::after {\n display: inline-block;\n width: 100%;\n height: 100%;\n background-color: currentColor;\n mask-image: var(--md-search-result-icon);\n mask-repeat: no-repeat;\n content: \"\";\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: 0;\n left: initial;\n\n // Flip icon vertically\n &::after {\n transform: scaleX(-1);\n }\n }\n\n // [tablet portrait -]: Hide page icon\n @include break-to-device(tablet portrait) {\n display: none;\n }\n }\n\n // Title\n &__title {\n margin: 0.5em 0;\n font-weight: 700;\n font-size: px2rem(12.8px);\n line-height: 1.6;\n }\n\n // Teaser\n &__teaser {\n display: -webkit-box;\n max-height: px2rem(40px);\n margin: 0.5em 0;\n overflow: hidden;\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n line-height: 1.6;\n text-overflow: ellipsis;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n\n // [mobile -]: Increase number of lines\n @include break-to-device(mobile) {\n max-height: px2rem(60px);\n -webkit-line-clamp: 3;\n }\n\n // [tablet landscape]: Increase number of lines\n @include break-at-device(tablet landscape) {\n max-height: px2rem(60px);\n -webkit-line-clamp: 3;\n }\n\n // Search term highlighting\n mark {\n background-color: transparent;\n border-bottom: px2rem(1px) solid var(--md-accent-fg-color);\n }\n }\n\n // Terms\n &__terms {\n margin: 0.5em 0;\n font-size: px2rem(12.8px);\n font-style: italic;\n }\n\n // Search term highlighting\n mark {\n color: var(--md-accent-fg-color);\n background-color: transparent;\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n// Active (toggled) drawer\n$md-toggle__drawer--checked:\n \"[data-md-toggle=\\\"drawer\\\"]:checked ~ .md-container\";\n\n// ----------------------------------------------------------------------------\n// Keyframes\n// ----------------------------------------------------------------------------\n\n// Activate scroll snapping with delay\n@keyframes md-sidebar__scrollwrap--hack {\n 0%, 99% {\n scroll-snap-type: none;\n }\n\n 100% {\n scroll-snap-type: y mandatory;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Sidebar container\n.md-sidebar {\n position: sticky;\n top: px2rem(48px);\n align-self: flex-start;\n width: px2rem(242px);\n padding: px2rem(24px) 0;\n overflow: hidden;\n\n // Hide for print\n @media print {\n display: none;\n }\n\n // [tablet -]: Convert navigation to drawer\n @include break-to-device(tablet) {\n\n // Render primary sidebar as a slideout container\n &--primary {\n position: fixed;\n top: 0;\n left: px2rem(-242px);\n z-index: 3;\n width: px2rem(242px);\n height: 100%;\n background-color: var(--md-default-bg-color);\n transform: translateX(0);\n transition:\n transform 250ms cubic-bezier(0.4, 0, 0.2, 1),\n box-shadow 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(-242px);\n left: initial;\n }\n\n // Expanded drawer\n #{$md-toggle__drawer--checked} & {\n @include z-depth(8);\n\n transform: translateX(px2rem(242px));\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-242px));\n }\n }\n\n // Hide overflow for nested navigation\n .md-sidebar__scrollwrap {\n overflow: hidden;\n }\n }\n }\n\n // Secondary sidebar with table of contents\n &--secondary {\n display: none;\n order: 2;\n\n // [tablet landscape +]: Show table of contents next to body copy\n @include break-from-device(tablet landscape) {\n display: block;\n\n // Ensure smooth scrolling on iOS\n .md-sidebar__scrollwrap {\n touch-action: pan-y;\n }\n }\n }\n\n // Wrapper for scrolling on overflow\n &__scrollwrap {\n max-height: 100%;\n margin: 0 px2rem(4px);\n overflow-y: auto;\n // Hack: reduce jitter\n backface-visibility: hidden;\n // Override Firefox scrollbar style\n scrollbar-width: thin;\n scrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\n // Hack: Chrome 81+ exhibits a strange bug, where it scrolls the container\n // to the bottom if `scroll-snap-type` is set on the initial render. For\n // this reason, we use an animation to set scroll snaping with a slight\n // delay, which seems to fix the issue (#1667).\n .js & {\n animation: md-sidebar__scrollwrap--hack 400ms forwards;\n }\n\n // [tablet -]: Adjust margins\n @include break-to-device(tablet) {\n\n // Stretch scrollwrap for primary sidebar\n .md-sidebar--primary & {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n margin: 0;\n scroll-snap-type: none;\n }\n }\n\n // Override Firefox scrollbar hover color\n &:hover {\n scrollbar-color: var(--md-accent-fg-color) transparent;\n }\n\n // Override native scrollbar styles\n &::-webkit-scrollbar {\n width: px2rem(4px);\n height: px2rem(4px);\n }\n\n // Scrollbar thumb\n &::-webkit-scrollbar-thumb {\n background-color: var(--md-default-fg-color--lighter);\n\n // Hovered scrollbar thumb\n &:hover {\n background-color: var(--md-accent-fg-color);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Keyframes\n// ----------------------------------------------------------------------------\n\n// Show source facts\n@keyframes md-source__facts--done {\n 0% {\n height: 0;\n }\n\n 100% {\n height: px2rem(13px);\n }\n}\n\n// Show source fact\n@keyframes md-source__fact--done {\n 0% {\n transform: translateY(100%);\n opacity: 0;\n }\n\n 50% {\n opacity: 0;\n }\n\n 100% {\n transform: translateY(0%);\n opacity: 1;\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Source container\n.md-source {\n display: block;\n font-size: px2rem(13px);\n line-height: 1.2;\n white-space: nowrap;\n // Hack: reduce jitter\n backface-visibility: hidden;\n transition: opacity 250ms;\n\n // Hovered source container\n &:hover {\n opacity: 0.7;\n }\n\n // Repository platform icon\n &__icon {\n display: inline-block;\n width: px2rem(48px);\n height: px2rem(48px);\n vertical-align: middle;\n\n // Align with margin only (as opposed to normal button alignment)\n svg {\n margin-top: px2rem(12px);\n margin-left: px2rem(12px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(12px);\n margin-left: initial;\n }\n }\n\n // Correct alignment, if icon is present\n + .md-source__repository {\n margin-left: px2rem(-40px);\n padding-left: px2rem(40px);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(-40px);\n margin-left: initial;\n padding-right: px2rem(40px);\n padding-left: initial;\n }\n }\n }\n\n // Repository name\n &__repository {\n display: inline-block;\n max-width: calc(100% - #{px2rem(24px)});\n margin-left: px2rem(12px);\n overflow: hidden;\n font-weight: 700;\n text-overflow: ellipsis;\n vertical-align: middle;\n }\n\n // Source facts (statistics etc.)\n &__facts {\n margin: 0;\n padding: 0;\n overflow: hidden;\n font-weight: 700;\n font-size: px2rem(11px);\n list-style-type: none;\n opacity: 0.75;\n\n // Show after the data was loaded\n [data-md-state=\"done\"] & {\n animation: md-source__facts--done 250ms ease-in;\n }\n }\n\n // Fact\n &__fact {\n float: left;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n float: right;\n }\n\n // Show after the data was loaded\n [data-md-state=\"done\"] & {\n animation: md-source__fact--done 400ms ease-out;\n }\n\n // Middle dot before fact\n &::before {\n margin: 0 px2rem(2px);\n content: \"\\00B7\";\n }\n\n // Remove middle dot on first fact\n &:first-child::before {\n display: none;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Tabs with outline\n.md-tabs {\n width: 100%;\n overflow: auto;\n color: var(--md-primary-bg-color);\n background-color: var(--md-primary-fg-color);\n transition: background 250ms;\n\n // Omit transitions, in case JavaScript is not available\n .no-js & {\n transition: none;\n }\n\n // [tablet -]: Hide tabs for tablet and below, as they don't make any sense\n @include break-to-device(tablet) {\n display: none;\n }\n\n // Hide for print\n @media print {\n display: none;\n }\n\n // List of items\n &__list {\n margin: 0;\n margin-left: px2rem(4px);\n padding: 0;\n white-space: nowrap;\n list-style: none;\n contain: content;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(4px);\n margin-left: initial;\n }\n }\n\n // List item\n &__item {\n display: inline-block;\n height: px2rem(48px);\n padding-right: px2rem(12px);\n padding-left: px2rem(12px);\n }\n\n // Link inside item - could be defined as block elements and aligned via\n // line height, but this would imply more repaints when scrolling\n &__link {\n display: block;\n margin-top: px2rem(16px);\n font-size: px2rem(14px);\n opacity: 0.7;\n transition:\n transform 400ms cubic-bezier(0.1, 0.7, 0.1, 1),\n opacity 250ms;\n\n // Omit transitions, in case JavaScript is not available\n .no-js & {\n transition: none;\n }\n\n // Active or hovered link\n &--active,\n &:hover {\n color: inherit;\n opacity: 1;\n }\n\n // Delay transitions by a small amount\n @for $i from 2 through 16 {\n .md-tabs__item:nth-child(#{$i}) & {\n transition-delay: 20ms * ($i - 1);\n }\n }\n }\n\n // Fade-out tabs background upon scrolling\n &[data-md-state=\"hidden\"] {\n pointer-events: none;\n\n // Hide tabs upon scrolling - disable transition to minimizes repaints\n // while scrolling down, while scrolling up seems to be okay\n .md-tabs__link {\n transform: translateY(50%);\n opacity: 0;\n transition:\n color 250ms,\n transform 0ms 400ms,\n opacity 100ms;\n }\n }\n\n // [screen +]: Adjust main navigation styles\n @include break-from-device(screen) {\n\n // Hide 1st level nested items, as they are listed in the tabs\n ~ .md-main .md-nav--primary > .md-nav__list > .md-nav__item--nested {\n display: none;\n }\n\n // Active tab\n &--active ~ .md-main {\n\n // Adjust 1st level styles\n .md-nav--primary {\n\n // Show title and remove spacing\n .md-nav__title {\n display: block;\n padding: 0 px2rem(12px);\n pointer-events: none;\n scroll-snap-align: start;\n\n // Hide site title\n &[for=\"__drawer\"] {\n display: none;\n }\n }\n\n // Hide 1st level items\n > .md-nav__list > .md-nav__item {\n display: none;\n\n // Show 1st level active nested items\n &--active {\n display: block;\n padding: 0;\n\n // Hide nested links\n > .md-nav__link {\n display: none;\n }\n }\n }\n }\n\n // Always expand nested navigation on 2nd level\n .md-nav[data-md-level=\"1\"] {\n // Hack: Always show active navigation tab on breakpoint screen, despite\n // of checkbox being checked or not. Fixes #1655.\n display: block;\n\n // Remove spacing on 2nd level items\n > .md-nav__list > .md-nav__item {\n padding: 0 px2rem(12px);\n\n // Add bottom spacing to last item\n &:last-child {\n padding-bottom: px2rem(12px);\n\n // Remove bottom spacing for nested items\n .md-nav__item {\n padding-bottom: 0;\n }\n }\n }\n\n // Hide titles from 2nd level on\n .md-nav .md-nav__title {\n display: none;\n }\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n///\n/// Admonition flavours\n///\n$admonitions: (\n note: pencil $clr-blue-a200,\n abstract summary tldr: text-subject $clr-light-blue-a400,\n info todo: information $clr-cyan-a700,\n tip hint important: fire $clr-teal-a700,\n success check done: check-circle $clr-green-a700,\n question help faq: help-circle $clr-light-green-a700,\n warning caution attention: alert $clr-orange-a400,\n failure fail missing: close-circle $clr-red-a200,\n danger error: flash-circle $clr-red-a400,\n bug: bug $clr-pink-a400,\n example: format-list-numbered $clr-deep-purple-a400,\n quote cite: format-quote-close $clr-grey\n) !default;\n\n// ----------------------------------------------------------------------------\n// Rules: layout\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n @each $names, $props in $admonitions {\n $name: nth($names, 1);\n $icon: nth($props, 1);\n\n // Inline icon through PostCSS in Webpack\n --md-admonition-icon--#{$name}: svg-load(\"@mdi/svg/svg/#{$icon}.svg\");\n }\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Admonition extension\n .admonition {\n margin: px2em(20px, 12.8px) 0;\n padding: 0 px2rem(12px);\n overflow: hidden;\n color: var(--md-admonition-fg-color);\n font-size: px2rem(12.8px);\n page-break-inside: avoid;\n background-color: var(--md-admonition-bg-color);\n border-left: px2rem(4px) solid $clr-blue-a200;\n border-radius: px2rem(2px);\n box-shadow:\n 0 px2rem(4px) px2rem(10px) hsla(0, 0%, 0%, 0.05),\n 0 0 px2rem(1px) hsla(0, 0%, 0%, 0.1);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n border-right: px2rem(4px) solid $clr-blue-a200;\n border-left: none;\n }\n\n // Hack: omit rendering errors for print\n @media print {\n box-shadow: none;\n }\n\n // Adjust spacing on last element\n html & > :last-child {\n margin-bottom: px2rem(12px);\n }\n\n // Adjust margin for nested admonition blocks\n .admonition {\n margin: 1em 0;\n }\n\n // Wrapper for scrolling on overflow\n .md-typeset__scrollwrap {\n margin: 1em px2rem(-12px);\n }\n\n // Data table wrapper, in case JavaScript is available\n .md-typeset__table {\n padding: 0 px2rem(12px);\n }\n\n // Tabbed block container is the only child\n > .tabbed-set:only-child {\n margin-top: 0;\n }\n }\n\n // Admonition title\n .admonition-title {\n position: relative;\n margin: 0 px2rem(-12px) 0 px2rem(-16px);\n padding: px2rem(8px) px2rem(12px) px2rem(8px) px2rem(44px);\n font-weight: 700;\n background-color: transparentize($clr-blue-a200, 0.9);\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n margin: 0 px2rem(-16px) 0 px2rem(-12px);\n padding: px2rem(8px) px2rem(40px) px2rem(8px) px2rem(12px);\n }\n\n // Reset spacing, if title is the only element\n html &:last-child {\n margin-bottom: 0;\n }\n\n // Admonition icon\n &::before {\n position: absolute;\n left: px2rem(16px);\n width: px2rem(20px);\n height: px2rem(20px);\n background-color: $clr-blue-a200;\n mask-image: var(--md-admonition-icon--note);\n mask-repeat: no-repeat;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2rem(16px);\n left: initial;\n }\n }\n\n // Reset code inside admonition titles\n code {\n margin: initial;\n padding: initial;\n color: currentColor;\n background-color: transparent;\n border-radius: initial;\n box-shadow: none;\n }\n\n // Tabbed block container is the last child\n + .tabbed-set:last-child {\n margin-top: 0;\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: flavours\n// ----------------------------------------------------------------------------\n\n@each $names, $props in $admonitions {\n $name: nth($names, 1);\n $tint: nth($props, 2);\n\n // Admonition base class\n .md-typeset .admonition.#{$name} {\n border-color: $tint;\n }\n\n // Admonition title\n .md-typeset .#{$name} > .admonition-title {\n background-color: transparentize($tint, 0.9);\n\n // Admonition icon\n &::before {\n background-color: $tint;\n mask-image: var(--md-admonition-icon--#{$name});\n mask-repeat: no-repeat;\n }\n }\n\n // Define synonyms for base class\n @if length($names) > 1 {\n @for $n from 2 through length($names) {\n .#{nth($names, $n)} {\n @extend .#{$name};\n }\n }\n }\n}\n","// ==========================================================================\n//\n// Name: UI Color Palette\n// Description: The color palette of material design.\n// Version: 2.3.1\n//\n// Author: Denis Malinochkin\n// Git: https://github.com/mrmlnc/material-color\n//\n// twitter: @mrmlnc\n//\n// ==========================================================================\n\n\n//\n// List of base colors\n//\n\n// $clr-red\n// $clr-pink\n// $clr-purple\n// $clr-deep-purple\n// $clr-indigo\n// $clr-blue\n// $clr-light-blue\n// $clr-cyan\n// $clr-teal\n// $clr-green\n// $clr-light-green\n// $clr-lime\n// $clr-yellow\n// $clr-amber\n// $clr-orange\n// $clr-deep-orange\n// $clr-brown\n// $clr-grey\n// $clr-blue-grey\n// $clr-black\n// $clr-white\n\n\n//\n// Red\n//\n\n$clr-red-list: (\n \"base\": #f44336,\n \"50\": #ffebee,\n \"100\": #ffcdd2,\n \"200\": #ef9a9a,\n \"300\": #e57373,\n \"400\": #ef5350,\n \"500\": #f44336,\n \"600\": #e53935,\n \"700\": #d32f2f,\n \"800\": #c62828,\n \"900\": #b71c1c,\n \"a100\": #ff8a80,\n \"a200\": #ff5252,\n \"a400\": #ff1744,\n \"a700\": #d50000\n);\n\n$clr-red: map-get($clr-red-list, \"base\");\n\n$clr-red-50: map-get($clr-red-list, \"50\");\n$clr-red-100: map-get($clr-red-list, \"100\");\n$clr-red-200: map-get($clr-red-list, \"200\");\n$clr-red-300: map-get($clr-red-list, \"300\");\n$clr-red-400: map-get($clr-red-list, \"400\");\n$clr-red-500: map-get($clr-red-list, \"500\");\n$clr-red-600: map-get($clr-red-list, \"600\");\n$clr-red-700: map-get($clr-red-list, \"700\");\n$clr-red-800: map-get($clr-red-list, \"800\");\n$clr-red-900: map-get($clr-red-list, \"900\");\n$clr-red-a100: map-get($clr-red-list, \"a100\");\n$clr-red-a200: map-get($clr-red-list, \"a200\");\n$clr-red-a400: map-get($clr-red-list, \"a400\");\n$clr-red-a700: map-get($clr-red-list, \"a700\");\n\n\n//\n// Pink\n//\n\n$clr-pink-list: (\n \"base\": #e91e63,\n \"50\": #fce4ec,\n \"100\": #f8bbd0,\n \"200\": #f48fb1,\n \"300\": #f06292,\n \"400\": #ec407a,\n \"500\": #e91e63,\n \"600\": #d81b60,\n \"700\": #c2185b,\n \"800\": #ad1457,\n \"900\": #880e4f,\n \"a100\": #ff80ab,\n \"a200\": #ff4081,\n \"a400\": #f50057,\n \"a700\": #c51162\n);\n\n$clr-pink: map-get($clr-pink-list, \"base\");\n\n$clr-pink-50: map-get($clr-pink-list, \"50\");\n$clr-pink-100: map-get($clr-pink-list, \"100\");\n$clr-pink-200: map-get($clr-pink-list, \"200\");\n$clr-pink-300: map-get($clr-pink-list, \"300\");\n$clr-pink-400: map-get($clr-pink-list, \"400\");\n$clr-pink-500: map-get($clr-pink-list, \"500\");\n$clr-pink-600: map-get($clr-pink-list, \"600\");\n$clr-pink-700: map-get($clr-pink-list, \"700\");\n$clr-pink-800: map-get($clr-pink-list, \"800\");\n$clr-pink-900: map-get($clr-pink-list, \"900\");\n$clr-pink-a100: map-get($clr-pink-list, \"a100\");\n$clr-pink-a200: map-get($clr-pink-list, \"a200\");\n$clr-pink-a400: map-get($clr-pink-list, \"a400\");\n$clr-pink-a700: map-get($clr-pink-list, \"a700\");\n\n\n//\n// Purple\n//\n\n$clr-purple-list: (\n \"base\": #9c27b0,\n \"50\": #f3e5f5,\n \"100\": #e1bee7,\n \"200\": #ce93d8,\n \"300\": #ba68c8,\n \"400\": #ab47bc,\n \"500\": #9c27b0,\n \"600\": #8e24aa,\n \"700\": #7b1fa2,\n \"800\": #6a1b9a,\n \"900\": #4a148c,\n \"a100\": #ea80fc,\n \"a200\": #e040fb,\n \"a400\": #d500f9,\n \"a700\": #aa00ff\n);\n\n$clr-purple: map-get($clr-purple-list, \"base\");\n\n$clr-purple-50: map-get($clr-purple-list, \"50\");\n$clr-purple-100: map-get($clr-purple-list, \"100\");\n$clr-purple-200: map-get($clr-purple-list, \"200\");\n$clr-purple-300: map-get($clr-purple-list, \"300\");\n$clr-purple-400: map-get($clr-purple-list, \"400\");\n$clr-purple-500: map-get($clr-purple-list, \"500\");\n$clr-purple-600: map-get($clr-purple-list, \"600\");\n$clr-purple-700: map-get($clr-purple-list, \"700\");\n$clr-purple-800: map-get($clr-purple-list, \"800\");\n$clr-purple-900: map-get($clr-purple-list, \"900\");\n$clr-purple-a100: map-get($clr-purple-list, \"a100\");\n$clr-purple-a200: map-get($clr-purple-list, \"a200\");\n$clr-purple-a400: map-get($clr-purple-list, \"a400\");\n$clr-purple-a700: map-get($clr-purple-list, \"a700\");\n\n\n//\n// Deep purple\n//\n\n$clr-deep-purple-list: (\n \"base\": #673ab7,\n \"50\": #ede7f6,\n \"100\": #d1c4e9,\n \"200\": #b39ddb,\n \"300\": #9575cd,\n \"400\": #7e57c2,\n \"500\": #673ab7,\n \"600\": #5e35b1,\n \"700\": #512da8,\n \"800\": #4527a0,\n \"900\": #311b92,\n \"a100\": #b388ff,\n \"a200\": #7c4dff,\n \"a400\": #651fff,\n \"a700\": #6200ea\n);\n\n$clr-deep-purple: map-get($clr-deep-purple-list, \"base\");\n\n$clr-deep-purple-50: map-get($clr-deep-purple-list, \"50\");\n$clr-deep-purple-100: map-get($clr-deep-purple-list, \"100\");\n$clr-deep-purple-200: map-get($clr-deep-purple-list, \"200\");\n$clr-deep-purple-300: map-get($clr-deep-purple-list, \"300\");\n$clr-deep-purple-400: map-get($clr-deep-purple-list, \"400\");\n$clr-deep-purple-500: map-get($clr-deep-purple-list, \"500\");\n$clr-deep-purple-600: map-get($clr-deep-purple-list, \"600\");\n$clr-deep-purple-700: map-get($clr-deep-purple-list, \"700\");\n$clr-deep-purple-800: map-get($clr-deep-purple-list, \"800\");\n$clr-deep-purple-900: map-get($clr-deep-purple-list, \"900\");\n$clr-deep-purple-a100: map-get($clr-deep-purple-list, \"a100\");\n$clr-deep-purple-a200: map-get($clr-deep-purple-list, \"a200\");\n$clr-deep-purple-a400: map-get($clr-deep-purple-list, \"a400\");\n$clr-deep-purple-a700: map-get($clr-deep-purple-list, \"a700\");\n\n\n//\n// Indigo\n//\n\n$clr-indigo-list: (\n \"base\": #3f51b5,\n \"50\": #e8eaf6,\n \"100\": #c5cae9,\n \"200\": #9fa8da,\n \"300\": #7986cb,\n \"400\": #5c6bc0,\n \"500\": #3f51b5,\n \"600\": #3949ab,\n \"700\": #303f9f,\n \"800\": #283593,\n \"900\": #1a237e,\n \"a100\": #8c9eff,\n \"a200\": #536dfe,\n \"a400\": #3d5afe,\n \"a700\": #304ffe\n);\n\n$clr-indigo: map-get($clr-indigo-list, \"base\");\n\n$clr-indigo-50: map-get($clr-indigo-list, \"50\");\n$clr-indigo-100: map-get($clr-indigo-list, \"100\");\n$clr-indigo-200: map-get($clr-indigo-list, \"200\");\n$clr-indigo-300: map-get($clr-indigo-list, \"300\");\n$clr-indigo-400: map-get($clr-indigo-list, \"400\");\n$clr-indigo-500: map-get($clr-indigo-list, \"500\");\n$clr-indigo-600: map-get($clr-indigo-list, \"600\");\n$clr-indigo-700: map-get($clr-indigo-list, \"700\");\n$clr-indigo-800: map-get($clr-indigo-list, \"800\");\n$clr-indigo-900: map-get($clr-indigo-list, \"900\");\n$clr-indigo-a100: map-get($clr-indigo-list, \"a100\");\n$clr-indigo-a200: map-get($clr-indigo-list, \"a200\");\n$clr-indigo-a400: map-get($clr-indigo-list, \"a400\");\n$clr-indigo-a700: map-get($clr-indigo-list, \"a700\");\n\n\n//\n// Blue\n//\n\n$clr-blue-list: (\n \"base\": #2196f3,\n \"50\": #e3f2fd,\n \"100\": #bbdefb,\n \"200\": #90caf9,\n \"300\": #64b5f6,\n \"400\": #42a5f5,\n \"500\": #2196f3,\n \"600\": #1e88e5,\n \"700\": #1976d2,\n \"800\": #1565c0,\n \"900\": #0d47a1,\n \"a100\": #82b1ff,\n \"a200\": #448aff,\n \"a400\": #2979ff,\n \"a700\": #2962ff\n);\n\n$clr-blue: map-get($clr-blue-list, \"base\");\n\n$clr-blue-50: map-get($clr-blue-list, \"50\");\n$clr-blue-100: map-get($clr-blue-list, \"100\");\n$clr-blue-200: map-get($clr-blue-list, \"200\");\n$clr-blue-300: map-get($clr-blue-list, \"300\");\n$clr-blue-400: map-get($clr-blue-list, \"400\");\n$clr-blue-500: map-get($clr-blue-list, \"500\");\n$clr-blue-600: map-get($clr-blue-list, \"600\");\n$clr-blue-700: map-get($clr-blue-list, \"700\");\n$clr-blue-800: map-get($clr-blue-list, \"800\");\n$clr-blue-900: map-get($clr-blue-list, \"900\");\n$clr-blue-a100: map-get($clr-blue-list, \"a100\");\n$clr-blue-a200: map-get($clr-blue-list, \"a200\");\n$clr-blue-a400: map-get($clr-blue-list, \"a400\");\n$clr-blue-a700: map-get($clr-blue-list, \"a700\");\n\n\n//\n// Light Blue\n//\n\n$clr-light-blue-list: (\n \"base\": #03a9f4,\n \"50\": #e1f5fe,\n \"100\": #b3e5fc,\n \"200\": #81d4fa,\n \"300\": #4fc3f7,\n \"400\": #29b6f6,\n \"500\": #03a9f4,\n \"600\": #039be5,\n \"700\": #0288d1,\n \"800\": #0277bd,\n \"900\": #01579b,\n \"a100\": #80d8ff,\n \"a200\": #40c4ff,\n \"a400\": #00b0ff,\n \"a700\": #0091ea\n);\n\n$clr-light-blue: map-get($clr-light-blue-list, \"base\");\n\n$clr-light-blue-50: map-get($clr-light-blue-list, \"50\");\n$clr-light-blue-100: map-get($clr-light-blue-list, \"100\");\n$clr-light-blue-200: map-get($clr-light-blue-list, \"200\");\n$clr-light-blue-300: map-get($clr-light-blue-list, \"300\");\n$clr-light-blue-400: map-get($clr-light-blue-list, \"400\");\n$clr-light-blue-500: map-get($clr-light-blue-list, \"500\");\n$clr-light-blue-600: map-get($clr-light-blue-list, \"600\");\n$clr-light-blue-700: map-get($clr-light-blue-list, \"700\");\n$clr-light-blue-800: map-get($clr-light-blue-list, \"800\");\n$clr-light-blue-900: map-get($clr-light-blue-list, \"900\");\n$clr-light-blue-a100: map-get($clr-light-blue-list, \"a100\");\n$clr-light-blue-a200: map-get($clr-light-blue-list, \"a200\");\n$clr-light-blue-a400: map-get($clr-light-blue-list, \"a400\");\n$clr-light-blue-a700: map-get($clr-light-blue-list, \"a700\");\n\n\n//\n// Cyan\n//\n\n$clr-cyan-list: (\n \"base\": #00bcd4,\n \"50\": #e0f7fa,\n \"100\": #b2ebf2,\n \"200\": #80deea,\n \"300\": #4dd0e1,\n \"400\": #26c6da,\n \"500\": #00bcd4,\n \"600\": #00acc1,\n \"700\": #0097a7,\n \"800\": #00838f,\n \"900\": #006064,\n \"a100\": #84ffff,\n \"a200\": #18ffff,\n \"a400\": #00e5ff,\n \"a700\": #00b8d4\n);\n\n$clr-cyan: map-get($clr-cyan-list, \"base\");\n\n$clr-cyan-50: map-get($clr-cyan-list, \"50\");\n$clr-cyan-100: map-get($clr-cyan-list, \"100\");\n$clr-cyan-200: map-get($clr-cyan-list, \"200\");\n$clr-cyan-300: map-get($clr-cyan-list, \"300\");\n$clr-cyan-400: map-get($clr-cyan-list, \"400\");\n$clr-cyan-500: map-get($clr-cyan-list, \"500\");\n$clr-cyan-600: map-get($clr-cyan-list, \"600\");\n$clr-cyan-700: map-get($clr-cyan-list, \"700\");\n$clr-cyan-800: map-get($clr-cyan-list, \"800\");\n$clr-cyan-900: map-get($clr-cyan-list, \"900\");\n$clr-cyan-a100: map-get($clr-cyan-list, \"a100\");\n$clr-cyan-a200: map-get($clr-cyan-list, \"a200\");\n$clr-cyan-a400: map-get($clr-cyan-list, \"a400\");\n$clr-cyan-a700: map-get($clr-cyan-list, \"a700\");\n\n\n//\n// Teal\n//\n\n$clr-teal-list: (\n \"base\": #009688,\n \"50\": #e0f2f1,\n \"100\": #b2dfdb,\n \"200\": #80cbc4,\n \"300\": #4db6ac,\n \"400\": #26a69a,\n \"500\": #009688,\n \"600\": #00897b,\n \"700\": #00796b,\n \"800\": #00695c,\n \"900\": #004d40,\n \"a100\": #a7ffeb,\n \"a200\": #64ffda,\n \"a400\": #1de9b6,\n \"a700\": #00bfa5\n);\n\n$clr-teal: map-get($clr-teal-list, \"base\");\n\n$clr-teal-50: map-get($clr-teal-list, \"50\");\n$clr-teal-100: map-get($clr-teal-list, \"100\");\n$clr-teal-200: map-get($clr-teal-list, \"200\");\n$clr-teal-300: map-get($clr-teal-list, \"300\");\n$clr-teal-400: map-get($clr-teal-list, \"400\");\n$clr-teal-500: map-get($clr-teal-list, \"500\");\n$clr-teal-600: map-get($clr-teal-list, \"600\");\n$clr-teal-700: map-get($clr-teal-list, \"700\");\n$clr-teal-800: map-get($clr-teal-list, \"800\");\n$clr-teal-900: map-get($clr-teal-list, \"900\");\n$clr-teal-a100: map-get($clr-teal-list, \"a100\");\n$clr-teal-a200: map-get($clr-teal-list, \"a200\");\n$clr-teal-a400: map-get($clr-teal-list, \"a400\");\n$clr-teal-a700: map-get($clr-teal-list, \"a700\");\n\n\n//\n// Green\n//\n\n$clr-green-list: (\n \"base\": #4caf50,\n \"50\": #e8f5e9,\n \"100\": #c8e6c9,\n \"200\": #a5d6a7,\n \"300\": #81c784,\n \"400\": #66bb6a,\n \"500\": #4caf50,\n \"600\": #43a047,\n \"700\": #388e3c,\n \"800\": #2e7d32,\n \"900\": #1b5e20,\n \"a100\": #b9f6ca,\n \"a200\": #69f0ae,\n \"a400\": #00e676,\n \"a700\": #00c853\n);\n\n$clr-green: map-get($clr-green-list, \"base\");\n\n$clr-green-50: map-get($clr-green-list, \"50\");\n$clr-green-100: map-get($clr-green-list, \"100\");\n$clr-green-200: map-get($clr-green-list, \"200\");\n$clr-green-300: map-get($clr-green-list, \"300\");\n$clr-green-400: map-get($clr-green-list, \"400\");\n$clr-green-500: map-get($clr-green-list, \"500\");\n$clr-green-600: map-get($clr-green-list, \"600\");\n$clr-green-700: map-get($clr-green-list, \"700\");\n$clr-green-800: map-get($clr-green-list, \"800\");\n$clr-green-900: map-get($clr-green-list, \"900\");\n$clr-green-a100: map-get($clr-green-list, \"a100\");\n$clr-green-a200: map-get($clr-green-list, \"a200\");\n$clr-green-a400: map-get($clr-green-list, \"a400\");\n$clr-green-a700: map-get($clr-green-list, \"a700\");\n\n\n//\n// Light green\n//\n\n$clr-light-green-list: (\n \"base\": #8bc34a,\n \"50\": #f1f8e9,\n \"100\": #dcedc8,\n \"200\": #c5e1a5,\n \"300\": #aed581,\n \"400\": #9ccc65,\n \"500\": #8bc34a,\n \"600\": #7cb342,\n \"700\": #689f38,\n \"800\": #558b2f,\n \"900\": #33691e,\n \"a100\": #ccff90,\n \"a200\": #b2ff59,\n \"a400\": #76ff03,\n \"a700\": #64dd17\n);\n\n$clr-light-green: map-get($clr-light-green-list, \"base\");\n\n$clr-light-green-50: map-get($clr-light-green-list, \"50\");\n$clr-light-green-100: map-get($clr-light-green-list, \"100\");\n$clr-light-green-200: map-get($clr-light-green-list, \"200\");\n$clr-light-green-300: map-get($clr-light-green-list, \"300\");\n$clr-light-green-400: map-get($clr-light-green-list, \"400\");\n$clr-light-green-500: map-get($clr-light-green-list, \"500\");\n$clr-light-green-600: map-get($clr-light-green-list, \"600\");\n$clr-light-green-700: map-get($clr-light-green-list, \"700\");\n$clr-light-green-800: map-get($clr-light-green-list, \"800\");\n$clr-light-green-900: map-get($clr-light-green-list, \"900\");\n$clr-light-green-a100: map-get($clr-light-green-list, \"a100\");\n$clr-light-green-a200: map-get($clr-light-green-list, \"a200\");\n$clr-light-green-a400: map-get($clr-light-green-list, \"a400\");\n$clr-light-green-a700: map-get($clr-light-green-list, \"a700\");\n\n\n//\n// Lime\n//\n\n$clr-lime-list: (\n \"base\": #cddc39,\n \"50\": #f9fbe7,\n \"100\": #f0f4c3,\n \"200\": #e6ee9c,\n \"300\": #dce775,\n \"400\": #d4e157,\n \"500\": #cddc39,\n \"600\": #c0ca33,\n \"700\": #afb42b,\n \"800\": #9e9d24,\n \"900\": #827717,\n \"a100\": #f4ff81,\n \"a200\": #eeff41,\n \"a400\": #c6ff00,\n \"a700\": #aeea00\n);\n\n$clr-lime: map-get($clr-lime-list, \"base\");\n\n$clr-lime-50: map-get($clr-lime-list, \"50\");\n$clr-lime-100: map-get($clr-lime-list, \"100\");\n$clr-lime-200: map-get($clr-lime-list, \"200\");\n$clr-lime-300: map-get($clr-lime-list, \"300\");\n$clr-lime-400: map-get($clr-lime-list, \"400\");\n$clr-lime-500: map-get($clr-lime-list, \"500\");\n$clr-lime-600: map-get($clr-lime-list, \"600\");\n$clr-lime-700: map-get($clr-lime-list, \"700\");\n$clr-lime-800: map-get($clr-lime-list, \"800\");\n$clr-lime-900: map-get($clr-lime-list, \"900\");\n$clr-lime-a100: map-get($clr-lime-list, \"a100\");\n$clr-lime-a200: map-get($clr-lime-list, \"a200\");\n$clr-lime-a400: map-get($clr-lime-list, \"a400\");\n$clr-lime-a700: map-get($clr-lime-list, \"a700\");\n\n\n//\n// Yellow\n//\n\n$clr-yellow-list: (\n \"base\": #ffeb3b,\n \"50\": #fffde7,\n \"100\": #fff9c4,\n \"200\": #fff59d,\n \"300\": #fff176,\n \"400\": #ffee58,\n \"500\": #ffeb3b,\n \"600\": #fdd835,\n \"700\": #fbc02d,\n \"800\": #f9a825,\n \"900\": #f57f17,\n \"a100\": #ffff8d,\n \"a200\": #ffff00,\n \"a400\": #ffea00,\n \"a700\": #ffd600\n);\n\n$clr-yellow: map-get($clr-yellow-list, \"base\");\n\n$clr-yellow-50: map-get($clr-yellow-list, \"50\");\n$clr-yellow-100: map-get($clr-yellow-list, \"100\");\n$clr-yellow-200: map-get($clr-yellow-list, \"200\");\n$clr-yellow-300: map-get($clr-yellow-list, \"300\");\n$clr-yellow-400: map-get($clr-yellow-list, \"400\");\n$clr-yellow-500: map-get($clr-yellow-list, \"500\");\n$clr-yellow-600: map-get($clr-yellow-list, \"600\");\n$clr-yellow-700: map-get($clr-yellow-list, \"700\");\n$clr-yellow-800: map-get($clr-yellow-list, \"800\");\n$clr-yellow-900: map-get($clr-yellow-list, \"900\");\n$clr-yellow-a100: map-get($clr-yellow-list, \"a100\");\n$clr-yellow-a200: map-get($clr-yellow-list, \"a200\");\n$clr-yellow-a400: map-get($clr-yellow-list, \"a400\");\n$clr-yellow-a700: map-get($clr-yellow-list, \"a700\");\n\n\n//\n// amber\n//\n\n$clr-amber-list: (\n \"base\": #ffc107,\n \"50\": #fff8e1,\n \"100\": #ffecb3,\n \"200\": #ffe082,\n \"300\": #ffd54f,\n \"400\": #ffca28,\n \"500\": #ffc107,\n \"600\": #ffb300,\n \"700\": #ffa000,\n \"800\": #ff8f00,\n \"900\": #ff6f00,\n \"a100\": #ffe57f,\n \"a200\": #ffd740,\n \"a400\": #ffc400,\n \"a700\": #ffab00\n);\n\n$clr-amber: map-get($clr-amber-list, \"base\");\n\n$clr-amber-50: map-get($clr-amber-list, \"50\");\n$clr-amber-100: map-get($clr-amber-list, \"100\");\n$clr-amber-200: map-get($clr-amber-list, \"200\");\n$clr-amber-300: map-get($clr-amber-list, \"300\");\n$clr-amber-400: map-get($clr-amber-list, \"400\");\n$clr-amber-500: map-get($clr-amber-list, \"500\");\n$clr-amber-600: map-get($clr-amber-list, \"600\");\n$clr-amber-700: map-get($clr-amber-list, \"700\");\n$clr-amber-800: map-get($clr-amber-list, \"800\");\n$clr-amber-900: map-get($clr-amber-list, \"900\");\n$clr-amber-a100: map-get($clr-amber-list, \"a100\");\n$clr-amber-a200: map-get($clr-amber-list, \"a200\");\n$clr-amber-a400: map-get($clr-amber-list, \"a400\");\n$clr-amber-a700: map-get($clr-amber-list, \"a700\");\n\n\n//\n// Orange\n//\n\n$clr-orange-list: (\n \"base\": #ff9800,\n \"50\": #fff3e0,\n \"100\": #ffe0b2,\n \"200\": #ffcc80,\n \"300\": #ffb74d,\n \"400\": #ffa726,\n \"500\": #ff9800,\n \"600\": #fb8c00,\n \"700\": #f57c00,\n \"800\": #ef6c00,\n \"900\": #e65100,\n \"a100\": #ffd180,\n \"a200\": #ffab40,\n \"a400\": #ff9100,\n \"a700\": #ff6d00\n);\n\n$clr-orange: map-get($clr-orange-list, \"base\");\n\n$clr-orange-50: map-get($clr-orange-list, \"50\");\n$clr-orange-100: map-get($clr-orange-list, \"100\");\n$clr-orange-200: map-get($clr-orange-list, \"200\");\n$clr-orange-300: map-get($clr-orange-list, \"300\");\n$clr-orange-400: map-get($clr-orange-list, \"400\");\n$clr-orange-500: map-get($clr-orange-list, \"500\");\n$clr-orange-600: map-get($clr-orange-list, \"600\");\n$clr-orange-700: map-get($clr-orange-list, \"700\");\n$clr-orange-800: map-get($clr-orange-list, \"800\");\n$clr-orange-900: map-get($clr-orange-list, \"900\");\n$clr-orange-a100: map-get($clr-orange-list, \"a100\");\n$clr-orange-a200: map-get($clr-orange-list, \"a200\");\n$clr-orange-a400: map-get($clr-orange-list, \"a400\");\n$clr-orange-a700: map-get($clr-orange-list, \"a700\");\n\n\n//\n// Deep orange\n//\n\n$clr-deep-orange-list: (\n \"base\": #ff5722,\n \"50\": #fbe9e7,\n \"100\": #ffccbc,\n \"200\": #ffab91,\n \"300\": #ff8a65,\n \"400\": #ff7043,\n \"500\": #ff5722,\n \"600\": #f4511e,\n \"700\": #e64a19,\n \"800\": #d84315,\n \"900\": #bf360c,\n \"a100\": #ff9e80,\n \"a200\": #ff6e40,\n \"a400\": #ff3d00,\n \"a700\": #dd2c00\n);\n\n$clr-deep-orange: map-get($clr-deep-orange-list, \"base\");\n\n$clr-deep-orange-50: map-get($clr-deep-orange-list, \"50\");\n$clr-deep-orange-100: map-get($clr-deep-orange-list, \"100\");\n$clr-deep-orange-200: map-get($clr-deep-orange-list, \"200\");\n$clr-deep-orange-300: map-get($clr-deep-orange-list, \"300\");\n$clr-deep-orange-400: map-get($clr-deep-orange-list, \"400\");\n$clr-deep-orange-500: map-get($clr-deep-orange-list, \"500\");\n$clr-deep-orange-600: map-get($clr-deep-orange-list, \"600\");\n$clr-deep-orange-700: map-get($clr-deep-orange-list, \"700\");\n$clr-deep-orange-800: map-get($clr-deep-orange-list, \"800\");\n$clr-deep-orange-900: map-get($clr-deep-orange-list, \"900\");\n$clr-deep-orange-a100: map-get($clr-deep-orange-list, \"a100\");\n$clr-deep-orange-a200: map-get($clr-deep-orange-list, \"a200\");\n$clr-deep-orange-a400: map-get($clr-deep-orange-list, \"a400\");\n$clr-deep-orange-a700: map-get($clr-deep-orange-list, \"a700\");\n\n\n//\n// Brown\n//\n\n$clr-brown-list: (\n \"base\": #795548,\n \"50\": #efebe9,\n \"100\": #d7ccc8,\n \"200\": #bcaaa4,\n \"300\": #a1887f,\n \"400\": #8d6e63,\n \"500\": #795548,\n \"600\": #6d4c41,\n \"700\": #5d4037,\n \"800\": #4e342e,\n \"900\": #3e2723,\n);\n\n$clr-brown: map-get($clr-brown-list, \"base\");\n\n$clr-brown-50: map-get($clr-brown-list, \"50\");\n$clr-brown-100: map-get($clr-brown-list, \"100\");\n$clr-brown-200: map-get($clr-brown-list, \"200\");\n$clr-brown-300: map-get($clr-brown-list, \"300\");\n$clr-brown-400: map-get($clr-brown-list, \"400\");\n$clr-brown-500: map-get($clr-brown-list, \"500\");\n$clr-brown-600: map-get($clr-brown-list, \"600\");\n$clr-brown-700: map-get($clr-brown-list, \"700\");\n$clr-brown-800: map-get($clr-brown-list, \"800\");\n$clr-brown-900: map-get($clr-brown-list, \"900\");\n\n\n//\n// Grey\n//\n\n$clr-grey-list: (\n \"base\": #9e9e9e,\n \"50\": #fafafa,\n \"100\": #f5f5f5,\n \"200\": #eeeeee,\n \"300\": #e0e0e0,\n \"400\": #bdbdbd,\n \"500\": #9e9e9e,\n \"600\": #757575,\n \"700\": #616161,\n \"800\": #424242,\n \"900\": #212121,\n);\n\n$clr-grey: map-get($clr-grey-list, \"base\");\n\n$clr-grey-50: map-get($clr-grey-list, \"50\");\n$clr-grey-100: map-get($clr-grey-list, \"100\");\n$clr-grey-200: map-get($clr-grey-list, \"200\");\n$clr-grey-300: map-get($clr-grey-list, \"300\");\n$clr-grey-400: map-get($clr-grey-list, \"400\");\n$clr-grey-500: map-get($clr-grey-list, \"500\");\n$clr-grey-600: map-get($clr-grey-list, \"600\");\n$clr-grey-700: map-get($clr-grey-list, \"700\");\n$clr-grey-800: map-get($clr-grey-list, \"800\");\n$clr-grey-900: map-get($clr-grey-list, \"900\");\n\n\n//\n// Blue grey\n//\n\n$clr-blue-grey-list: (\n \"base\": #607d8b,\n \"50\": #eceff1,\n \"100\": #cfd8dc,\n \"200\": #b0bec5,\n \"300\": #90a4ae,\n \"400\": #78909c,\n \"500\": #607d8b,\n \"600\": #546e7a,\n \"700\": #455a64,\n \"800\": #37474f,\n \"900\": #263238,\n);\n\n$clr-blue-grey: map-get($clr-blue-grey-list, \"base\");\n\n$clr-blue-grey-50: map-get($clr-blue-grey-list, \"50\");\n$clr-blue-grey-100: map-get($clr-blue-grey-list, \"100\");\n$clr-blue-grey-200: map-get($clr-blue-grey-list, \"200\");\n$clr-blue-grey-300: map-get($clr-blue-grey-list, \"300\");\n$clr-blue-grey-400: map-get($clr-blue-grey-list, \"400\");\n$clr-blue-grey-500: map-get($clr-blue-grey-list, \"500\");\n$clr-blue-grey-600: map-get($clr-blue-grey-list, \"600\");\n$clr-blue-grey-700: map-get($clr-blue-grey-list, \"700\");\n$clr-blue-grey-800: map-get($clr-blue-grey-list, \"800\");\n$clr-blue-grey-900: map-get($clr-blue-grey-list, \"900\");\n\n\n//\n// Black\n//\n\n$clr-black-list: (\n \"base\": #000\n);\n\n$clr-black: map-get($clr-black-list, \"base\");\n\n\n//\n// White\n//\n\n$clr-white-list: (\n \"base\": #fff\n);\n\n$clr-white: map-get($clr-white-list, \"base\");\n\n\n//\n// List for all Colors for looping\n//\n\n$clr-list-all: (\n \"red\": $clr-red-list,\n \"pink\": $clr-pink-list,\n \"purple\": $clr-purple-list,\n \"deep-purple\": $clr-deep-purple-list,\n \"indigo\": $clr-indigo-list,\n \"blue\": $clr-blue-list,\n \"light-blue\": $clr-light-blue-list,\n \"cyan\": $clr-cyan-list,\n \"teal\": $clr-teal-list,\n \"green\": $clr-green-list,\n \"light-green\": $clr-light-green-list,\n \"lime\": $clr-lime-list,\n \"yellow\": $clr-yellow-list,\n \"amber\": $clr-amber-list,\n \"orange\": $clr-orange-list,\n \"deep-orange\": $clr-deep-orange-list,\n \"brown\": $clr-brown-list,\n \"grey\": $clr-grey-list,\n \"blue-grey\": $clr-blue-grey-list,\n \"black\": $clr-black-list,\n \"white\": $clr-white-list\n);\n\n\n//\n// Typography\n//\n\n$clr-ui-display-4: $clr-grey-600;\n$clr-ui-display-3: $clr-grey-600;\n$clr-ui-display-2: $clr-grey-600;\n$clr-ui-display-1: $clr-grey-600;\n$clr-ui-headline: $clr-grey-900;\n$clr-ui-title: $clr-grey-900;\n$clr-ui-subhead-1: $clr-grey-900;\n$clr-ui-body-2: $clr-grey-900;\n$clr-ui-body-1: $clr-grey-900;\n$clr-ui-caption: $clr-grey-600;\n$clr-ui-menu: $clr-grey-900;\n$clr-ui-button: $clr-grey-900;\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules: syntax highlighting\n// ----------------------------------------------------------------------------\n\n// Codehilite extension\n.codehilite {\n\n .o, // Operator\n .ow { // Operator, word\n color: var(--md-code-hl-operator-color);\n }\n\n .p { // Punctuation\n color: var(--md-code-hl-punctuation-color);\n }\n\n .cpf, // Comment, preprocessor file\n .l, // Literal\n .s, // Literal, string\n .sb, // Literal, string backticks\n .sc, // Literal, string char\n .s2, // Literal, string double\n .si, // Literal, string interpol\n .s1, // Literal, string single\n .ss { // Literal, string symbol\n color: var(--md-code-hl-string-color);\n }\n\n .cp, // Comment, pre-processor\n .se, // Literal, string escape\n .sh, // Literal, string heredoc\n .sr, // Literal, string regex\n .sx { // Literal, string other\n color: var(--md-code-hl-special-color);\n }\n\n .m, // Number\n .mf, // Number, float\n .mh, // Number, hex\n .mi, // Number, integer\n .il, // Number, integer long\n .mo { // Number, octal\n color: var(--md-code-hl-number-color);\n }\n\n .k, // Keyword,\n .kd, // Keyword, declaration\n .kn, // Keyword, namespace\n .kp, // Keyword, pseudo\n .kr, // Keyword, reserved\n .kt { // Keyword, type\n color: var(--md-code-hl-keyword-color);\n }\n\n .kc, // Keyword, constant\n .n { // Name\n color: var(--md-code-hl-name-color);\n }\n\n .no, // Name, constant\n .nb, // Name, builtin\n .bp { // Name, builtin pseudo\n color: var(--md-code-hl-constant-color);\n }\n\n .nc, // Name, class\n .ne, // Name, exception\n .nf, // Name, function\n .nn { // Name, namespace\n color: var(--md-code-hl-function-color);\n }\n\n .nd, // Name, decorator\n .ni, // Name, entity\n .nl, // Name, label\n .nt { // Name, tag\n color: var(--md-code-hl-keyword-color);\n }\n\n .c, // Comment\n .cm, // Comment, multiline\n .c1, // Comment, single\n .ch, // Comment, shebang\n .cs, // Comment, special\n .sd { // Literal, string doc\n color: var(--md-code-hl-comment-color);\n }\n\n .na, // Name, attribute\n .nv, // Variable,\n .vc, // Variable, class\n .vg, // Variable, global\n .vi { // Variable, instance\n color: var(--md-code-hl-variable-color);\n }\n\n .ge, // Generic, emph\n .gr, // Generic, error\n .gh, // Generic, heading\n .go, // Generic, output\n .gp, // Generic, prompt\n .gs, // Generic, strong\n .gu, // Generic, subheading\n .gt { // Generic, traceback\n color: var(--md-code-hl-generic-color);\n }\n\n .gd, // Diff, delete\n .gi { // Diff, insert\n margin: 0 px2em(-2px);\n padding: 0 px2em(2px);\n border-radius: px2rem(2px);\n }\n\n .gd { // Diff, delete\n background-color: var(--md-typeset-del-color);\n }\n\n .gi { // Diff, insert\n background-color: var(--md-typeset-ins-color)\n }\n\n // Highlighted lines\n .hll {\n display: block;\n margin: 0 px2em(-16px, 13.6px);\n padding: 0 px2em(16px, 13.6px);\n background-color: var(--md-code-hl-color)\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: layout\n// ----------------------------------------------------------------------------\n\n// Block with line numbers\n.codehilitetable {\n display: block;\n overflow: hidden;\n\n // Set table elements to block layout, because otherwise the whole flexbox\n // hacking won't work correctly\n tbody,\n td {\n display: block;\n padding: 0;\n }\n\n // We need to use flexbox layout, because otherwise it's not possible to\n // make the code container scroll while keeping the line numbers static\n tr {\n display: flex;\n }\n\n // The pre tags are nested inside a table, so we need to remove the\n // margin because it collapses below all the overflows\n pre {\n margin: 0;\n }\n\n // Disable user selection, so code can be easily copied without\n // accidentally also copying the line numbers\n .linenos {\n padding: px2rem(10.5px) px2em(16px, 13.6px);\n padding-right: 0;\n font-size: px2em(13.6px);\n background-color: var(--md-code-bg-color);\n user-select: none;\n }\n\n // Add spacing to line number container\n .linenodiv {\n padding-right: px2em(8px, 13.6px);\n box-shadow: px2rem(-1px) 0 var(--md-default-fg-color--lighter) inset;\n\n // Reset spacings\n pre {\n color: var(--md-default-fg-color--light);\n text-align: right;\n }\n }\n\n // The table cell containing the code container wrapper and code should\n // stretch horizontally to the remaining space\n .code {\n flex: 1;\n overflow: hidden;\n }\n}\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Block with line numbers\n .codehilitetable {\n margin: 1em 0;\n direction: ltr;\n border-radius: px2rem(2px);\n\n // Remove rounded borders\n code {\n border-radius: 0;\n }\n }\n\n // [mobile -]: Stretch to whole width\n @include break-to-device(mobile) {\n\n // Full-width container\n > .codehilite {\n margin: 1em px2rem(-16px);\n\n // Stretch highlighted lines\n .hll {\n margin: 0 px2rem(-16px);\n padding: 0 px2rem(16px);\n }\n\n // Remove rounded borders\n code {\n border-radius: 0;\n }\n }\n\n // Full-width container on top-level\n > .codehilitetable {\n margin: 1em px2rem(-16px);\n border-radius: 0;\n\n // Stretch highlighted lines\n .hll {\n margin: 0 px2rem(-16px);\n padding: 0 px2rem(16px);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-footnotes-icon: svg-load(\"@mdi/svg/svg/keyboard-return.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // All footnote references\n [id^=\"fnref:\"] {\n display: inline-block;\n\n // Targeted anchor\n &:target {\n margin-top: -1 * px2rem(48px + 12px + 16px);\n padding-top: px2rem(48px + 12px + 16px);\n pointer-events: none;\n scroll-margin-top: initial;\n }\n }\n\n // All footnote back references\n [id^=\"fn:\"] {\n\n // Add spacing to anchor for offset\n &::before {\n display: none;\n height: 0;\n content: \"\";\n }\n\n // Reset, as we use the anchor-correction hack here.\n &:target {\n scroll-margin-top: initial;\n }\n\n // Targeted anchor\n &:target::before {\n display: block;\n margin-top: -1 * px2rem(48px + 12px + 10px);\n padding-top: px2rem(48px + 12px + 10px);\n pointer-events: none;\n }\n }\n\n // Footnotes extension\n .footnote {\n color: var(--md-default-fg-color--light);\n font-size: px2rem(12.8px);\n\n // Remove additional spacing on footnotes\n ol {\n margin-left: 0;\n }\n\n // Footnote\n li {\n transition: color 125ms;\n\n // Darken color for targeted footnote\n &:target {\n color: var(--md-default-fg-color);\n }\n\n // Remove spacing on first element\n :first-child {\n margin-top: 0;\n }\n\n // Make back references visible on container hover\n &:hover .footnote-backref,\n &:target .footnote-backref {\n transform: translateX(0);\n opacity: 1;\n }\n\n // Hovered back reference\n &:hover .footnote-backref:hover {\n color: var(--md-accent-fg-color);\n }\n }\n }\n\n // Footnote reference\n .footnote-ref {\n display: inline-block;\n pointer-events: initial;\n }\n\n // Footnote back reference\n .footnote-backref {\n display: inline-block;\n color: var(--md-typeset-a-color);\n // Hack: remove Unicode arrow for icon\n font-size: 0;\n vertical-align: text-bottom;\n transform: translateX(px2rem(5px));\n opacity: 0;\n transition:\n color 250ms,\n transform 250ms 250ms,\n opacity 125ms 250ms;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n transform: translateX(px2rem(-5px));\n }\n\n // Back reference icon\n &::before {\n display: inline-block;\n width: px2rem(16px);\n height: px2rem(16px);\n background-color: currentColor;\n mask-image: var(--md-footnotes-icon);\n mask-repeat: no-repeat;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n\n // Flip icon vertically\n svg {\n transform: scaleX(-1)\n }\n }\n }\n\n // Always show for print\n @media print {\n color: var(--md-typeset-a-color);\n transform: translateX(0);\n opacity: 1;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Permalinks extension\n .headerlink {\n display: inline-block;\n margin-left: px2rem(10px);\n // Hack: if we don't set visibility hidden, the text content of the node\n // will include the headerlink character, which is why Google indexes them.\n visibility: hidden;\n opacity: 0;\n transition:\n color 250ms,\n visibility 0ms 500ms,\n opacity 125ms;\n\n // Adjust for RTL languages\n [dir=\"rtl\"] & {\n margin-right: px2rem(10px);\n margin-left: initial;\n }\n\n // Higher specificity for color due to palettes integration\n html body & {\n color: var(--md-default-fg-color--lighter);\n }\n\n // Hide for print\n @media print {\n display: none;\n }\n }\n\n // Make permalink visible on hover\n :hover > .headerlink,\n :target > .headerlink,\n .headerlink:focus {\n visibility: visible;\n opacity: 1;\n transition:\n color 250ms,\n visibility 0ms,\n opacity 125ms;\n }\n\n // Active or targeted permalink\n :target > .headerlink,\n .headerlink:focus,\n .headerlink:hover {\n color: var(--md-accent-fg-color);\n }\n\n // General scroll margin offset for anything that can be targeted. Browser\n // support is pretty decent by now, and if we wait until Edge 79+ has more\n // adoption, we can get rid of all anchor-correction hacks.\n :target {\n scroll-margin-top: px2rem(48px + 24px);\n }\n\n // Correct anchor offset for link blurring\n @each $level, $delta in (\n h1 h2 h3: 8px,\n h4: 9px,\n h5 h6: 12px,\n ) {\n %#{nth($level, 1)} {\n\n // Reset, as we use the anchor-correction hack here.\n &:target {\n scroll-margin-top: initial;\n }\n\n // Un-targeted anchor\n &::before {\n display: block;\n margin-top: -1 * px2rem($delta);\n padding-top: px2rem($delta);\n content: \"\";\n }\n\n // Targeted anchor (48px from header, 12px from sidebar offset)\n &:target::before {\n margin-top: -1 * px2rem(48px + 12px + $delta);\n padding-top: px2rem(48px + 12px + $delta);\n }\n }\n\n // Define levels\n @for $n from 1 through length($level) {\n #{nth($level, $n)}[id] {\n @extend %#{nth($level, 1)};\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Scroll math block on overflow\n div.arithmatex {\n overflow-x: scroll;\n\n // [mobile -]: Stretch to whole width\n @include break-to-device(mobile) {\n margin: 0 px2rem(-16px);\n }\n\n // MathJax integration\n > * {\n width: min-content;\n margin: 1em auto !important;\n padding: 0 px2rem(16px);\n overflow: auto;\n touch-action: auto;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Deletions, additions and comments\n del.critic,\n ins.critic,\n .critic.comment {\n box-decoration-break: clone;\n }\n\n // Deletion\n del.critic {\n background-color: var(--md-typeset-del-color);\n }\n\n // Addition\n ins.critic {\n background-color: var(--md-typeset-ins-color);\n }\n\n // Comment\n .critic.comment {\n color: var(--md-code-hl-comment-color);\n\n // Comment opening mark\n &::before {\n content: \"/* \";\n }\n\n // Comment closing mark\n &::after {\n content: \" */\";\n }\n }\n\n // Block\n .critic.block {\n display: block;\n margin: 1em 0;\n padding-right: px2rem(16px);\n padding-left: px2rem(16px);\n overflow: auto;\n box-shadow: none;\n\n // Decrease spacing on first element\n :first-child {\n margin-top: 0.5em;\n }\n\n // Decrease spacing on last element\n :last-child {\n margin-bottom: 0.5em;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-details-icon: svg-load(\"@mdi/svg/svg/chevron-right.svg\");\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Details extension\n details {\n @extend .admonition;\n\n display: block;\n padding-top: 0;\n overflow: visible;\n\n // Rotate title icon\n &[open] > summary::after {\n transform: rotate(90deg);\n }\n\n // Remove bottom spacing for closed details\n &:not([open]) {\n padding-bottom: 0;\n\n // We cannot set overflow: hidden, as the outline would not be visible,\n // so we need to correct the border radius\n > summary {\n border-radius: px2rem(2px);\n }\n }\n\n // Hack: omit margin collapse\n &::after {\n display: table;\n content: \"\";\n }\n }\n\n // Details title\n summary {\n @extend .admonition-title;\n\n display: block;\n min-height: px2rem(20px);\n padding: px2rem(8px) px2rem(36px) px2rem(8px) px2rem(44px);\n border-top-left-radius: px2rem(2px);\n border-top-right-radius: px2rem(2px);\n cursor: pointer;\n\n // Disable focus indicator for pointer devices\n &:not(.focus-visible) {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n padding: px2rem(8px) px2rem(44px) px2rem(8px) px2rem(36px);\n }\n\n // Remove default details marker\n &::-webkit-details-marker {\n display: none;\n }\n\n // Details marker\n &::after {\n position: absolute;\n top: px2rem(8px);\n right: px2rem(8px);\n width: px2rem(20px);\n height: px2rem(20px);\n background-color: currentColor;\n mask-image: var(--md-details-icon);\n mask-repeat: no-repeat;\n transform: rotate(0deg);\n transition: transform 250ms;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: initial;\n left: px2rem(8px);\n transform: rotate(180deg);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Emojis\n img.emojione,\n img.twemoji,\n img.gemoji {\n width: px2em(18px);\n max-height: 100%;\n vertical-align: -15%;\n }\n\n // Inlined SVG icons via mkdocs-material-extensions\n span.twemoji {\n display: inline-block;\n height: px2em(18px);\n vertical-align: text-top;\n\n // Icon\n svg {\n width: px2em(18px);\n max-height: 100%;\n fill: currentColor;\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// When pymdownx.superfences is enabled but codehilite is disabled,\n// pymdownx.highlight will be used. When this happens, the outer container\n// and tables get this class names by default\n.highlight {\n @extend .codehilite;\n\n // Inline line numbers\n [data-linenos]::before {\n position: sticky;\n left: px2em(-16px, 13.6px);\n float: left;\n margin-right: px2em(16px, 13.6px);\n margin-left: px2em(-16px, 13.6px);\n padding-left: px2em(16px, 13.6px);\n color: var(--md-default-fg-color--light);\n background-color: var(--md-code-bg-color);\n box-shadow: px2rem(-1px) 0 var(--md-default-fg-color--lighter) inset;\n content: attr(data-linenos);\n user-select: none;\n }\n}\n\n// Same as above, but for code blocks with line numbers enabled\n.highlighttable {\n @extend .codehilitetable;\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset .keys {\n\n // Keyboard key icon\n kbd::before,\n kbd::after {\n position: relative;\n margin: 0;\n color: inherit;\n -moz-osx-font-smoothing: initial;\n -webkit-font-smoothing: initial;\n }\n\n // Surrounding text\n span {\n padding: 0 px2em(3.2px);\n color: var(--md-default-fg-color--light);\n }\n\n // Build special keys with left icon\n @each $name, $code in (\n\n // Modifiers\n \"alt\": \"\\2387\",\n \"left-alt\": \"\\2387\",\n \"right-alt\": \"\\2387\",\n \"command\": \"\\2318\",\n \"left-command\": \"\\2318\",\n \"right-command\": \"\\2318\",\n \"control\": \"\\2303\",\n \"left-control\": \"\\2303\",\n \"right-control\": \"\\2303\",\n \"meta\": \"\\25C6\",\n \"left-meta\": \"\\25C6\",\n \"right-meta\": \"\\25C6\",\n \"option\": \"\\2325\",\n \"left-option\": \"\\2325\",\n \"right-option\": \"\\2325\",\n \"shift\": \"\\21E7\",\n \"left-shift\": \"\\21E7\",\n \"right-shift\": \"\\21E7\",\n \"super\": \"\\2756\",\n \"left-super\": \"\\2756\",\n \"right-super\": \"\\2756\",\n \"windows\": \"\\229E\",\n \"left-windows\": \"\\229E\",\n \"right-windows\": \"\\229E\",\n\n // Other keys\n \"arrow-down\": \"\\2193\",\n \"arrow-left\": \"\\2190\",\n \"arrow-right\": \"\\2192\",\n \"arrow-up\": \"\\2191\",\n \"backspace\": \"\\232B\",\n \"backtab\": \"\\21E4\",\n \"caps-lock\": \"\\21EA\",\n \"clear\": \"\\2327\",\n \"context-menu\": \"\\2630\",\n \"delete\": \"\\2326\",\n \"eject\": \"\\23CF\",\n \"end\": \"\\2913\",\n \"escape\": \"\\238B\",\n \"home\": \"\\2912\",\n \"insert\": \"\\2380\",\n \"page-down\": \"\\21DF\",\n \"page-up\": \"\\21DE\",\n \"print-screen\": \"\\2399\"\n ) {\n .key-#{$name} {\n &::before {\n padding-right: px2em(6.4px);\n content: $code;\n }\n }\n }\n\n // Build special keys with right icon\n @each $name, $code in (\n \"tab\": \"\\21E5\",\n \"num-enter\": \"\\2324\",\n \"enter\": \"\\23CE\"\n ) {\n .key-#{$name} {\n &::after {\n padding-left: px2em(6.4px);\n content: $code;\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Tabbed block content\n .tabbed-content {\n display: none;\n order: 99;\n width: 100%;\n box-shadow: 0 px2rem(-1px) var(--md-default-fg-color--lightest);\n\n // Mirror old superfences behavior, if there's only a single code block.\n > pre:only-child,\n > .codehilite:only-child pre,\n > .codehilitetable:only-child,\n > .highlight:only-child pre,\n > .highlighttable:only-child {\n margin: 0;\n\n // Remove rounded borders at the top\n > code {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n }\n }\n\n // Nested tabs\n > .tabbed-set {\n margin: 0;\n }\n }\n\n // Tabbed block container\n .tabbed-set {\n position: relative;\n display: flex;\n flex-wrap: wrap;\n margin: 1em 0;\n border-radius: px2rem(2px);\n\n // Hide radio buttons\n > input {\n position: absolute;\n width: 0;\n height: 0;\n opacity: 0;\n\n // Active tab label\n &:checked + label {\n color: var(--md-accent-fg-color);\n border-color: var(--md-accent-fg-color);\n\n // Show tabbed block content\n & + .tabbed-content {\n display: block;\n }\n }\n\n // Focused tab label\n &:focus + label {\n outline-style: auto;\n }\n\n // Disable focus indicator for pointer devices\n &:not(.focus-visible) + label {\n outline: none;\n -webkit-tap-highlight-color: transparent;\n }\n }\n\n // Tab label\n > label {\n z-index: 1;\n width: auto;\n padding: px2em(12px, 12.8px) 1.25em px2em(10px, 12.8px);\n color: var(--md-default-fg-color--light);\n font-weight: 700;\n font-size: px2rem(12.8px);\n border-bottom: px2rem(2px) solid transparent;\n cursor: pointer;\n transition: color 250ms;\n\n // Hovered label\n html &:hover {\n color: var(--md-accent-fg-color);\n }\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Icon definitions\n:root {\n --md-tasklist-icon: svg-load(\n \"@primer/octicons/build/svg/check-circle-fill-24.svg\"\n );\n --md-tasklist-icon--checked: svg-load(\n \"@primer/octicons/build/svg/check-circle-fill-24.svg\"\n );\n}\n\n// ----------------------------------------------------------------------------\n\n// Scoped in typesetted content to match specificity of regular content\n.md-typeset {\n\n // Remove list icon on task items\n .task-list-item {\n position: relative;\n list-style-type: none;\n\n // Make checkbox items align with normal list items, but position\n // everything in ems for correct layout at smaller font sizes\n [type=\"checkbox\"] {\n position: absolute;\n top: 0.45em;\n left: -2em;\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: -2em;\n left: initial;\n }\n }\n }\n\n // Wrapper for list controls, in case custom checkboxes are enabled\n .task-list-control {\n\n // Checkbox icon in unchecked state\n .task-list-indicator::before {\n position: absolute;\n top: 0.15em;\n left: px2em(-24px);\n width: px2em(20px);\n height: px2em(20px);\n background-color: var(--md-default-fg-color--lightest);\n mask-image: var(--md-tasklist-icon);\n mask-repeat: no-repeat;\n content: \"\";\n\n // Adjust for right-to-left languages\n [dir=\"rtl\"] & {\n right: px2em(-24px);\n left: initial;\n }\n }\n\n // Checkbox icon in checked state\n [type=\"checkbox\"]:checked + .task-list-indicator::before {\n background-color: $clr-green-a400;\n mask-image: var(--md-tasklist-icon--checked);\n }\n\n // Hide original checkbox behind icon\n [type=\"checkbox\"] {\n z-index: -1;\n opacity: 0;\n }\n }\n}\n"],"sourceRoot":""} \ No newline at end of file diff --git a/docs/v3/v2/assets/stylesheets/palette.3f72e892.min.css b/docs/v3/v2/assets/stylesheets/palette.3f72e892.min.css deleted file mode 100644 index 0d3745832..000000000 --- a/docs/v3/v2/assets/stylesheets/palette.3f72e892.min.css +++ /dev/null @@ -1,3 +0,0 @@ -[data-md-color-accent=red]{--md-accent-fg-color: hsla(348, 100%, 55%, 1);--md-accent-fg-color--transparent: hsla(348, 100%, 55%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-accent=pink]{--md-accent-fg-color: hsla(339, 100%, 48%, 1);--md-accent-fg-color--transparent: hsla(339, 100%, 48%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-accent=purple]{--md-accent-fg-color: hsla(291, 96%, 62%, 1);--md-accent-fg-color--transparent: hsla(291, 96%, 62%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-accent=deep-purple]{--md-accent-fg-color: hsla(256, 100%, 65%, 1);--md-accent-fg-color--transparent: hsla(256, 100%, 65%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-accent=indigo]{--md-accent-fg-color: hsla(231, 99%, 66%, 1);--md-accent-fg-color--transparent: hsla(231, 99%, 66%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-accent=blue]{--md-accent-fg-color: hsla(218, 100%, 63%, 1);--md-accent-fg-color--transparent: hsla(218, 100%, 63%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-accent=light-blue]{--md-accent-fg-color: hsla(203, 100%, 46%, 1);--md-accent-fg-color--transparent: hsla(203, 100%, 46%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-accent=cyan]{--md-accent-fg-color: hsla(188, 100%, 42%, 1);--md-accent-fg-color--transparent: hsla(188, 100%, 42%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-accent=teal]{--md-accent-fg-color: hsla(172, 100%, 37%, 1);--md-accent-fg-color--transparent: hsla(172, 100%, 37%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-accent=green]{--md-accent-fg-color: hsla(145, 100%, 39%, 1);--md-accent-fg-color--transparent: hsla(145, 100%, 39%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-accent=light-green]{--md-accent-fg-color: hsla(97, 81%, 48%, 1);--md-accent-fg-color--transparent: hsla(97, 81%, 48%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-accent=lime]{--md-accent-fg-color: hsla(75, 100%, 46%, 1);--md-accent-fg-color--transparent: hsla(75, 100%, 46%, 0.1);--md-accent-bg-color: hsla(0, 0%, 0%, 0.87);--md-accent-bg-color--light: hsla(0, 0%, 0%, 0.54)}[data-md-color-accent=yellow]{--md-accent-fg-color: hsla(50, 100%, 50%, 1);--md-accent-fg-color--transparent: hsla(50, 100%, 50%, 0.1);--md-accent-bg-color: hsla(0, 0%, 0%, 0.87);--md-accent-bg-color--light: hsla(0, 0%, 0%, 0.54)}[data-md-color-accent=amber]{--md-accent-fg-color: hsla(40, 100%, 50%, 1);--md-accent-fg-color--transparent: hsla(40, 100%, 50%, 0.1);--md-accent-bg-color: hsla(0, 0%, 0%, 0.87);--md-accent-bg-color--light: hsla(0, 0%, 0%, 0.54)}[data-md-color-accent=orange]{--md-accent-fg-color: hsla(34, 100%, 50%, 1);--md-accent-fg-color--transparent: hsla(34, 100%, 50%, 0.1);--md-accent-bg-color: hsla(0, 0%, 0%, 0.87);--md-accent-bg-color--light: hsla(0, 0%, 0%, 0.54)}[data-md-color-accent=deep-orange]{--md-accent-fg-color: hsla(14, 100%, 63%, 1);--md-accent-fg-color--transparent: hsla(14, 100%, 63%, 0.1);--md-accent-bg-color: hsla(0, 0%, 100%, 1);--md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=red]{--md-primary-fg-color: hsla(1, 83%, 63%, 1);--md-primary-fg-color--light: hsla(0, 69%, 67%, 1);--md-primary-fg-color--dark: hsla(1, 77%, 55%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=pink]{--md-primary-fg-color: hsla(340, 82%, 52%, 1);--md-primary-fg-color--light: hsla(340, 82%, 59%, 1);--md-primary-fg-color--dark: hsla(336, 78%, 43%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=purple]{--md-primary-fg-color: hsla(291, 47%, 51%, 1);--md-primary-fg-color--light: hsla(291, 47%, 60%, 1);--md-primary-fg-color--dark: hsla(287, 65%, 40%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=deep-purple]{--md-primary-fg-color: hsla(262, 47%, 55%, 1);--md-primary-fg-color--light: hsla(262, 47%, 63%, 1);--md-primary-fg-color--dark: hsla(262, 52%, 47%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=indigo]{--md-primary-fg-color: hsla(231, 48%, 48%, 1);--md-primary-fg-color--light: hsla(231, 44%, 56%, 1);--md-primary-fg-color--dark: hsla(232, 54%, 41%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=blue]{--md-primary-fg-color: hsla(207, 90%, 54%, 1);--md-primary-fg-color--light: hsla(207, 90%, 61%, 1);--md-primary-fg-color--dark: hsla(210, 79%, 46%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=light-blue]{--md-primary-fg-color: hsla(199, 98%, 48%, 1);--md-primary-fg-color--light: hsla(199, 92%, 56%, 1);--md-primary-fg-color--dark: hsla(201, 98%, 41%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=cyan]{--md-primary-fg-color: hsla(187, 100%, 42%, 1);--md-primary-fg-color--light: hsla(187, 71%, 50%, 1);--md-primary-fg-color--dark: hsla(186, 100%, 33%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=teal]{--md-primary-fg-color: hsla(174, 100%, 29%, 1);--md-primary-fg-color--light: hsla(174, 63%, 40%, 1);--md-primary-fg-color--dark: hsla(173, 100%, 24%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=green]{--md-primary-fg-color: hsla(122, 39%, 49%, 1);--md-primary-fg-color--light: hsla(123, 38%, 57%, 1);--md-primary-fg-color--dark: hsla(123, 43%, 39%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=light-green]{--md-primary-fg-color: hsla(88, 50%, 53%, 1);--md-primary-fg-color--light: hsla(88, 50%, 60%, 1);--md-primary-fg-color--dark: hsla(92, 48%, 42%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=lime]{--md-primary-fg-color: hsla(66, 70%, 54%, 1);--md-primary-fg-color--light: hsla(66, 70%, 61%, 1);--md-primary-fg-color--dark: hsla(62, 61%, 44%, 1);--md-primary-bg-color: hsla(0, 0%, 0%, 0.87);--md-primary-bg-color--light: hsla(0, 0%, 0%, 0.54)}[data-md-color-primary=yellow]{--md-primary-fg-color: hsla(54, 100%, 62%, 1);--md-primary-fg-color--light: hsla(54, 100%, 67%, 1);--md-primary-fg-color--dark: hsla(43, 96%, 58%, 1);--md-primary-bg-color: hsla(0, 0%, 0%, 0.87);--md-primary-bg-color--light: hsla(0, 0%, 0%, 0.54)}[data-md-color-primary=amber]{--md-primary-fg-color: hsla(45, 100%, 51%, 1);--md-primary-fg-color--light: hsla(45, 100%, 58%, 1);--md-primary-fg-color--dark: hsla(38, 100%, 50%, 1);--md-primary-bg-color: hsla(0, 0%, 0%, 0.87);--md-primary-bg-color--light: hsla(0, 0%, 0%, 0.54)}[data-md-color-primary=orange]{--md-primary-fg-color: hsla(36, 100%, 57%, 1);--md-primary-fg-color--light: hsla(36, 100%, 57%, 1);--md-primary-fg-color--dark: hsla(33, 100%, 49%, 1);--md-primary-bg-color: hsla(0, 0%, 0%, 0.87);--md-primary-bg-color--light: hsla(0, 0%, 0%, 0.54)}[data-md-color-primary=deep-orange]{--md-primary-fg-color: hsla(14, 100%, 63%, 1);--md-primary-fg-color--light: hsla(14, 100%, 70%, 1);--md-primary-fg-color--dark: hsla(14, 91%, 54%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=brown]{--md-primary-fg-color: hsla(16, 25%, 38%, 1);--md-primary-fg-color--light: hsla(16, 18%, 47%, 1);--md-primary-fg-color--dark: hsla(14, 26%, 29%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=grey]{--md-primary-fg-color: hsla(0, 0%, 46%, 1);--md-primary-fg-color--light: hsla(0, 0%, 62%, 1);--md-primary-fg-color--dark: hsla(0, 0%, 38%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=blue-grey]{--md-primary-fg-color: hsla(199, 18%, 40%, 1);--md-primary-fg-color--light: hsla(200, 18%, 46%, 1);--md-primary-fg-color--dark: hsla(199, 18%, 33%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7)}[data-md-color-primary=white]{--md-primary-fg-color: hsla(0, 0%, 100%, 1);--md-primary-fg-color--light: hsla(0, 0%, 100%, 0.7);--md-primary-fg-color--dark: hsla(0, 0%, 0%, 0.07);--md-primary-bg-color: hsla(0, 0%, 0%, 0.87);--md-primary-bg-color--light: hsla(0, 0%, 0%, 0.54);--md-typeset-a-color: hsla(231, 48%, 48%, 1)}@media screen and (min-width: 60em){[data-md-color-primary=white] .md-search__input{background-color:rgba(0,0,0,.07)}[data-md-color-primary=white] .md-search__input+.md-search__icon{color:rgba(0,0,0,.87)}[data-md-color-primary=white] .md-search__input::-webkit-input-placeholder{color:rgba(0,0,0,.54)}[data-md-color-primary=white] .md-search__input::-moz-placeholder{color:rgba(0,0,0,.54)}[data-md-color-primary=white] .md-search__input::-ms-input-placeholder{color:rgba(0,0,0,.54)}[data-md-color-primary=white] .md-search__input::placeholder{color:rgba(0,0,0,.54)}[data-md-color-primary=white] .md-search__input:hover{background-color:rgba(0,0,0,.32)}}@media screen and (min-width: 76.25em){[data-md-color-primary=white] .md-tabs{border-bottom:.05rem solid rgba(0,0,0,.07)}}[data-md-color-primary=black]{--md-primary-fg-color: hsla(0, 0%, 0%, 1);--md-primary-fg-color--light: hsla(0, 0%, 0%, 0.54);--md-primary-fg-color--dark: hsla(0, 0%, 0%, 1);--md-primary-bg-color: hsla(0, 0%, 100%, 1);--md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);--md-typeset-a-color: hsla(231, 48%, 48%, 1)}[data-md-color-primary=black] .md-header{background-color:#000}@media screen and (max-width: 59.9375em){[data-md-color-primary=black] .md-nav__source{background-color:rgba(0,0,0,.87)}}@media screen and (min-width: 60em){[data-md-color-primary=black] .md-search__input{background-color:rgba(255,255,255,.12)}[data-md-color-primary=black] .md-search__input:hover{background-color:rgba(255,255,255,.3)}}@media screen and (max-width: 76.1875em){html [data-md-color-primary=black] .md-nav--primary .md-nav__title[for=__drawer]{background-color:#000}}@media screen and (min-width: 76.25em){[data-md-color-primary=black] .md-tabs{background-color:#000}}[data-md-color-scheme=slate]{--md-hue: 232;--md-default-fg-color: hsla(var(--md-hue), 75%, 95%, 1);--md-default-fg-color--light: hsla(var(--md-hue), 75%, 90%, 0.62);--md-default-fg-color--lighter: hsla(var(--md-hue), 75%, 90%, 0.32);--md-default-fg-color--lightest: hsla(var(--md-hue), 75%, 90%, 0.12);--md-default-bg-color: hsla(var(--md-hue), 15%, 21%, 1);--md-default-bg-color--light: hsla(var(--md-hue), 15%, 21%, 0.54);--md-default-bg-color--lighter: hsla(var(--md-hue), 15%, 21%, 0.26);--md-default-bg-color--lightest: hsla(var(--md-hue), 15%, 21%, 0.07);--md-code-fg-color: hsla(var(--md-hue), 18%, 86%, 1);--md-code-bg-color: hsla(var(--md-hue), 15%, 15%, 1);--md-code-hl-color: hsla(218, 100%, 63%, 0.15);--md-code-hl-number-color: hsla(6, 74%, 63%, 1);--md-code-hl-special-color: hsla(340, 83%, 66%, 1);--md-code-hl-function-color: hsla(291, 57%, 65%, 1);--md-code-hl-constant-color: hsla(250, 62%, 70%, 1);--md-code-hl-keyword-color: hsla(219, 66%, 64%, 1);--md-code-hl-string-color: hsla(150, 58%, 44%, 1);--md-typeset-a-color: var(--md-primary-fg-color--light);--md-typeset-mark-color: hsla(218, 100%, 63%, 0.3);--md-typeset-kbd-color: hsla(var(--md-hue), 15%, 94%, 0.12);--md-typeset-kbd-accent-color: hsla(var(--md-hue), 15%, 94%, 0.2);--md-typeset-kbd-border-color: hsla(var(--md-hue), 15%, 14%, 1);--md-admonition-bg-color: hsla(var(--md-hue), 0%, 100%, 0.025);--md-footer-bg-color: hsla(var(--md-hue), 15%, 12%, 0.87);--md-footer-bg-color--dark: hsla(var(--md-hue), 15%, 10%, 1)} - -/*# sourceMappingURL=palette.3f72e892.min.css.map*/ \ No newline at end of file diff --git a/docs/v3/v2/assets/stylesheets/palette.3f72e892.min.css.map b/docs/v3/v2/assets/stylesheets/palette.3f72e892.min.css.map deleted file mode 100644 index 8d3158622..000000000 --- a/docs/v3/v2/assets/stylesheets/palette.3f72e892.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["webpack:///src/assets/stylesheets/palette/_accent.scss","webpack:///src/assets/stylesheets/palette/_primary.scss","webpack:///src/assets/stylesheets/utilities/_break.scss","webpack:///src/assets/stylesheets/palette/_scheme.scss"],"names":[],"mappings":"AA8CE,2BACE,8CACA,6DAOE,2CACA,oDAVJ,4BACE,8CACA,6DAOE,2CACA,oDAVJ,8BACE,6CACA,4DAOE,2CACA,oDAVJ,mCACE,8CACA,6DAOE,2CACA,oDAVJ,8BACE,6CACA,4DAOE,2CACA,oDAVJ,4BACE,8CACA,6DAOE,2CACA,oDAVJ,kCACE,8CACA,6DAOE,2CACA,oDAVJ,4BACE,8CACA,6DAOE,2CACA,oDAVJ,4BACE,8CACA,6DAOE,2CACA,oDAVJ,6BACE,8CACA,6DAOE,2CACA,oDAVJ,mCACE,4CACA,2DAOE,2CACA,oDAVJ,4BACE,6CACA,4DAIE,4CACA,mDAPJ,8BACE,6CACA,4DAIE,4CACA,mDAPJ,6BACE,6CACA,4DAIE,4CACA,mDAPJ,8BACE,6CACA,4DAIE,4CACA,mDAPJ,mCACE,6CACA,4DAOE,2CACA,oDCPJ,4BACE,4CACA,mDACA,kDAOE,4CACA,qDAXJ,6BACE,8CACA,qDACA,oDAOE,4CACA,qDAXJ,+BACE,8CACA,qDACA,oDAOE,4CACA,qDAXJ,oCACE,8CACA,qDACA,oDAOE,4CACA,qDAXJ,+BACE,8CACA,qDACA,oDAOE,4CACA,qDAXJ,6BACE,8CACA,qDACA,oDAOE,4CACA,qDAXJ,mCACE,8CACA,qDACA,oDAOE,4CACA,qDAXJ,6BACE,+CACA,qDACA,qDAOE,4CACA,qDAXJ,6BACE,+CACA,qDACA,qDAOE,4CACA,qDAXJ,8BACE,8CACA,qDACA,oDAOE,4CACA,qDAXJ,oCACE,6CACA,oDACA,mDAOE,4CACA,qDAXJ,6BACE,6CACA,oDACA,mDAIE,6CACA,oDARJ,+BACE,8CACA,qDACA,mDAIE,6CACA,oDARJ,8BACE,8CACA,qDACA,oDAIE,6CACA,oDARJ,+BACE,8CACA,qDACA,oDAIE,6CACA,oDARJ,oCACE,8CACA,qDACA,mDAOE,4CACA,qDAXJ,8BACE,6CACA,oDACA,mDAOE,4CACA,qDAXJ,6BACE,2CACA,kDACA,iDAOE,4CACA,qDAXJ,kCACE,8CACA,qDACA,oDAOE,4CACA,qDAUN,8BACE,4CACA,qDACA,mDACA,6CACA,oDAGA,6CC6GE,oCDvGA,gDACE,iCAGA,iEACE,sBAIF,2EACE,sBADF,kEACE,sBADF,uEACE,sBADF,6DACE,sBAIF,sDACE,kCCwFJ,uCD/EA,uCACE,4CAUN,8BACE,0CACA,oDACA,gDACA,4CACA,qDAGA,6CAGA,yCACE,sBC0EA,yCDnEA,8CACE,kCCgDF,oCDxCA,gDACE,uCAGA,sDACE,uCCqDJ,yCD5CA,iFACE,uBCyBF,uCDjBA,uCACE,uBEhJN,6BAKE,cAGA,wDACA,kEACA,oEACA,qEACA,wDACA,kEACA,oEACA,qEAGA,qDACA,qDAGA,+CACA,gDACA,mDACA,oDACA,oDACA,mDACA,kDAGA,wDAGA,mDAGA,4DACA,kEACA,gEAGA,+DAGA,0DACA,6D","file":"assets/stylesheets/palette.3f72e892.min.css","sourcesContent":["////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n@each $name, $color in (\n \"red\": $clr-red-a400,\n \"pink\": $clr-pink-a400,\n \"purple\": $clr-purple-a200,\n \"deep-purple\": $clr-deep-purple-a200,\n \"indigo\": $clr-indigo-a200,\n \"blue\": $clr-blue-a200,\n \"light-blue\": $clr-light-blue-a700,\n \"cyan\": $clr-cyan-a700,\n \"teal\": $clr-teal-a700,\n \"green\": $clr-green-a700,\n \"light-green\": $clr-light-green-a700,\n \"lime\": $clr-lime-a700,\n \"yellow\": $clr-yellow-a700,\n \"amber\": $clr-amber-a700,\n \"orange\": $clr-orange-a400,\n \"deep-orange\": $clr-deep-orange-a200\n) {\n\n // Color palette\n [data-md-color-accent=\"#{$name}\"] {\n --md-accent-fg-color: hsla(#{hex2hsl($color)}, 1);\n --md-accent-fg-color--transparent: hsla(#{hex2hsl($color)}, 0.1);\n\n // Inverted text for lighter shades\n @if index(\"lime\" \"yellow\" \"amber\" \"orange\", $name) {\n --md-accent-bg-color: hsla(0, 0%, 0%, 0.87);\n --md-accent-bg-color--light: hsla(0, 0%, 0%, 0.54);\n } @else {\n --md-accent-bg-color: hsla(0, 0%, 100%, 1);\n --md-accent-bg-color--light: hsla(0, 0%, 100%, 0.7);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n@each $name, $colors in (\n \"red\": $clr-red-400 $clr-red-300 $clr-red-600,\n \"pink\": $clr-pink-500 $clr-pink-400 $clr-pink-700,\n \"purple\": $clr-purple-400 $clr-purple-300 $clr-purple-600,\n \"deep-purple\": $clr-deep-purple-400 $clr-deep-purple-300 $clr-deep-purple-500,\n \"indigo\": $clr-indigo-500 $clr-indigo-400 $clr-indigo-700,\n \"blue\": $clr-blue-500 $clr-blue-400 $clr-blue-700,\n \"light-blue\": $clr-light-blue-500 $clr-light-blue-400 $clr-light-blue-700,\n \"cyan\": $clr-cyan-500 $clr-cyan-400 $clr-cyan-700,\n \"teal\": $clr-teal-500 $clr-teal-400 $clr-teal-700,\n \"green\": $clr-green-500 $clr-green-400 $clr-green-700,\n \"light-green\": $clr-light-green-500 $clr-light-green-400 $clr-light-green-700,\n \"lime\": $clr-lime-500 $clr-lime-400 $clr-lime-700,\n \"yellow\": $clr-yellow-500 $clr-yellow-400 $clr-yellow-700,\n \"amber\": $clr-amber-500 $clr-amber-400 $clr-amber-700,\n \"orange\": $clr-orange-400 $clr-orange-400 $clr-orange-600,\n \"deep-orange\": $clr-deep-orange-400 $clr-deep-orange-300 $clr-deep-orange-600,\n \"brown\": $clr-brown-500 $clr-brown-400 $clr-brown-700,\n \"grey\": $clr-grey-600 $clr-grey-500 $clr-grey-700,\n \"blue-grey\": $clr-blue-grey-600 $clr-blue-grey-500 $clr-blue-grey-700\n) {\n\n // Color palette\n [data-md-color-primary=\"#{$name}\"] {\n --md-primary-fg-color: hsla(#{hex2hsl(nth($colors, 1))}, 1);\n --md-primary-fg-color--light: hsla(#{hex2hsl(nth($colors, 2))}, 1);\n --md-primary-fg-color--dark: hsla(#{hex2hsl(nth($colors, 3))}, 1);\n\n // Inverted text for lighter shades\n @if index(\"lime\" \"yellow\" \"amber\" \"orange\", $name) {\n --md-primary-bg-color: hsla(0, 0%, 0%, 0.87);\n --md-primary-bg-color--light: hsla(0, 0%, 0%, 0.54);\n } @else {\n --md-primary-bg-color: hsla(0, 0%, 100%, 1);\n --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: white\n// ----------------------------------------------------------------------------\n\n// Color palette\n[data-md-color-primary=\"white\"] {\n --md-primary-fg-color: hsla(0, 0%, 100%, 1);\n --md-primary-fg-color--light: hsla(0, 0%, 100%, 0.7);\n --md-primary-fg-color--dark: hsla(0, 0%, 0%, 0.07);\n --md-primary-bg-color: hsla(0, 0%, 0%, 0.87);\n --md-primary-bg-color--light: hsla(0, 0%, 0%, 0.54);\n\n // Typeset color shades\n --md-typeset-a-color: hsla(#{hex2hsl($clr-indigo-500)}, 1);\n\n // [tablet portrait +]: Change color of search input\n @include break-from-device(tablet landscape) {\n\n // Search input\n .md-search__input {\n background-color: hsla(0, 0%, 0%, 0.07);\n\n // Search icon color\n + .md-search__icon {\n color: hsla(0, 0%, 0%, 0.87);\n }\n\n // Placeholder color\n &::placeholder {\n color: hsla(0, 0%, 0%, 0.54);\n }\n\n // Hovered search field\n &:hover {\n background-color: hsla(0, 0%, 0%, 0.32);\n }\n }\n }\n\n // [screen +]: Set bottom border for tabs\n @include break-from-device(screen) {\n\n // Tabs with outline\n .md-tabs {\n border-bottom: px2rem(1px) solid hsla(0, 0%, 0%, 0.07);\n }\n }\n}\n\n// ----------------------------------------------------------------------------\n// Rules: black\n// ----------------------------------------------------------------------------\n\n// Color palette\n[data-md-color-primary=\"black\"] {\n --md-primary-fg-color: hsla(0, 0%, 0%, 1);\n --md-primary-fg-color--light: hsla(0, 0%, 0%, 0.54);\n --md-primary-fg-color--dark: hsla(0, 0%, 0%, 1);\n --md-primary-bg-color: hsla(0, 0%, 100%, 1);\n --md-primary-bg-color--light: hsla(0, 0%, 100%, 0.7);\n\n // Text color shades\n --md-typeset-a-color: hsla(#{hex2hsl($clr-indigo-500)}, 1);\n\n // Application header (stays always on top)\n .md-header {\n background-color: hsla(0, 0%, 0%, 1);\n }\n\n // [tablet portrait -]: Layered navigation\n @include break-to-device(tablet portrait) {\n\n // Repository containing source\n .md-nav__source {\n background-color: hsla(0, 0%, 0%, 0.87);\n }\n }\n\n // [tablet landscape +]: Header-embedded search\n @include break-from-device(tablet landscape) {\n\n // Search input\n .md-search__input {\n background-color: hsla(0, 0%, 100%, 0.12);\n\n // Hovered search field\n &:hover {\n background-color: hsla(0, 0%, 100%, 0.3);\n }\n }\n }\n\n // [tablet -]: Layered navigation\n @include break-to-device(tablet) {\n\n // Site title in main navigation\n html & .md-nav--primary .md-nav__title[for=\"__drawer\"] {\n background-color: hsla(0, 0%, 0%, 1);\n }\n }\n\n // [screen +]: Set background color for tabs\n @include break-from-device(screen) {\n\n // Tabs with outline\n .md-tabs {\n background-color: hsla(0, 0%, 0%, 1);\n }\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Variables\n// ----------------------------------------------------------------------------\n\n///\n/// Device-specific breakpoints\n///\n/// @example\n/// $break-devices: (\n/// mobile: (\n/// portrait: 220px 479px,\n/// landscape: 480px 719px\n/// ),\n/// tablet: (\n/// portrait: 720px 959px,\n/// landscape: 960px 1219px\n/// ),\n/// screen: (\n/// small: 1220px 1599px,\n/// medium: 1600px 1999px,\n/// large: 2000px\n/// )\n/// );\n///\n$break-devices: () !default;\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\n///\n/// Choose minimum and maximum device widths\n///\n@function break-select-min-max($devices) {\n $min: 1000000;\n $max: 0;\n @each $key, $value in $devices {\n @while type-of($value) == map {\n $value: break-select-min-max($value);\n }\n @if type-of($value) == list {\n @each $number in $value {\n @if type-of($number) == number {\n $min: min($number, $min);\n @if $max != null {\n $max: max($number, $max);\n }\n } @else {\n @error \"Invalid number: #{$number}\";\n }\n }\n } @else if type-of($value) == number {\n $min: min($value, $min);\n $max: null;\n } @else {\n @error \"Invalid value: #{$value}\";\n }\n }\n @return $min, $max;\n}\n\n///\n/// Select minimum and maximum widths for a device breakpoint\n///\n@function break-select-device($device) {\n $current: $break-devices;\n @for $n from 1 through length($device) {\n @if type-of($current) == map {\n $current: map-get($current, nth($device, $n));\n } @else {\n @error \"Invalid device map: #{$devices}\";\n }\n }\n @if type-of($current) == list or type-of($current) == number {\n $current: (default: $current);\n }\n @return break-select-min-max($current);\n}\n\n// ----------------------------------------------------------------------------\n// Mixins\n// ----------------------------------------------------------------------------\n\n///\n/// A minimum-maximum media query breakpoint\n///\n@mixin break-at($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (min-width: $breakpoint) {\n @content;\n }\n } @else if type-of($breakpoint) == list {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n @if type-of($min) == number and type-of($max) == number {\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// An orientation media query breakpoint\n///\n@mixin break-at-orientation($breakpoint) {\n @if type-of($breakpoint) == string {\n @media screen and (orientation: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A maximum-aspect-ratio media query breakpoint\n///\n@mixin break-at-ratio($breakpoint) {\n @if type-of($breakpoint) == number {\n @media screen and (max-aspect-ratio: $breakpoint) {\n @content;\n }\n } @else {\n @error \"Invalid breakpoint: #{$breakpoint}\";\n }\n}\n\n///\n/// A minimum-maximum media query device breakpoint\n///\n@mixin break-at-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n @if nth($breakpoint, 2) != null {\n $min: nth($breakpoint, 1);\n $max: nth($breakpoint, 2);\n @media screen and (min-width: $min) and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A minimum media query device breakpoint\n///\n@mixin break-from-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $min: nth($breakpoint, 1);\n @media screen and (min-width: $min) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n\n///\n/// A maximum media query device breakpoint\n///\n@mixin break-to-device($device) {\n @if type-of($device) == string {\n $device: $device,;\n }\n @if type-of($device) == list {\n $breakpoint: break-select-device($device);\n $max: nth($breakpoint, 2);\n @media screen and (max-width: $max) {\n @content;\n }\n } @else {\n @error \"Invalid device: #{$device}\";\n }\n}\n","////\n/// Copyright (c) 2016-2020 Martin Donath \n///\n/// Permission is hereby granted, free of charge, to any person obtaining a\n/// copy of this software and associated documentation files (the \"Software\"),\n/// to deal in the Software without restriction, including without limitation\n/// the rights to use, copy, modify, merge, publish, distribute, sublicense,\n/// and/or sell copies of the Software, and to permit persons to whom the\n/// Software is furnished to do so, subject to the following conditions:\n///\n/// The above copyright notice and this permission notice shall be included in\n/// all copies or substantial portions of the Software.\n///\n/// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n/// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL\n/// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n/// DEALINGS\n////\n\n// ----------------------------------------------------------------------------\n// Rules\n// ----------------------------------------------------------------------------\n\n// Slate theme, i.e. dark mode\n[data-md-color-scheme=\"slate\"] {\n\n // Slate's hue in the range [0,360] - change this variable to alter the tone\n // of the theme, e.g. to make it more redish or greenish. This is a slate-\n // specific variable, but the same approach may be adapted to custom themes.\n --md-hue: 232;\n\n // Default color shades\n --md-default-fg-color: hsla(var(--md-hue), 75%, 95%, 1);\n --md-default-fg-color--light: hsla(var(--md-hue), 75%, 90%, 0.62);\n --md-default-fg-color--lighter: hsla(var(--md-hue), 75%, 90%, 0.32);\n --md-default-fg-color--lightest: hsla(var(--md-hue), 75%, 90%, 0.12);\n --md-default-bg-color: hsla(var(--md-hue), 15%, 21%, 1);\n --md-default-bg-color--light: hsla(var(--md-hue), 15%, 21%, 0.54);\n --md-default-bg-color--lighter: hsla(var(--md-hue), 15%, 21%, 0.26);\n --md-default-bg-color--lightest: hsla(var(--md-hue), 15%, 21%, 0.07);\n\n // Code color shades\n --md-code-fg-color: hsla(var(--md-hue), 18%, 86%, 1);\n --md-code-bg-color: hsla(var(--md-hue), 15%, 15%, 1);\n\n // Code highlighting color shades\n --md-code-hl-color: hsla(#{hex2hsl($clr-blue-a200)}, 0.15);\n --md-code-hl-number-color: hsla(6, 74%, 63%, 1);\n --md-code-hl-special-color: hsla(340, 83%, 66%, 1);\n --md-code-hl-function-color: hsla(291, 57%, 65%, 1);\n --md-code-hl-constant-color: hsla(250, 62%, 70%, 1);\n --md-code-hl-keyword-color: hsla(219, 66%, 64%, 1);\n --md-code-hl-string-color: hsla(150, 58%, 44%, 1);\n\n // Typeset color shades\n --md-typeset-a-color: var(--md-primary-fg-color--light);\n\n // Typeset `mark` color shades\n --md-typeset-mark-color: hsla(#{hex2hsl($clr-blue-a200)}, 0.3);\n\n // Typeset `kbd` color shades\n --md-typeset-kbd-color: hsla(var(--md-hue), 15%, 94%, 0.12);\n --md-typeset-kbd-accent-color: hsla(var(--md-hue), 15%, 94%, 0.2);\n --md-typeset-kbd-border-color: hsla(var(--md-hue), 15%, 14%, 1);\n\n // Admonition color shades\n --md-admonition-bg-color: hsla(var(--md-hue), 0%, 100%, 0.025);\n\n // Footer color shades\n --md-footer-bg-color: hsla(var(--md-hue), 15%, 12%, 0.87);\n --md-footer-bg-color--dark: hsla(var(--md-hue), 15%, 10%, 1);\n}\n"],"sourceRoot":""} \ No newline at end of file diff --git a/docs/v3/v2/authentication/adaljsclient/index.html b/docs/v3/v2/authentication/adaljsclient/index.html deleted file mode 100644 index da0464104..000000000 --- a/docs/v3/v2/authentication/adaljsclient/index.html +++ /dev/null @@ -1,2510 +0,0 @@ - - - - - - - - - - - - - - - - - - ADAL Client - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/core/adalclient

    -

    This module contains the AdalClient class which can be used to authenticate to any AzureAD secured resource. It is designed to work seamlessly with SharePoint Framework's permissions.

    -
    -

    Where possible it is recommended to use the MSAL client.

    -
    -

    Getting Started

    -

    Install the library and required dependencies

    -

    npm install @pnp/adaljsclient --save

    -

    Setup and Use inside SharePoint Framework

    -

    Using the SharePoint Framework is the preferred way to make use of the AdalClient as we can use the AADTokenProvider to efficiently get tokens on your behalf. You can also read more about how this process works and the necessary SPFx configurations in the SharePoint Framework 1.6 release notes. This method will only work for SharePoint Framework >= 1.6. For earlier versions of SharePoint Framework you can still use the AdalClient as outlined below using the constructor to specify the values for an AAD Application you have setup.

    -

    Calling the graph api

    -

    By providing the context in the onInit we can create the adal client from known information.

    -
    import { graph } from "@pnp/graph";
    -import { getRandomString } from "@pnp/core";
    -
    -// ...
    -
    -public onInit(): Promise<void> {
    -
    -  return super.onInit().then(_ => {
    -
    -    // other init code may be present
    -    graph.setup(this.context);
    -  });
    -}
    -
    -public render(): void {
    -
    -  // here we are creating a team with a random name, required Group ReadWrite All permissions
    -  const teamName = `ATeam.${getRandomString(4)}`;
    -
    -  this.domElement.innerHTML = `Hello, I am creating a team named "${teamName}" for you...`;
    -
    -  graph.teams.create(teamName, "This is a description").then(t => {
    -
    -    this.domElement.innerHTML += "done!";
    -
    -  }).catch(e => {
    -
    -    this.domElement.innerHTML = `Oops, I ran into a problem...${JSON.stringify(e, null, 4)}`;
    -  });
    -}
    -
    -

    Calling the SharePoint API

    -

    This example shows how to use the ADALClient with the @pnp/sp library to call an API secured with AAD from within SharePoint Framework.

    -
    import { SPFxAdalClient } from "@pnp/core";
    -import { sp } from "@pnp/sp/presets/all";
    -
    -// ...
    -
    -public onInit(): Promise<void> {
    -
    -  return super.onInit().then(_ => {
    -
    -    // other init code may be present
    -    sp.setup({
    -      spfxContext: this.context,
    -      sp: {
    -        fetchClientFactory: () => new SPFxAdalClient(this.context),
    -      },
    -    });
    -
    -  });
    -}
    -
    -public render(): void {
    -
    -  sp.web().then(t => {
    -    this.domElement.innerHTML = JSON.stringify(t);
    -  }).catch(e => {
    -    this.domElement.innerHTML = JSON.stringify(e);
    -  });
    -}
    -
    -

    Calling the any API

    -

    You can also use the AdalClient to execute AAD authenticated requests to any API which is properly configured to accept the incoming tokens. This approach will only work within SharePoint Framework >= 1.6. Here we call the SharePoint REST API without the sp library as an example.

    -
    import { FetchOptions } from "@pnp/core";
    -import { AdalClient } from "@pnp/adaljsclient";
    -import { ODataDefaultParser } from "@pnp/queryable";
    -
    -// ...
    -
    -public render(): void {
    -
    -  // create an ADAL Client
    -  const client = AdalClient.fromSPFxContext(this.context);
    -
    -  // setup the request options
    -  const opts: FetchOptions = {
    -    method: "GET",
    -    headers: {
    -      "Accept": "application/json",
    -    },
    -  };
    -
    -  // execute the request
    -  client.fetch("https://{tenant}.sharepoint.com/_api/web", opts).then(response => {
    -
    -    // create a parser to convert the response into JSON.
    -    // You can create your own, at this point you have a fetch Response to work with
    -    const parser = new ODataDefaultParser();
    -
    -    parser.parse(response).then(json => {
    -      this.domElement.innerHTML = JSON.stringify(json);
    -    });
    -
    -  }).catch(e => {
    -    this.domElement.innerHTML = JSON.stringify(e);
    -  });
    -
    -}
    -
    -

    Manually Configure

    -

    This example shows setting up and using the AdalClient to make queries using information you have setup. You can review this article for more information on setting up and securing any application using AzureAD.

    -

    Setup and Use with Microsoft Graph

    -

    This sample uses a custom AzureAd app you have created and granted the appropriate permissions.

    -
    import { AdalClient } from "@pnp/adaljsclient";
    -import { graph } from "@pnp/graph";
    -
    -// configure the graph client
    -// parameters are:
    -// client id - the id of the application you created in azure ad
    -// tenant - can be id or URL (shown)
    -// redirect url - absolute url of a page to which your application and Azure AD app allows replies
    -graph.setup({
    -    graph: {
    -        fetchClientFactory: () => {
    -            return new AdalClient(
    -                "00000000-0000-0000-0000-000000000000",
    -                "{tenant}.onmicrosoft.com",
    -                "https://myapp/singlesignon.aspx");
    -        },
    -    },
    -});
    -
    -try {
    -
    -    // call the graph API
    -    const groups = await graph.groups();
    -
    -    console.log(JSON.stringify(groups, null, 4));
    -
    -} catch (e) {
    -    console.error(e);
    -}
    -
    -

    Nodejs Applications

    -

    We have a dedicated node client in @pnp/nodejs.

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/authentication/bearertokenclient/index.html b/docs/v3/v2/authentication/bearertokenclient/index.html deleted file mode 100644 index 9b1498120..000000000 --- a/docs/v3/v2/authentication/bearertokenclient/index.html +++ /dev/null @@ -1,2251 +0,0 @@ - - - - - - - - - - - - - - - - - - Bearer Token Client - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/core/BearerTokenFetchClient

    -

    The BearerTokenFetchClient takes a single parameter representing an access token and uses it to make the requests.

    -
    -

    The disadvantage to this approach is not knowing to where the request will be sent, which in some cases is fine. An alternative is the LambdaFetchClient

    -
    -

    Static

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -import { BearerTokenFetchClient } from "@pnp/core";
    -import { myTokenFactory } from "./my-auth.js";
    -
    -graph.setup({
    -    graph: {
    -        fetchClientFactory: () => {
    -
    -            // note this method is not async, so your logic here cannot await.
    -            // Please see the LambdaFetchClient if you have a need for async support.
    -            const token = myTokenFactory();
    -            return new BearerTokenFetchClient(token);
    -        },
    -    },
    -});
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/authentication/client-spa/index.html b/docs/v3/v2/authentication/client-spa/index.html deleted file mode 100644 index 6efdf446f..000000000 --- a/docs/v3/v2/authentication/client-spa/index.html +++ /dev/null @@ -1,2206 +0,0 @@ - - - - - - - - - - - - - - - - - - SPA Auth - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Authentication in Single Page Application

    -

    If you are writing a single page application deployed outside SharePoint it is recommended to use the MSAL client. You can find further details on the settings in the MSAL docs. You will need to ensure that you grant the permissions required to the application you are trying to use.

    -
    import { MsalClientSetup  } from "@pnp/msaljsclient";
    -import { graph } from "@pnp/graph/presets/all";
    -
    -graph.setup({
    -    graph: {
    -        fetchClientFactory: MsalClientSetup({
    -            auth: {
    -                authority: "https://login.microsoftonline.com/common",
    -                clientId: "00000000-0000-0000-0000-000000000000",
    -                redirectUri: "{your redirect uri}",
    -            },
    -            cache: {
    -                cacheLocation: "sessionStorage",
    -            },
    -        }, ["email", "Files.Read.All", "User.Read.All"]),
    -    },
    -});
    -
    -const data = await graph.me();
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/authentication/client-spfx/index.html b/docs/v3/v2/authentication/client-spfx/index.html deleted file mode 100644 index 25ada5bb2..000000000 --- a/docs/v3/v2/authentication/client-spfx/index.html +++ /dev/null @@ -1,2397 +0,0 @@ - - - - - - - - - - - - - - - - - - SPFx Auth - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Authentication in SharePoint Framework

    -

    Auth as Current User

    -

    PnPjs is designed to work as easily as possible within the SharePoint Framework so the authentication setup is very simple for the base case. Supply the current SharePoint Framework context to the library. This works for both SharePoint authentication and Graph authentication using the current user. Graph permissions are controlled by the permissions granted to the SharePoint shared application within your tenant.

    -

    The below example is taken from a SharePoint Framework webpart.

    -

    Connect to SharePoint as Current User

    -
    import { sp } from "@pnp/sp/presets/all";
    -
    -// ...
    -
    -protected async onInit(): Promise<void> {
    -
    -  await super.onInit();
    -
    -  // other init code may be present
    -
    -  sp.setup(this.context);
    -}
    -
    -// ...
    -
    -

    Connect to Graph as Current User

    -

    Permissions for this graph connection are controlled by the Shared SharePoint Application. You can target other applications using the MSAL Client.

    -
    import { graph } from "@pnp/graph/presets/all";
    -
    -// ...
    -
    -protected async onInit(): Promise<void> {
    -
    -  await super.onInit();
    -
    -  // other init code may be present
    -
    -  // this will use the ADAL client behind the scenes with no additional configuration work
    -  graph.setup(this.context);
    -}
    -
    -// ...
    -
    -

    MSAL Client

    -

    You might want/need to use a client configured to use your own AAD application and not the shared SharePoint application. You can do so using the MSAL client. Here we show this using graph, this works the same with any of the setup strategies. Please see the MSAL library docs for more details on what values to supply in the configuration.

    -
    -

    Note: you must install the @pnp/msaljsclient client package before using it

    -
    -
    import { MsalClientSetup  } from "@pnp/msaljsclient";
    -import { graph } from "@pnp/graph/presets/all";
    -
    -// ...
    -
    -protected async onInit(): Promise<void> {
    -
    -  await super.onInit();
    -
    -  // other init code may be present
    -
    -  graph.setup({
    -      graph: {
    -          fetchClientFactory: MsalClientSetup({
    -              auth: {
    -                  authority: "https://login.microsoftonline.com/common",
    -                  clientId: "00000000-0000-0000-0000-000000000000",
    -                  redirectUri: "{your redirect uri}",
    -              },
    -              cache: {
    -                  cacheLocation: "sessionStorage",
    -              },
    -          }, ["email", "Files.Read.All", "User.Read.All"]),
    -      },
    -  });
    -}
    -
    -// ...
    -
    -

    ADAL Client

    -

    You can use the ADAL client from within SPFx, though it is recommended to transition to the MSAL client.

    -
    -

    Note: you must install the @pnp/adaljsclient client package before using it

    -
    -
    import { AdalClient  } from "@pnp/adaljsclient";
    -import { graph } from "@pnp/graph/presets/all";
    -
    -// ...
    -
    -protected async onInit(): Promise<void> {
    -
    -  await super.onInit();
    -
    -  // other init code may be present
    -
    -  graph.setup({
    -      graph: {
    -          fetchClientFactory: () => {
    -            return new AdalClient(
    -                "00000000-0000-0000-0000-000000000000",
    -                "{tenant}.onmicrosoft.com",
    -                "");
    -          },
    -  });
    -}
    -
    -// ...
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/authentication/index.html b/docs/v3/v2/authentication/index.html deleted file mode 100644 index ef50de4d8..000000000 --- a/docs/v3/v2/authentication/index.html +++ /dev/null @@ -1,2307 +0,0 @@ - - - - - - - - - - - - - - - - - - Getting Started - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Authentication

    -

    One of the more challenging aspects of web development is ensuring you are properly authenticated to access the resources you need. This section is designed to guide you through connecting to the resources you need using the appropriate methods.

    -

    There are two places the PnPjs libraries can be used to connect to various services client (browser) or server.

    -

    Utility Scenarios

    - -

    Client Scenarios

    - -

    Server Scenarios

    - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/authentication/lambdaclient/index.html b/docs/v3/v2/authentication/lambdaclient/index.html deleted file mode 100644 index 21c271eb1..000000000 --- a/docs/v3/v2/authentication/lambdaclient/index.html +++ /dev/null @@ -1,2304 +0,0 @@ - - - - - - - - - - - - - - - - - - Lambda Token Client - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/core/LambdaFetchClient

    -

    The LambdaFetchClient class allows you to provide an async function that returns an access token using any logic/supporting libraries you need. This provides total freedom to define how you do authentication, so long as it results in a usable Bearer token to call the target resource. The advantage to the LambdaFetchClient is that you get the url for each request, meaning your logic can account for where the request is headed.

    -
    -

    The token function should be as efficient as possible as it's logic must complete before each request will be sent.

    -
    -

    Signature

    -

    The LambdaFetchClient accepts a single argument of type ILambdaTokenFactoryParams.

    -
    // signature of method, the return string is the access token
    -(parms: ILambdaTokenFactoryParams) => Promise<string>
    -
    -// ILambdaTokenFactoryParams
    -export interface ILambdaTokenFactoryParams {
    -    /**
    -     * Url to which the request for which we are requesting a token will be sent
    -     */
    -    url: string;
    -    /**
    -     * Any options supplied for the request
    -     */
    -    options: IFetchOptions;
    -}
    -
    -

    @azure/msal-browser example

    -

    This example shows how to use @azure/msal-browser along with LambdaFetchClient to achieve signin. msal-browser has many possible configurations which are described within their documentation.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -import { LambdaFetchClient } from "@pnp/core";
    -import { PublicClientApplication, Configuration } from "@azure/msal-browser";
    -
    -const config: Configuration = {
    -    auth: {
    -        clientId: "{client id}",
    -        authority: "https://login.microsoftonline.com/common/"
    -    }
    -}
    -
    -// create a single application, could also create this within the lambda client, but it would create a new applicaiton per request
    -const msal = new PublicClientApplication(config);
    -
    -// create a new instance of the lambda fetch client
    -const client = new LambdaFetchClient(async () => {
    -
    -    const request = {
    -        scopes: ["User.Read.All"],
    -    };
    -
    -    const response = await msal.loginPopup(request);
    -
    -    // lamba returns the access token
    -    return response.accessToken;
    -});
    -
    -// setup graph with the client
    -graph.setup({
    -    graph: {
    -        fetchClientFactory: () => client,
    -    },
    -});
    -
    -// execute the request to graph which will use the client defined above
    -const result = await graph.users();
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/authentication/msaljsclient/index.html b/docs/v3/v2/authentication/msaljsclient/index.html deleted file mode 100644 index 3df1f5a4d..000000000 --- a/docs/v3/v2/authentication/msaljsclient/index.html +++ /dev/null @@ -1,2413 +0,0 @@ - - - - - - - - - - - - - - - - - - MSAL Client - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    msaljsclient - MSAL Client for PnPjs

    -

    The MSAL client is a thin wrapper around the MSAL library adapting it for use with PnPjs's request pipeline.

    -

    Install

    -

    You need to install the MSAL client before using it. This is in addition to installing the other PnPjs libraries you require.

    -

    npm install @pnp/msaljsclient --save

    -

    Configure

    -

    The PnP client is a very thin wrapper around the MSAL library and you can supply any of the arguments supported. These are described in the MSAL docs.

    -

    The basic configuration values you need (at least from our testing) are client id, authority, and redirectUri. The other options are settable but not required. This article is not intended to be an exhaustive discussion of all the MSAL configuration possibilities, please see the official docs to understand all of the available options.

    -

    The second parameter when configuring the PnP client is the list of scope you are seeking to use. These must be configured and properly granted within AAD and you can request one or more scopes as needed for the current scenario.

    -

    Use in SPFx

    -

    Calling SharePoint via MSAL

    -
    -

    When calling the SharePoint REST API we must use only a special scope "https://{tenant}.sharepoint.com/.default"

    -
    -
    import { MsalClientSetup  } from "@pnp/msaljsclient";
    -import { sp } from "@pnp/sp/presets/all";
    -
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: MsalClientSetup({
    -            auth: {
    -                authority: "https://login.microsoftonline.com/mytentant.onmicrosoft.com/",
    -                clientId: "00000000-0000-0000-0000-000000000000",
    -                redirectUri: "https://mytentant.sharepoint.com/sites/dev/SitePages/test.aspx",
    -            },
    -        }, ["https://mytentant.sharepoint.com/.default"]),
    -    },
    -});
    -
    -const r = await sp.web();
    -
    -

    Calling Graph via MSAL

    -
    -

    When calling the graph API you must specify the scopes you need and ensure they are configured in AAD

    -
    -
    import { MsalClientSetup } from "@pnp/msaljsclient";
    -import { graph } from "@pnp/graph/presets/all";
    -
    -graph.setup({
    -    graph: {
    -        fetchClientFactory: MsalClientSetup({
    -            auth: {
    -                authority: "https://login.microsoftonline.com/tenant.onmicrosoft.com/",
    -                clientId: "00000000-0000-0000-0000-000000000000",
    -                redirectUri: "https://tenant.sharepoint.com/sites/dev/SitePages/test.aspx",
    -            },
    -        }, ["Group.Read.All"]),
    -    },
    -});
    -
    -const r = await graph.groups();
    -
    -

    Use in Single Page Applications

    -

    You can also use the PnPjs MSAL client within your SPA applications. Please review the various settings to ensure you are configuring MSAL as needed for your application

    -
    import { MsalClientSetup } from "@pnp/msaljsclient";
    -import { graph } from "@pnp/graph/presets/all";
    -
    -graph.setup({
    -    graph: {
    -        fetchClientFactory: MsalClientSetup({
    -            auth: {
    -                authority: "https://login.microsoftonline.com/tenant.onmicrosoft.com/",
    -                clientId: "00000000-0000-0000-0000-000000000000",
    -                redirectUri: "https://myapp.com/login.aspx",
    -            },
    -        }, ["Group.Read.All"]),
    -    },
    -});
    -
    -const r = await graph.groups();
    -
    -

    Get a Token

    -

    You can also use the client to get a token if you need a token for use outside the PnPjs libraries

    -
    import { MsalClient } from "@pnp/msaljsclient";
    -
    -// note we do not provide scopes here as the second parameter. We certainly could and will get a token
    -// based on those scopes by making a call to getToken() without a param.
    -const client = new MsalClient({
    -    auth: {
    -        authority: "https://login.microsoftonline.com/{tenant}.onmicrosoft.com/",
    -        clientId: "00000000-0000-0000-0000-000000000000",
    -        redirectUri: "https://{tenant}.sharepoint.com/sites/dev/SitePages/webpacktest.aspx",
    -    },
    -});
    -
    -const token = await client.getToken(["Group.Read.All"]);
    -
    -const token2 = await client.getToken(["Files.Read"]);
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/authentication/server-nodejs/index.html b/docs/v3/v2/authentication/server-nodejs/index.html deleted file mode 100644 index 0e000ec3f..000000000 --- a/docs/v3/v2/authentication/server-nodejs/index.html +++ /dev/null @@ -1,2404 +0,0 @@ - - - - - - - - - - - - - - - - - - NodeJS Auth - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Authentication in Nodejs

    -

    SharePoint App Registration

    -
    -

    Due to a recent change in how SPO is configured NEW tenants will have ACS authentication disabled by default. You can read more details in this article. For testing we recommend using MSAL Certificate Auth.

    -
    -

    Within the PnPjs testing framework we make use of SharePoint App Registration. This uses the SPFetchClient client from the nodejs package. This client works based on the legacy SharePoint App Registration model making use of a client and secret granted permissions through AppInv.aspx. This method works and at the time of writing has no published end date.

    -

    See: details on how to register a legacy SharePoint application.

    -
    import { SPFetchClient } from "@pnp/nodejs";
    -import { sp } from "@pnp/sp/presets/all";
    -
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -            return new SPFetchClient("{site url}", "{client id}", "{client secret}");
    -        },
    -    },
    -});
    -
    -// execute a library request as normal
    -const w = await sp.web();
    -
    -

    MSAL

    -

    Added in 2.0.11

    -

    You can now use the @azure/msal-node client with PnPjs using MsalFetchClient. You must configure an AAD application with the appropriate permissions for your application.

    -
    -

    At the time this article was written the msal-node package is not yet GA.

    -
    -

    Call Graph

    -

    You can call the Microsoft Graph API with a client id and secret or certificate (see SharePoint example for cert auth)

    -
    import { graph } from "@pnp/graph/presets/all";
    -
    -// configure your node options
    -graph.setup({
    -  graph: {
    -    fetchClientFactory: () => {
    -      return new MsalFetchClient({
    -        auth: {
    -          authority: "https://login.microsoftonline.com/{tenant id or common}/",
    -          clientId: "{guid}",
    -          clientSecret: "{client secret}",
    -        }
    -      });
    -    },
    -  },
    -});
    -
    -const userInfo = await graph.users();
    -
    -

    Call SharePoint

    -

    To call the SharePoint APIs via MSAL you are required to use certificate authentication with your application. Fully covering certificates is outside the scope of these docs, but the following commands were used with openssl to create testing certs for the sample code below.

    -
    mkdir \temp
    -cd \temp
    -openssl req -x509 -newkey rsa:2048 -keyout keytmp.pem -out cert.pem -days 365 -passout pass:HereIsMySuperPass -subj '/C=US/ST=Washington/L=Seattle'
    -openssl rsa -in keytmp.pem -out key.pem -passin pass:HereIsMySuperPass
    -
    -

    Using the above code you end up with three files, "cert.pem", "key.pem", and "keytmp.pem". The "cert.pem" file is uploaded to your AAD application registration. The "key.pem" is read as the private key for the configuration.

    -
    -

    You need to set the baseUrl property when using the MsalFetchClient

    -
    -
    import { sp } from "@pnp/sp";
    -import "@pnp/sp/webs";
    -import { readFileSync } from "fs";
    -
    -// read in our private key
    -const buffer = readFileSync("c:/temp/key.pem");
    -
    -// configure node options
    -sp.setup({
    -  sp: {
    -    baseUrl: "https://{my tenant}.sharepoint.com/sites/dev/",
    -    fetchClientFactory: () => {
    -      return new MsalFetchClient({
    -        auth: {
    -          authority: "https://login.microsoftonline.com/{tenant id or common}/",
    -          clientCertificate: {
    -            thumbprint: "{certificate thumbprint, displayed in AAD}",
    -            privateKey: buffer.toString(),
    -          },
    -          clientId: "{client id}",
    -        }
    -      }, ["https://{my tenant}.sharepoint.com/.default"]); // you must set the scope for SharePoint access
    -    },
    -  },
    -});
    -
    -const w = await sp.web();
    -
    -

    ADAL

    -

    The AdalFetchClient class depends on the adal-node package to authenticate against Azure AD. The example below -outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected.

    -

    See: More details on the node client

    -
    import { AdalFetchClient } from "@pnp/nodejs";
    -import { graph } from "@pnp/graph/presets/all";
    -
    -// setup the client using graph setup function
    -graph.setup({
    -    graph: {
    -        fetchClientFactory: () => {
    -            return new AdalFetchClient("{tenant}", "{app id}", "{app secret}");
    -        },
    -    },
    -});
    -
    -// execute a library request as normal
    -const g = await graph.groups();
    -
    -console.log(JSON.stringify(g, null, 4));
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/authentication/sp-app-registration/index.html b/docs/v3/v2/authentication/sp-app-registration/index.html deleted file mode 100644 index cbbb7139b..000000000 --- a/docs/v3/v2/authentication/sp-app-registration/index.html +++ /dev/null @@ -1,2273 +0,0 @@ - - - - - - - - - - - - - - - - - - SP App Reg - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Legacy SharePoint App Registration

    -

    This section outlines how to register for a client id and secret for use in the above code.

    -
    -

    Due to a recent change in how SPO is configured NEW tenants will have ACS authentication disabled by default. You can read more details in this article. For testing we recommend using MSAL Certificate Authentication.

    -
    -

    Register An Add-In

    -

    Before you can begin running tests you need to register a low-trust add-in with SharePoint. This is primarily designed for Office 365, but can work on-premises if you configure your farm accordingly.

    -
      -
    1. Navigation to {site url}/_layouts/appregnew.aspx
    2. -
    3. Click "Generate" for both the Client Id and Secret values
    4. -
    5. Give you add-in a title, this can be anything but will let you locate it in the list of add-in permissions
    6. -
    7. Provide a fake value for app domain and redirect uri
    8. -
    9. Click "Create"
    10. -
    11. Copy the returned block of text containing the client id and secret as well as app name for your records and later in this article.
    12. -
    -

    Grant Your Add-In Permissions

    -

    Now that we have created an add-in registration we need to tell SharePoint what permissions it can use. Due to an update in SharePoint Online you now have to register add-ins with certain permissions in the admin site.

    -
      -
    1. Navigate to {admin site url}/_layouts/appinv.aspx
    2. -
    3. Paste your client id from the above section into the App Id box and click "Lookup"
    4. -
    5. You should see the information populated into the form from the last section, if not ensure you have the correct id value
    6. -
    7. Paste the below XML into the permissions request xml box and hit "Create"
    8. -
    9. You should get a confirmation message.
    10. -
    -
      <AppPermissionRequests AllowAppOnlyPolicy="true">
    -    <AppPermissionRequest Scope="http://sharepoint/content/tenant" Right="FullControl" />
    -    <AppPermissionRequest Scope="http://sharepoint/social/tenant" Right="FullControl" />
    -    <AppPermissionRequest Scope="http://sharepoint/search" Right="QueryAsUserIgnoreAppPrincipal" />
    -  </AppPermissionRequests>
    -
    -

    Note that the above XML will grant full tenant control. This is OK for testing, but you should grant only those permissions necessary for your application in production.

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/common/collections/index.html b/docs/v3/v2/common/collections/index.html deleted file mode 100644 index a3a883f1d..000000000 --- a/docs/v3/v2/common/collections/index.html +++ /dev/null @@ -1,2277 +0,0 @@ - - - - - - - - - - - - - - - - - - collections - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/core/collections

    -

    The collections module provides typings and classes related to working with dictionaries.

    -

    TypedHash

    -

    Interface used to described an object with string keys corresponding to values of type T

    -
    export interface TypedHash<T> {
    -    [key: string]: T;
    -}
    -
    -

    objectToMap

    -

    Converts a plain object to a Map instance

    -
    const map = objectToMap({ a: "b", c: "d"});
    -
    -

    mergeMaps

    -

    Merges two or more maps, overwriting values with the same key. Last value in wins.

    -
    const m1 = new Map();
    -const m2 = new Map();
    -const m3 = new Map();
    -const m4 = new Map();
    -
    -const m = mergeMaps(m1, m2, m3, m4);
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/common/custom-httpclientimpl/index.html b/docs/v3/v2/common/custom-httpclientimpl/index.html deleted file mode 100644 index 9b6f812be..000000000 --- a/docs/v3/v2/common/custom-httpclientimpl/index.html +++ /dev/null @@ -1,2300 +0,0 @@ - - - - - - - - - - - - - - - - - - Custom HttpClientImpl - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Custom HttpClientImpl

    -

    This should be considered an advanced topic and creating a custom HttpClientImpl is not something you will likely need to do. Also, we don't offer support beyond this article for writing your own implementation.

    -

    It is possible you may need complete control over the sending and receiving of requests.

    -

    Before you get started read and understand the fetch specification as you are essentially writing a custom fetch implementation.

    -

    The first step (second if you read the fetch spec as mentioned just above) is to understand the interface you need to implement, HttpClientImpl.

    -
    export interface HttpClientImpl {
    -    fetch(url: string, options: FetchOptions): Promise<Response>;
    -}
    -
    -

    There is a single method "fetch" which takes a url string and a set of options. These options can be just about anything but are constrained within the library to the FetchOptions interface.

    -
    export interface FetchOptions {
    -    method?: string;
    -    headers?: HeadersInit | { [index: string]: string };
    -    body?: BodyInit;
    -    mode?: string | RequestMode;
    -    credentials?: string | RequestCredentials;
    -    cache?: string | RequestCache;
    -}
    -
    -

    So you will need to handle any of those options along with the provided url when sending your request. The library will expect your implementation to return a Promise that resolves to a Response defined by the fetch specification - which you've already read 👍.

    -

    Using Your Custom HttpClientImpl

    -

    Once you have written your implementation using it on your requests is done by setting it in the global library configuration:

    -
    import { setup } from "@pnp/core";
    -import { sp, Web } from "@pnp/sp";
    -import { MyAwesomeClient } from "./awesomeclient";
    -
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -            return new MyAwesomeClient();
    -        }
    -    }
    -});
    -
    -let w = new Web("{site url}");
    -
    -// this request will use your client.
    -const result = await w.select("Title")();
    -console.log(result);
    -
    -

    Subclassing is Better

    -

    You can of course inherit from one of the implementations available within the @pnp scope if you just need to say add a header or need to do something to every request sent. Perhaps some advanced logging. This approach will save you from needing to fully write a fetch implementation.

    -

    A FINAL NOTE

    -

    Whatever you do, do not write a client that uses a client id and secret and exposes them on the client side. Client Id and Secret should only ever be used on a server, never exposed to clients as anyone with those values has the full permissions granted to that id and secret.

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/common/index.html b/docs/v3/v2/common/index.html deleted file mode 100644 index cedf6573e..000000000 --- a/docs/v3/v2/common/index.html +++ /dev/null @@ -1,2262 +0,0 @@ - - - - - - - - - - - - - - - - - - common - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/core

    -

    npm version

    -

    The common modules provides a set of utilities classes and reusable building blocks used throughout the @pnp modules. They can be used within your applications as well.

    -

    Getting Started

    -

    Install the library and required dependencies

    -

    npm install @pnp/core --save

    -

    Import and use functionality, see details on modules below.

    -
    import { getGUID } from "@pnp/core";
    -
    -console.log(getGUID());
    -
    -

    Exports

    - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/common/libconfig/index.html b/docs/v3/v2/common/libconfig/index.html deleted file mode 100644 index f6c3badc9..000000000 --- a/docs/v3/v2/common/libconfig/index.html +++ /dev/null @@ -1,2413 +0,0 @@ - - - - - - - - - - - - - - - - - - libconfig - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/core/libconfig

    -

    Contains the shared classes and interfaces used to configure the libraries. These bases classes are expanded on in dependent libraries with the core configuration defined here. This module exposes an instance of the RuntimeConfigImpl class: RuntimeConfig. This configuration object can be referenced and contains the global configuration shared across the libraries. You can also extend the configuration for use within your own applications.

    -

    ILibraryConfiguration Interface

    -

    Defines the shared configurable values used across the library as shown below. Each of these has a default value as shown below

    -
    export interface ILibraryConfiguration {
    -
    -    /**
    -     * Allows caching to be global disabled, default: false
    -     */
    -    globalCacheDisable?: boolean;
    -
    -    /**
    -     * Defines the default store used by the usingCaching method, default: session
    -     */
    -    defaultCachingStore?: "session" | "local";
    -
    -    /**
    -     * Defines the default timeout in seconds used by the usingCaching method, default 30
    -     */
    -    defaultCachingTimeoutSeconds?: number;
    -
    -    /**
    -     * If true a timeout expired items will be removed from the cache in intervals determined by cacheTimeoutInterval
    -     */
    -    enableCacheExpiration?: boolean;
    -
    -    /**
    -     * Determines the interval in milliseconds at which the cache is checked to see if items have expired (min: 100)
    -     */
    -    cacheExpirationIntervalMilliseconds?: number;
    -
    -    /**
    -     * Used to supply the current context from an SPFx webpart to the library
    -     */
    -    spfxContext?: any;
    -}
    -
    -

    RuntimeConfigImpl

    -

    The class which implements the runtime configuration management as well as sets the default values used within the library. At its heart lies a Dictionary -used to track the configuration values. The keys will match the values in the interface or plain object passed to the extend method.

    -

    assign

    -

    The assign method is used to add configuration to the global configuration instance. You can pass it any plain object with string keys and those values will be added. Any existing values will be overwritten based on the keys. Last value in wins. For a more detailed scenario of using the RuntimeConfig instance in your own application please see the section below "Using RuntimeConfig within your application". Note there are no methods to remove/clear the global config as it should be considered fairly static as frequent updates may have unpredictable side effects as it is a global shared object. Generally it should be set at the start of your application.

    -
    import { RuntimeConfig } from "@pnp/core";
    -
    -// add your custom keys to the global configuration
    -// note you can use object hashes as values
    -RuntimeConfig.assign({
    -   "myKey1": "value 1",
    -   "myKey2": {
    -       "subKey": "sub value 1",
    -       "subKey2": "sub value 2",
    -   },
    -});
    -
    -// read your custom values
    -const v = RuntimeConfig.get("myKey1"); // "value 1"
    -
    -

    Using RuntimeConfig within your Application

    -

    If you have a set of properties you will access very frequently it may be desirable to implement your own configuration object and expose those values as properties. To do so you will need to create an interface for your configuration (optional) and a wrapper class for RuntimeConfig to expose your properties

    -
    import { ILibraryConfiguration, RuntimeConfig, ITypedHash } from "@pnp/core";
    -
    -// first we create our own interface by extending LibraryConfiguration. This allows your class to accept all the values with correct type checking. Note, because
    -// TypeScript allows you to extend from multiple interfaces you can build a complex configuration definition from many sub definitions.
    -
    -// create the interface of your properties
    -// by creating this separately you allows others to compose your parts into their own config
    -interface MyConfigurationPart {
    -
    -    // you can create a grouped definition and access your settings as an object
    -    // keys can be optional or required as defined by your interface
    -    my?: {
    -        prop1?: string;
    -        prop2?: string;
    -    }
    -
    -    // and/or define multiple top level properties (beware key collision)
    -    // it is good practice to use a unique prefix
    -    myProp1: string;
    -    myProp2: number;
    -}
    -
    -// now create a combined interface
    -interface MyConfiguration extends ILibraryConfiguration, MyConfigurationPart { }
    -
    -
    -// now create a wrapper object and expose your properties
    -class MyRuntimeConfigImpl {
    -
    -    // exposing a nested property
    -    public get prop1(): ITypedHash<string> {
    -
    -        const myPart = RuntimeConfig.get("my");
    -        if (myPart !== null && typeof myPart !== "undefined" && typeof myPart.prop1 !== "undefined") {
    -            return myPart.prop1;
    -        }
    -
    -        return {};
    -    }
    -
    -    // exposing a root level property
    -    public get myProp1(): string | null {
    -
    -        let myProp1 = RuntimeConfig.get("myProp1");
    -
    -        if (myProp1 === null) {
    -            myProp1 = "some default value";
    -        }
    -
    -        return myProp1;
    -    }
    -
    -    setup(config: MyConfiguration): void {
    -        RuntimeConfig.assign(config);
    -    }
    -}
    -
    -// create a single static instance of your impl class
    -export let MyRuntimeConfig = new MyRuntimeConfigImpl();
    -
    -

    Now in other files you can use and set your configuration with a typed interface and properties

    -
    import { MyRuntimeConfig } from "{location of module}";
    -
    -
    -MyRuntimeConfig.setup({
    -    my: {
    -        prop1: "hello",
    -    },
    -});
    -
    -const value = MyRuntimeConfig.myProp1; // "hello"
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/common/netutil/index.html b/docs/v3/v2/common/netutil/index.html deleted file mode 100644 index e44173460..000000000 --- a/docs/v3/v2/common/netutil/index.html +++ /dev/null @@ -1,2350 +0,0 @@ - - - - - - - - - - - - - - - - - - netutil - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/core/net

    -

    This module contains a set of classes and interfaces used to characterize shared http interactions and configuration of the libraries. Some of the interfaces -are described below (many have no use outside the library) as well as several classes.

    -

    Interfaces

    -

    HttpClientImpl

    -

    Defines an implementation of an Http Client within the context of @pnp. This being a class with a a single method "fetch" takes a URL and options. It returns a Promise<Response>. Used primarily with the shared request pipeline to define the client used to make the actual request. You can write your own custom implementation if needed.

    -

    RequestClient

    -

    An abstraction that contains specific methods related to each of the primary request methods get, post, patch, delete as well as fetch and fetchRaw. The -difference between fetch and fetchRaw is that a client may include additional logic or processing in fetch, where fetchRaw should be a direct call to the -underlying HttpClientImpl fetch method.

    -

    Classes

    -

    This module export two classes of note, FetchClient and BearerTokenFetchClient. Both implement HttpClientImpl.

    -

    FetchClient

    -

    Basic implementation that calls the global (window) fetch method with no additional processing.

    -
    import { FetchClient } from "@pnp/core";
    -
    -const client = new FetchClient();
    -
    -client.fetch("{url}", {});
    -
    -

    BearerTokenFetchClient

    -

    A simple implementation that takes a provided authentication token and adds the Authentication Bearer header to the request. No other processing is done and the token is treated as a static string.

    -
    import { BearerTokenFetchClient } from "@pnp/core";
    -
    -const client = new BearerTokenFetchClient("{authentication token}");
    -
    -client.fetch("{url}", {});
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/common/storage/index.html b/docs/v3/v2/common/storage/index.html deleted file mode 100644 index 0eecaf8c7..000000000 --- a/docs/v3/v2/common/storage/index.html +++ /dev/null @@ -1,2339 +0,0 @@ - - - - - - - - - - - - - - - - - - storage - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/core/storage

    -

    This module provides a thin wrapper over the browser storage options, local and session. If neither option is available it shims storage with a non-persistent in memory polyfill. Optionally through configuration you can activate expiration. Sample usage is shown below.

    -

    PnPClientStorage

    -

    The main export of this module, contains properties representing local and session storage.

    -
    import { PnPClientStorage } from "@pnp/core";
    -
    -const storage = new PnPClientStorage();
    -const myvalue = storage.local.get("mykey");
    -
    -

    PnPClientStorageWrapper

    -

    Each of the storage locations (session and local) are wrapped with this helper class. You can use it directly, but generally it would be used -from an instance of PnPClientStorage as shown below. These examples all use local storage, the operations are identical for session storage.

    -
    import { PnPClientStorage } from "@pnp/core";
    -
    -const storage = new PnPClientStorage();
    -
    -// get a value from storage
    -const value = storage.local.get("mykey");
    -
    -// put a value into storage
    -storage.local.put("mykey2", "my value");
    -
    -// put a value into storage with an expiration
    -storage.local.put("mykey2", "my value", new Date());
    -
    -// put a simple object into storage
    -// because JSON.stringify is used to package the object we do NOT do a deep rehydration of stored objects
    -storage.local.put("mykey3", {
    -    key: "value",
    -    key2: "value2",
    -});
    -
    -// remove a value from storage
    -storage.local.delete("mykey3");
    -
    -// get an item or add it if it does not exist
    -// returns a promise in case you need time to get the value for storage
    -// optionally takes a third parameter specifying the expiration
    -storage.local.getOrPut("mykey4", () => {
    -    return Promise.resolve("value");
    -});
    -
    -// delete expired items
    -storage.local.deleteExpired();
    -
    -

    Cache Expiration

    -

    The ability remove of expired items based on a configured timeout can help if the cache is filling up. This can be accomplished in two ways. The first is to explicitly call the new deleteExpired method on the cache you wish to clear. A suggested usage is to add this into your page init code as clearing expired items once per page load is likely sufficient.

    -
    import { PnPClientStorage } from "@pnp/core";
    -
    -const storage = new PnPClientStorage();
    -
    -// session storage
    -storage.session.deleteExpired();
    -
    -// local storage
    -storage.local.deleteExpired();
    -
    -// this returns a promise, so you can perform some activity after the expired items are removed:
    -storage.local.deleteExpired().then(_ => {
    -    // init my application
    -});
    -
    -

    The second method is to enable automated cache expiration through global config. Setting the enableCacheExpiration property to true will enable the timer. Optionally you can set the interval at which the cache is checked via the cacheExpirationIntervalMilliseconds property, by default 750 milliseconds is used. We enforce a minimum of 300 milliseconds as this functionality is enabled via setTimeout and there is little value in having an excessive number of cache checks. This method is more appropriate for a single page application where the page is infrequently reloaded and many cached operations are performed. There is no advantage to enabling cache expiration unless you are experiencing cache storage space pressure in a long running page - and you may see a performance hit due to the use of setTimeout.

    -
    
    -import { setup } from "@pnp/core";
    -
    -setup({
    -    enableCacheExpiration: true,
    -    cacheExpirationIntervalMilliseconds: 1000, // optional
    -});
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/common/util/index.html b/docs/v3/v2/common/util/index.html deleted file mode 100644 index b2d3409c8..000000000 --- a/docs/v3/v2/common/util/index.html +++ /dev/null @@ -1,2638 +0,0 @@ - - - - - - - - - - - - - - - - - - util - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/core/util

    -

    This module contains utility methods that you can import individually from the common library.

    -
    import {
    -    getRandomString,
    -} from "@pnp/core";
    -
    -// use from individually imported method
    -console.log(getRandomString(10));
    -
    -

    assign

    -

    Merges a source object's own enumerable properties into a single target object. Similar to Object.assign, but allows control of overwriting of existing -properties.

    -
    import { assign } from "@pnp/core";
    -
    -let obj1 = {
    -    prop: 1,
    -    prop2: 2,
    -};
    -
    -const obj2 = {
    -    prop: 4,
    -    prop3: 9,
    -};
    -
    -const example1 = assign(obj1, obj2);
    -// example1 = { prop: 4, prop2: 2, prop3: 9 }
    -
    -//noOverwrite = true stops overwriting existing properties
    -const example2 = assign(obj1, obj2, true);
    -// example2 = { prop: 1, prop2: 2, prop3: 9 }
    -
    -

    combine

    -

    Combines any number of paths, normalizing the slashes as required

    -
    import { combine } from "@pnp/core";
    -
    -// "https://microsoft.com/something/more"
    -const paths = combine("https://microsoft.com", "something", "more");
    -
    -// "also/works/with/relative"
    -const paths2 = combine("/also/", "/works", "with/", "/relative\\");
    -
    -

    dateAdd

    -

    Manipulates a date, please see the Stack Overflow discussion from where this method was taken.

    -
    import { dateAdd } from "@pnp/core";
    -
    -const testDate = new Date();
    -
    -dateAdd(testDate,'minute',10);
    -
    -

    getCtxCallback

    -

    Gets a callback function which will maintain context across async calls.

    -
    import { getCtxCallback } from "@pnp/core";
    -
    -const contextThis = {
    -    myProp: 6,
    -};
    -
    -function theFunction() {
    -    // "this" within this function will be the context object supplied
    -    // in this case the variable contextThis, so myProp will exist
    -    return this.myProp;
    -}
    -
    -const callback = getCtxCallback(contextThis, theFunction);
    -
    -callback(); // returns 6
    -
    -// You can also supply additional parameters if needed
    -
    -function theFunction2(g: number) {
    -    // "this" within this function will be the context object supplied
    -    // in this case the variable contextThis, so myProp will exist
    -    return this.myProp + g;
    -}
    -
    -const callback2 = getCtxCallback(contextThis, theFunction2, 4);
    -
    -callback2(); // returns 10 (6 + 4)
    -
    -

    getGUID

    -

    Creates a random guid, please see the Stack Overflow discussion from where this method was taken.

    -
    import { getGUID } from "@pnp/core";
    -
    -const newGUID = getGUID();
    -
    -

    getRandomString

    -

    Gets a random string consisting of the number of characters requested.

    -
    import { getRandomString } from "@pnp/core";
    -
    -const randomString = getRandomString(10);
    -
    -

    hOP

    -

    Shortcut for Object.hasOwnProperty. Determines if an object has a specified property.

    -
    import { HttpRequestError } from "@pnp/queryable";
    -import { hOP } from "@pnp/core";
    -
    -export async function handleError(e: Error | HttpRequestError): Promise<void> {
    -
    -  //Checks to see if the error object has a property called isHttpRequestError. Returns a bool.
    -  if (hOP(e, "isHttpRequestError")) {
    -      // Handle this type or error
    -  } else {
    -    // not an HttpRequestError so we do something else
    -
    -  }
    -}
    -
    -

    isArray

    -

    Determines if a supplied variable represents an array.

    -
    import { isArray } from "@pnp/core";
    -
    -let x:String[] = [1,2,3]];
    -
    -if (isArray(x)){
    -    console.log("I am an array");
    -}else{
    -    console.log("I am not an array");
    -}
    -
    -

    isFunc

    -

    Determines if a supplied variable represents a function.

    -
    import { isFunc } from "@pnp/core";
    -
    -public testFunction() {
    -    console.log("test function");
    -    return
    -}
    -
    -if (isFunc(testFunction)){
    -    console.log("this is a function");
    -    testFunction();
    -}
    -
    -

    isUrlAbsolute

    -

    Determines if a supplied url is absolute and returns true; otherwise returns false.

    -
    import { isUrlAbsolute } from "@pnp/core";
    -
    -const webPath = 'https://{tenant}.sharepoint.com/sites/dev/';
    -
    -if (isUrlAbsolute(webPath)){
    -    console.log("URL is absolute");
    -}else{
    -    console.log("URL is not absolute");
    -}
    -
    -

    objectDefinedNotNull

    -

    Determines if an object is defined and not null.

    -
    import { objectDefinedNotNull } from "@pnp/core";
    -
    -let obj = {
    -    prop: 1
    -};
    -
    -if (objectDefinedNotNull(obj)){
    -    console.log("Not null");
    -}else{
    -    console.log("Null");
    -}
    -
    -

    stringIsNullOrEmpty

    -

    Determines if a supplied string is null or empty.

    -
    import { stringIsNullOrEmpty } from "@pnp/core";
    -
    -let x:String = "hello";
    -
    -if (stringIsNullOrEmpty(x)){
    -    console.log("Null or empty");
    -}else{
    -    console.log("Not null or empty");
    -}
    -
    -

    Removed

    -

    Some methods that were no longer used internally by the @pnp libraries have been removed. You can find the source for those methods -below for use in your projects should you require.

    -
    /**
    - * Loads a stylesheet into the current page
    - *
    - * @param path The url to the stylesheet
    - * @param avoidCache If true a value will be appended as a query string to avoid browser caching issues
    - */
    -public static loadStylesheet(path: string, avoidCache: boolean): void {
    -    if (avoidCache) {
    -        path += "?" + encodeURIComponent((new Date()).getTime().toString());
    -    }
    -    const head = document.getElementsByTagName("head");
    -    if (head.length > 0) {
    -        const e = document.createElement("link");
    -        head[0].appendChild(e);
    -        e.setAttribute("type", "text/css");
    -        e.setAttribute("rel", "stylesheet");
    -        e.setAttribute("href", path);
    -    }
    -}
    -
    -/**
    - * Tests if a url param exists
    - *
    - * @param name The name of the url parameter to check
    - */
    -public static urlParamExists(name: string): boolean {
    -    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    -    const regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
    -    return regex.test(location.search);
    -}
    -
    -/**
    - * Gets a url param value by name
    - *
    - * @param name The name of the parameter for which we want the value
    - */
    -public static getUrlParamByName(name: string): string {
    -    name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
    -    const regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
    -    const results = regex.exec(location.search);
    -    return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
    -}
    -
    -/**
    - * Gets a url param by name and attempts to parse a bool value
    - *
    - * @param name The name of the parameter for which we want the boolean value
    - */
    -public static getUrlParamBoolByName(name: string): boolean {
    -    const p = this.getUrlParamByName(name);
    -    const isFalse = (p === "" || /false|0/i.test(p));
    -    return !isFalse;
    -}
    -
    -/**
    - * Inserts the string s into the string target as the index specified by index
    - *
    - * @param target The string into which we will insert s
    - * @param index The location in target to insert s (zero based)
    - * @param s The string to insert into target at position index
    - */
    -public static stringInsert(target: string, index: number, s: string): string {
    -    if (index > 0) {
    -        return target.substring(0, index) + s + target.substring(index, target.length);
    -    }
    -    return s + target;
    -}
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/concepts/configuration/index.html b/docs/v3/v2/concepts/configuration/index.html deleted file mode 100644 index db47642f8..000000000 --- a/docs/v3/v2/concepts/configuration/index.html +++ /dev/null @@ -1,2685 +0,0 @@ - - - - - - - - - - - - - - - - - - Configuration - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    PnPjs Configuration

    -

    This article describes the configuration architecture used by the library as well as the settings available.

    -
    -

    Starting with version 2.1.0 we updated our configuration design to support the ability to isolate settings to individual objects. The first part of this article discusses the newer design, you can read about the pre v2.1.0 configuration further down.

    -
    -

    Post v2.1.0

    -

    Architecture

    -

    Starting from v2.1.0 we have modified our configuration design to allow for configuring individual queryable objects.

    -

    Backward Compatibility

    -

    If you have no need to use the isolated runtimes introduced in 2.1.0 then you should see no change in library behavior from prior versions. You can continue to refer to the pre v2.1.0 configuration section - and if you see any issues please let us know.

    -

    All of the available settings as described below remain, unchanged.

    -
    -

    If you previously used our internal configuration classes directly RuntimeConfigImpl, SPRuntimeConfigImpl, or GraphRuntimeConfigImpl they no longer exist. We do not consider this a breaking change as they were meant to be internal and their direct use was not documented. This includes the concrete default instances RuntimeConfig, SPRuntimeConfig, and GraphRuntimeConfig.

    -
    -

    Isolated Runtimes

    -

    You can create an isolated runtime when using either the sp or graph libraries. What this does is create an isolated set of properties and behaviors specific to a given fluent chain. Have a look at this basic example below:

    -
    import { sp } from "@pnp/sp";
    -import "@pnp/sp/webs";
    -
    -// create an isolated sp root instance
    -const isolatedSP = await sp.createIsolated();
    -
    -// this configuration applies to all objects created from "sp"
    -sp.setup({
    -  sp: {
    -    baseUrl: "https://mytenant.sharepoint.com/",
    -  },
    -});
    -
    -// this configuration applies to all objects created from "isolatedSP"
    -isolatedSP.setup({
    -  sp: {
    -    baseUrl: "https://mytenant.sharepoint.com/sites/dev",
    -  },
    -});
    -
    -// details for the web at https://mytenant.sharepoint.com/
    -const web1 = await sp.web();
    -
    -// details for the web at https://mytenant.sharepoint.com/sites/dev
    -const web2 = await isolatedSP.web();
    -
    -

    This configuration is supplied to all objects down a given fluent chain:

    -
    import { sp } from "@pnp/sp";
    -import "@pnp/sp/webs";
    -import "@pnp/sp/lists";
    -
    -// create an isolated sp root instance
    -const isolatedSP = await sp.createIsolated();
    -
    -// this configuraiton applies to all objects created from "sp"
    -sp.setup({
    -  sp: {
    -    baseUrl: "https://mytenant.sharepoint.com/",
    -  },
    -});
    -
    -// this configuraiton applies to all objects created from "isolatedSP"
    -isolatedSP.setup({
    -  sp: {
    -    baseUrl: "https://mytenant.sharepoint.com/sites/dev",
    -  },
    -});
    -
    -// details for the lists at https://mytenant.sharepoint.com/
    -const lists1 = await sp.web.lists();
    -
    -// details for the lists at https://mytenant.sharepoint.com/sites/dev
    -const lists2 = await isolatedSP.web.lists();
    -
    -

    createIsolated

    -

    The createIsolated method is used to establish the isolated runtime for a given instance of either the sp or graph libraries. Once created it is no longer connected to the default instance and if you have common settings that must be updated you would need to update them across each isolated instance, this is by design. Currently sp and graph createIsolated methods accept the same init, but we have broken them out to make thing clear. All properties of the init object are optional. Any properties provided will overwrite those cloned from the default if cloneGlobal is true. If cloneGlobal is false you start with an empty config containing only the core defaults.

    -

    sp.createIsolated

    -
    import { sp, ISPConfiguration } from "@pnp/sp";
    -
    -// accept all the defaults, will clone any settings from sp
    -const isolatedSP = await sp.createIsolated();
    -
    -// - specify all the config options, using the ISPConfiguration interface to type the config
    -// - setting baseUrl in the root is equivelent to setting it with sp: { baseUrl: }, it is provided as a shortcut as this seemed to be a common use case
    -//   - if you set them both the baseUrl in the root will be used.
    -// - you can set some or all of the settings in config and if you clone from the global the ones you specify will overwrite the cloned values
    -//   - for example your global config can specify everything and your isolated config could specify a different fetchClientFactory, see node example below
    -const isolatedSP = await sp.createIsolated<ISPConfiguration>({
    -  baseUrl: "https://mytenant.sharepoint.com",
    -  cloneGlobal: false,
    -  config: {
    -    cacheExpirationIntervalMilliseconds: 1000,
    -    sp: {
    -      baseUrl: "https://mytenant.sharepoint.com",
    -      fetchClientFactory: () => void(0),
    -      headers: {
    -        "X-AnotherHeader": "54321",
    -      },
    -    },
    -    spfxContext: this.context, // only valid within SPFx
    -  },
    -  options: {
    -    headers: {
    -      "X-SomeHeader": "12345",
    -    },
    -  },
    -});
    -
    -

    Defaults

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDefault
    baseUrl""
    cloneGlobaltrue
    config{}
    options{}
    -

    graph.createIsolated

    -
    import { graph, IGraphConfiguration } from "@pnp/graph";
    -
    -// - specify all the config options, using the IGraphConfiguration interface to type the config
    -// - setting baseUrl in the root is restricted to "v1.0" or "beta". If you need to specify a different absolute url should use config.graph.baseUrl
    -//   - in practice you should use one or the other. You can always swap Graph api version using IGraphQueryable.setEndpoint
    -// - you can set some or all of the settings in config and if you clone from the global the ones you specify will overwrite the cloned values
    -//   - for example your global config can specify everything and your isolated config could specify a different fetchClientFactory, see node example below
    -const isolatedGraph = await graph.createIsolated<IGraphConfiguration>({
    -  baseUrl: "v1.0",
    -  cloneGlobal: false,
    -  config: {
    -    cacheExpirationIntervalMilliseconds: 1000,
    -    graph: {
    -      baseUrl: "https://graph.microsoft.com",
    -      fetchClientFactory: () => void(0),
    -      headers: {
    -        "X-AnotherHeader": "54321",
    -      },
    -    },
    -    spfxContext: this.context, // only valid within SPFx
    -  },
    -  options: {
    -    headers: {
    -      "X-SomeHeader": "12345",
    -    },
    -  },
    -});
    -
    -

    Defaults

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    nameDefault
    baseUrl"v1.0"
    cloneGlobaltrue
    config{}
    options{}
    -

    Additional Examples

    -

    MSAL with Node multiple site requests

    -

    MSAL Support Added in 2.0.11

    -

    In this example you can see how you can setup the MSAL client once and then set a different baseUrl for an isolated instance. More information specific to setting up the MSAL client is available.

    -
    import { sp } from "@pnp/sp";
    -import "@pnp/sp/webs";
    -import { readFileSync } from "fs";
    -
    -// read in our private key
    -const buffer = readFileSync("c:/temp/key.pem");
    -
    -// configure node options
    -sp.setup({
    -  sp: {
    -    baseUrl: "https://{my tenant}.sharepoint.com/sites/dev/",
    -    fetchClientFactory: () => {
    -      return new MsalFetchClient({
    -        auth: {
    -          authority: "https://login.microsoftonline.com/{tenant id or common}",
    -          clientCertificate: {
    -            thumbprint: "{certificate thumbprint, displayed in AAD}",
    -            privateKey: buffer.toString(),
    -          },
    -          clientId: "{client id}",
    -        }
    -      }, ["https://{my tenant}.sharepoint.com/.default"]); // you must set the scope for SharePoint access
    -    },
    -  },
    -});
    -
    -const isolatedSP = await sp.createIsolated<ISPConfigurationPart>({
    -  config: {
    -    sp: {
    -      baseUrl: "https://{my tenant}.sharepoint.com/sites/dev2/",
    -    },
    -  },
    -});
    -
    -

    Node multiple site requests

    -

    Isolated configuration was most requested for scenarios in node where you need to access information in multiple sites. This example shows setting up the global configuration and then creating an isolated config with only the baseUrl updated.

    -
    import { SPFetchClient } from "@pnp/nodejs";
    -import { ISPConfigurationPart, sp } from "@pnp/sp";
    -
    -sp.setup({
    -  cacheExpirationIntervalMilliseconds: 1000,
    -  defaultCachingStore: "local",
    -  sp: {
    -    fetchClientFactory: () => {
    -      return new SPFetchClient("https://mytenant.sharepoint.com/", "id", "secret");
    -    },
    -    headers: {
    -      "X-MyRequiredHeader": "SomeValue",
    -      "X-MyRequiredHeader2": "SomeValue",
    -    },
    -  },
    -});
    -
    -const isolatedSP = await sp.createIsolated<ISPConfigurationPart>({
    -  config: {
    -    sp: {
    -      fetchClientFactory: () => {
    -        return new SPFetchClient("https://mytenant.sharepoint.com/site/dev", "id", "secret");
    -      },
    -    },
    -  },
    -});
    -
    -

    Batching

    -

    All batching functionality works as expected, but you must take care to only associate requests from the same isolated instance as you create the batch. Mixing requests across isolation boundaries is not supported. This applies to sp and graph batching.

    -
    sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -            return new SPFetchClient("url1", "id", "secret");
    -        },
    -    },
    -});
    -
    -const isolated = await sp.createIsolated<ISPConfiguration>({
    -    config: {
    -        sp: {
    -            fetchClientFactory: () => {
    -                return new SPFetchClient("url2", "id", "secret");
    -            },
    -        },
    -    },
    -});
    -
    -const batch1 = sp.createBatch();
    -sp.web.lists.select("Title").top(3).inBatch(batch1)().then(r => console.log(`here 1: ${JSON.stringify(r, null, 2)}`));
    -sp.web.select("Title").inBatch(batch1)().then(r => console.log(`here 2: ${JSON.stringify(r, null, 2)}`));
    -await batch1.execute();
    -
    -const batch2 = isolated.createBatch();
    -isolated.web.lists.select("Title").top(3).inBatch(batch2)().then(r => console.log(`here 3: ${JSON.stringify(r, null, 2)}`));
    -isolated.web.select("Title").inBatch(batch2)().then(r => console.log(`here 4: ${JSON.stringify(r, null, 2)}`));
    -await batch2.execute();
    -
    -

    IE11 Mode

    -

    The IE11 mode setting is always global. There is no scenario we care to support where once instance needs to run in ie11 mode and another does not. Your code either does or does not run in ie11.

    -

    Prior to v2.1.0

    -

    Architecture

    -

    PnPjs uses an additive configuration design with multiple libraries sharing a single global configuration instance. If you need non-global configuration please see this section. There are three ways to access the setup functionality - through either the common, sp, or graph library's setup method. While the configuration is global the various methods have different typing on their input parameter. You can review the libconfig article for more details on storing your own configuration.

    -

    Common Configuration

    -

    The common libary's setup method takes parameters defined by ILibraryConfiguration. The properties and their defaults are listed below, followed by a code sample. You can call setup multiple times and any new values will be added to the existing configuration or replace the previous value if one existed.

    -

    All values are optional.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescriptionDefault
    defaultCachingStoreWhere will PnPjs store cached data by default (session or local)session
    defaultCachingTimeoutSecondsThe global default value used for cached data timeouts in seconds60
    globalCacheDisableProvides a way to globally within PnPjs disable all cachingfalse
    enableCacheExpirationIf true a timeout expired items will be removed from the cache in intervals determined by cacheTimeoutIntervalfalse
    cacheExpirationIntervalMillisecondsDetermines the interval in milliseconds at which the cache is checked to see if items have expired (min: 100)750
    spfxContextWhen running in SPFx the current context should always be supplied to PnPjs when availablenull
    ie11If true the library downgrades functionality to work in IE11false
    -
    -

    For more information on setting up in SPFx please see the authentication section

    -

    For more details on ie11 mode please see the topic article

    -
    -
    import { setup } from "@pnp/core";
    -
    -// called before other code
    -setup({
    -  cacheExpirationIntervalMilliseconds: 15000,
    -  defaultCachingStore: "local",
    -  defaultCachingTimeoutSeconds: 600,
    -  enableCacheExpiration: true,
    -  globalCacheDisable: false,
    -  ie11: false,
    -  spfxContext: this.context, // if in SPFx, otherwise leave it out
    -});
    -
    -

    SP Configuration

    -

    The sp library's configuration is defined by the ISPConfiguration interface which extends ILibraryConfiguration. All of the sp values are contained in a top level property named "sp". The following table describes the properties with a code sample following.

    -

    All values are optional.

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescriptionDefault
    headersAllows you to apply any headers to all calls made by the sp librarynone
    baseUrlAllows you to define a base site url for all requests, takes precedence over all other url logic. Must be absolute.none
    fetchClientFactoryAllows you to specify a factory function used to produce IHttpClientImpl instancesnone
    -
    -

    There are many examples of using fetchClientFactory available in the authentication section.

    -
    -
    import { sp } from "@pnp/sp";
    -import { SPFxAdalClient } from "@pnp/core";
    -
    -// note you can still set the global configuration such as ie11 using the same object as 
    -// the interface extends ILibraryConfiguration
    -sp.setup({
    -  ie11: false,
    -  sp: {
    -    baseUrl: "https://tenant.sharepoint.com/sites/dev",
    -    fetchClientFactory: () => {
    -      return new SPFxAdalClient(this.context);
    -    },
    -    headers: {
    -      "Accept": "application/json;odata=verbose",
    -      "X-Something": "header-value",
    -    },
    -  },
    -  spfxContext: this.context,
    -});
    -
    -

    SharePoint Framework

    -

    You can optionally supply only the SPFx context to the sp configure method.

    -
    import { sp } from "@pnp/sp";
    -
    -// in SPFx only
    -sp.setup(this.context);
    -
    -

    Graph Configuration

    -

    The graph configuration works exactly the same as the sp configuration but is defined by the IGraphConfiguration interface which extends ILibraryConfiguration. All of the graph values are contained in a top level property named "graph". The following table describes the properties with a code sample following.

    -

    All values are optional.

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescriptionDefault
    headersAllows you to apply any headers to all calls made by the sp librarynone
    baseUrlAllows you to define a base site url for all requests, takes precedence over all other url logic. Must be absolute. (Added in 2.0.8)none
    fetchClientFactoryAllows you to specify a factory function used to produce IHttpClientImpl instancesnone
    -
    -

    There are many examples of using fetchClientFactory available in the authentication section.

    -
    -
    import { graph } from "@pnp/graph";
    -import { MsalClientSetup } from "@pnp/msaljsclient";
    -
    -// note you can still set the global configuration such as ie11 using the same object as 
    -// the interface extends ILibraryConfiguration
    -graph.setup({
    -  ie11: false,
    -  graph: {
    -    // we set the GCC url
    -    baseUrl: "https://graph.microsoft.us",
    -    fetchClientFactory: MsalClientSetup({
    -        auth: {
    -            authority: "https://login.microsoftonline.com/tenant.onmicrosoft.com",
    -            clientId: "00000000-0000-0000-0000-000000000000",
    -            redirectUri: "https://tenant.sharepoint.com/sites/dev/SitePages/test.aspx",
    -        },
    -    }, ["Group.Read.All"]),
    -    headers: {
    -      "Accept": "application/json;odata=verbose",
    -      "X-Something": "header-value",
    -    },
    -  },
    -  spfxContext: this.context,
    -});
    -
    -

    SharePoint Framework

    -

    You can optionally supply only the SPFx context to the graph configure method. We will attempt to set the baseUrl property from the context - but if that is failing in your environment and you need to call a special cloud (i.e. graph.microsoft.us) please set the baseUrl property.

    -
    import { graph } from "@pnp/graph";
    -
    -// in SPFx only
    -graph.setup(this.context);
    -
    -

    Configure Everything At Once

    -

    In some cases you might want to configure everything in one go. Because the configuration is stored in a single location you can use the common library's setup method and adjust the typings to ensure you are using the correct property names while only having to setup things with a single call.

    -
    -

    In versions before 2.0.8 ISPConfigurationPart, IGraphConfigurationPart, and ILibraryConfiguration incorrectly were missing the "I" prefix. That was fixed in 2.0.8 - but note if you are using an older version of the library you'll need to use the old names. Everything else in the below example works as expected.

    -
    -
    import { ISPConfigurationPart } from "@pnp/sp";
    -import { IGraphConfigurationPart } from "@pnp/graph";
    -import { ILibraryConfiguration, setup } from "@pnp/core";
    -
    -// you could also include your custom configuration parts
    -export interface AllConfig extends ILibraryConfiguration, ISPConfigurationPart, IGraphConfigurationPart { }
    -
    -// create a single big configuration entry
    -const config: AllConfig = {
    -  graph: {
    -    baseUrl: "https://graph.microsoft.us",
    -  },
    -  ie11: false,
    -  sp: {
    -    baseUrl: "https://tenant.sharepoint.com/sites/dev",
    -  },
    -};
    -
    -setup(config);
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/concepts/custom-bundle/index.html b/docs/v3/v2/concepts/custom-bundle/index.html deleted file mode 100644 index 375bc30c8..000000000 --- a/docs/v3/v2/concepts/custom-bundle/index.html +++ /dev/null @@ -1,2279 +0,0 @@ - - - - - - - - - - - - - - - - - - Custom Bundle - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Custom Bundling

    -

    With the introduction of selective imports it is now possible to create your own bundle to exactly fit your needs. This provides much greater control over how your solutions are deployed and what is included in your bundles.

    -

    Scenarios could include:

    -
      -
    • Deploying a company-wide PnPjs custom bundle shared by all your components so it only needs to be downloaded once.
    • -
    • Creating SPFx libraries either for one project or a single webpart.
    • -
    • Create a single library containing the PnPjs code you need bundled along with your custom extensions.
    • -
    -

    Create a custom bundle

    -

    Webpack

    -

    You can see/clone a sample project of this example here.

    -

    Rollup

    -

    You can see/clone a sample project of this example here.

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/concepts/error-handling/index.html b/docs/v3/v2/concepts/error-handling/index.html deleted file mode 100644 index ad8111303..000000000 --- a/docs/v3/v2/concepts/error-handling/index.html +++ /dev/null @@ -1,2539 +0,0 @@ - - - - - - - - - - - - - - - - - - Error Handling - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Error Handling

    -

    This article describes the most common types of errors generated by the library. It provides context on the error object, and ways to handle the errors. As always you should tailor your error handling to what your application needs. These are ideas that can be applied to many different patterns.

    -
    -

    For 429, 503, and 504 errors we include retry logic within the library

    -
    -

    The HttpRequestError

    -

    All errors resulting from executed web requests will be returned as an HttpRequestError object which extends the base Error. In addition to the standard Error properties it has some other properties to help you figure out what went wrong. We used a custom error to attempt to normalize what can be a wide assortment of http related errors, while also seeking to provide as much information to library consumers as possible.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Property NameDescription
    nameStandard Error.name property. Always 'Error'
    messageNormalized string containing the status, status text, and the full response text
    stackThe callstack producing the error
    isHttpRequestErrorAlways true, allows you to reliably determine if you have an HttpRequestError instance
    responseUnread copy of the Response object resulting in the thrown error
    statusThe Response.status value (such as 404)
    statusTextThe Response.statusText value (such as 'Not Found')
    -

    Basic Handling

    -

    For all operations involving a web request you should account for the possibility they might fail. That failure might be transient or permanent - you won't know until they happen 😉. The most basic type of error handling involves a simple try-catch.

    -
    import { sp } from "@pnp/sp/presets/all";
    -
    -try {
    -
    -  // get a list that doesn't exist
    -  const w = await sp.web.lists.getByTitle("no")();
    -
    -} catch (e) {
    -
    -  console.error(e);
    -}
    -
    -

    This will produce output like:

    -
    Error making HttpClient request in queryable [404] Not Found ::> {"odata.error":{"code":"-1, System.ArgumentException","message":{"lang":"en-US","value":"List 'no' does not exist at site with URL 'https://tenant.sharepoint.com/sites/dev'."}}} Data: {"response":{"size":0,"timeout":0},"status":404,"statusText":"Not Found","isHttpRequestError":true}
    -
    -

    This is very descriptive and provides full details as to what happened, but you might want to handle things a little more cleanly.

    -

    Reading the Response

    -

    In some cases the response body will have additional details such as a localized error messages which can be nicer to display rather than our normalized string. You can read the response directly and process it however you desire:

    -
    import { sp } from "@pnp/sp/presets/all";
    -import { HttpRequestError } from "@pnp/queryable";
    -
    -try {
    -
    -  // get a list that doesn't exist
    -  const w = await sp.web.lists.getByTitle("no")();
    -
    -} catch (e) {
    -
    -  // are we dealing with an HttpRequestError?
    -  if (e?.isHttpRequestError) {
    -
    -    // we can read the json from the response
    -    const json = await (<HttpRequestError>e).response.json();
    -
    -    // if we have a value property we can show it
    -    console.log(typeof json["odata.error"] === "object" ? json["odata.error"].message.value : e.message);
    -
    -    // add of course you have access to the other properties and can make choices on how to act
    -    if ((<HttpRequestError>e).status === 404) {
    -       console.error((<HttpRequestError>e).statusText);
    -      // maybe create the resource, or redirect, or fallback to a secondary data source
    -      // just ideas, handle any of the status codes uniquely as needed
    -    }
    -
    -  } else {
    -    // not an HttpRequestError so we just log message
    -    console.log(e.message);
    -  }
    -}
    -
    -

    Logging errors

    -

    Using the PnPjs Logging Framework you can directly pass the error object and the normalized message will be logged. These techniques can be applied to any logging framework.

    -
    import { Logger } from "@pnp/logging";
    -import { sp } from "@pnp/sp/presets/all";
    -
    -try {
    -  // get a list that doesn't exist
    -  const w = await sp.web.lists.getByTitle("no")();  
    -} catch (e) {
    -
    -  Logger.error(e);
    -}
    -
    -

    You may want to read the response and customize the message as described above:

    -
    import { Logger } from "@pnp/logging";
    -import { sp } from "@pnp/sp/presets/all";
    -import { HttpRequestError } from "@pnp/queryable";
    -
    -try {
    -  // get a list that doesn't exist
    -  const w = await sp.web.lists.getByTitle("no")();  
    -} catch (e) {
    -
    -  if (e?.isHttpRequestError) {
    -
    -    // we can read the json from the response
    -    const data = await (<HttpRequestError>e).response.json();
    -
    -    // parse this however you want
    -    const message = typeof data["odata.error"] === "object" ? data["odata.error"].message.value : e.message;
    -
    -    // we use the status to determine a custom logging level
    -    const level: LogLevel = (<HttpRequestError>e).status === 404 ? LogLevel.Warning : LogLevel.Info;
    -
    -    // create a custom log entry
    -    Logger.log({
    -      data,
    -      level,
    -      message,
    -    });
    -
    -  } else {
    -    // not an HttpRequestError so we just log message
    -    Logger.error(e);
    -  }
    -}
    -
    -

    Putting it All Together

    -

    After reviewing the above section you might have thought it seems like a lot of work to include all that logic for every error. One approach is to establish a single function you use application wide to process errors. This allows all the error handling logic to be easily updated and consistent across the application.

    -

    errorhandler.ts

    -
    import { Logger } from "@pnp/logging";
    -import { HttpRequestError } from "@pnp/queryable";
    -import { hOP } from "@pnp/core";
    -
    -export async function handleError(e: Error | HttpRequestError): Promise<void> {
    -
    -  if (hOP(e, "isHttpRequestError")) {
    -
    -    // we can read the json from the response
    -    const data = await (<HttpRequestError>e).response.json();
    -
    -    // parse this however you want
    -    const message = typeof data["odata.error"] === "object" ? data["odata.error"].message.value : e.message;
    -
    -    // we use the status to determine a custom logging level
    -    const level: LogLevel = (<HttpRequestError>e).status === 404 ? LogLevel.Warning : LogLevel.Info;
    -
    -    // create a custom log entry
    -    Logger.log({
    -      data,
    -      level,
    -      message,
    -    });
    -
    -  } else {
    -    // not an HttpRequestError so we just log message
    -    Logger.error(e);
    -  }
    -}
    -
    -

    web-request.ts

    -
    import { sp } from "@pnp/sp/presets/all";
    -import { handleError } from "./errorhandler";
    -
    -try {
    -
    -  const w = await sp.web.lists.getByTitle("no")();
    -
    -} catch (e) {
    -
    -  await handleError(e);
    -}
    -
    -

    web-request2.ts

    -
    import { sp } from "@pnp/sp/presets/all";
    -import { handleError } from "./errorhandler";
    -
    -try {
    -
    -  const w = await sp.web.lists();
    -
    -} catch (e) {
    -
    -  await handleError(e);
    -}
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/concepts/ie11-mode/index.html b/docs/v3/v2/concepts/ie11-mode/index.html deleted file mode 100644 index a8d02830a..000000000 --- a/docs/v3/v2/concepts/ie11-mode/index.html +++ /dev/null @@ -1,2278 +0,0 @@ - - - - - - - - - - - - - - - - - - IE11 Mode - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    IE11 Mode

    -

    Starting with v2 we have made the decision to no longer support IE11. Because we know this affects folks we have introduced IE11 compatibility mode. Configuring the library will allow it to work within IE11, however at a possibly reduced level of functionality depending on your use case. Please see the list below of known limitations.

    -

    Limitations

    -

    When required to use IE11 mode there is certain functionality which may not work correctly or at all.

    - -

    Configure IE11 Mode

    -

    To enable IE11 Mode set the ie11 flag to true in the setup object. Optionally, supply the context object when working in SharePoint Framework.

    -
    import { sp } from "@pnp/sp";
    -
    -sp.setup({
    -  // set ie 11 mode
    -  ie11: true,
    -  // only needed when working within SharePoint Framework
    -  spfxContext: this.context
    -});
    -
    -
    -

    If you are supporting IE 11, please see the article on required polyfills.

    -
    -

    A note on ie11 mode and support

    -

    Because IE11 is no longer a primary supported browser our policy moving forward will be doing our best not to break anything in ie11 mode, but not all features will work and new features may never come to ie11 mode. Also, if you find an ie11 bug we expect you to work with us on helping to fix it. If you aren't willing to invest some time to support an old browser it seems we shouldn't either.

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/concepts/invokable/index.html b/docs/v3/v2/concepts/invokable/index.html deleted file mode 100644 index 7ca6d66a1..000000000 --- a/docs/v3/v2/concepts/invokable/index.html +++ /dev/null @@ -1,2254 +0,0 @@ - - - - - - - - - - - - - - - - - - Invokables - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Invokables

    -

    For people who have been using the library since the early days you are familiar with the need to use the () method to invoke a method chain:

    -
    // an example of get
    -const lists = await sp.web.lists();
    -
    -

    Starting with v2 this is no longer required, you can invoke the object directly to execute the default action for that class - typically a get.

    -
    const lists = await sp.web.lists();
    -
    -

    This has two main benefits for people using the library: you can write less code, and we now have a way to model default actions for objects that might do something other than a get. The way we designed the library prior to v2 hid the post, put, delete operations as protected methods attached to the Queryable classes. Without diving into why we did this, having a rethink seemed appropriate for v2. Based on that, the entire queryable chain is now invokable as well for any of the operations.

    -

    Other Operations (post, put, delete)

    -
    import { sp, spPost } from "@pnp/sp";
    -import "@pnp/sp/webs";
    -
    -// do a post to a web - just an example doesn't do anything fancy
    -spPost(sp.web);
    -
    -

    Things get a little more interesting in that you can now do posts (or any of the operations) to any of the urls defined by a fluent chain. Meaning you can easily implement methods that are not yet part of the library. For this example I have made up a method called "MagicFieldCreationMethod" that doesn't exist. Imagine it was just added to the SharePoint API and we do not yet have support for it. You can now write code like so:

    -
    import { sp, spPost, SharePointQueryable } from "@pnp/sp";
    -import "@pnp/sp/webs";
    -import "@pnp/sp/fields/web";
    -
    -// call our made up example method
    -spPost(SharePointQueryable(sp.web.fields, "MagicFieldCreationMethod"), {
    -    body: JSON.stringify({
    -        // ... this would be the post body
    -    }),
    -});
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/concepts/polyfill/index.html b/docs/v3/v2/concepts/polyfill/index.html deleted file mode 100644 index 5a7977162..000000000 --- a/docs/v3/v2/concepts/polyfill/index.html +++ /dev/null @@ -1,2401 +0,0 @@ - - - - - - - - - - - - - - - - - - Polyfills - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Polyfills

    -

    These libraries may make use of some features not found in older browsers. This primarily affects Internet Explorer 11, which requires that we provide this missing functionality.

    -
    -

    If you are supporting IE11 enable IE11 mode.

    -
    -

    IE 11 Polyfill package

    -

    We created a package you try and help provide this missing functionality. This package is independent of the other @pnp/* packages and does not need to be updated monthly unless we introduce additional polyfills and publish a new version. This package is only needed if you are required to support IE 11.

    -

    Install

    -

    npm install @pnp/polyfill-ie11 --save

    -

    Use

    -
    import "@pnp/polyfill-ie11";
    -import { sp } from "@pnp/sp/presets/all";
    -
    -sp.web.lists.getByTitle("BigList").items.filter(`ID gt 6000`)().then(r => {
    -  this.domElement.innerHTML += r.map(l => `${l.Title}<br />`);
    -});
    -
    -

    Selective Use

    -

    Starting with version 2.0.2 you can selectively include the polyfills from the package. Depending on your needs it may make sense in your application to use the underlying libraries directly. We have added an expanded statement on our polyfills.

    -
    // individually include polyfills as needed to match your requirements
    -import "@pnp/polyfill-ie11/dist/fetch";
    -import "@pnp/polyfill-ie11/dist/fill";
    -import "@pnp/polyfill-ie11/dist/from";
    -import "@pnp/polyfill-ie11/dist/iterator";
    -import "@pnp/polyfill-ie11/dist/map";
    -import "@pnp/polyfill-ie11/dist/promise";
    -import "@pnp/polyfill-ie11/dist/reflect";
    -import "@pnp/polyfill-ie11/dist/symbol";
    -
    -
    -// works in IE11 and other browsers
    -sp.web.lists.getByTitle("BigList").items.filter(`ID gt 6000`)().then(r => {
    -  this.domElement.innerHTML += r.map(l => `${l.Title}<br />`);
    -});
    -
    -

    SearchQueryBuilder

    -

    Because the latest version of SearchQueryBuilder uses Proxy internally you can fall back on the older version as shown below.

    -
    import "@pnp/polyfill-ie11";
    -import { SearchQueryBuilder } from "@pnp/polyfill-ie11/dist/searchquerybuilder";
    -import { sp, ISearchQueryBuilder } from "@pnp/sp/presets/all";
    -
    -// works in IE11 and other browsers
    -const builder: ISearchQueryBuilder = SearchQueryBuilder().text("test");
    -
    -sp.search(builder).then(r => {
    -  this.domElement.innerHTML = JSON.stringify(r);
    -});
    -
    -

    General Statement on Polyfills

    -

    Internet Explorer 11 (IE11) has been an enterprise standard browser for many years. Given the complexity in changing technical platforms in many organizations, it is no surprise standardization on this out-of-date browser continues. Unfortunately, for those organizations, the Internet has moved on and many - if not all - SaaS platforms are embracing modern standards and no longer supporting the legacy IE11 browser. Even Microsoft states in their official documentation that Microsoft 365 is best experienced with a modern browser. They have even gone so far to build the latest version of Microsoft Edge based on Chromium (Edge Chromium), with an "Internet Explorer mode" allowing organizations to load legacy sites which require IE automatically.

    -

    PnPjs is now "modern" as well, and by that we mean we have moved to using capabilities of current browsers and JavaScript which are not present in IE11. We understand as a developer your ability to require an organization to switch browsers is unrealistic. We want to do everything we can to support you, but it is up to you to ensure your application is properly supported in IE11.

    -

    There are many polyfills available, depending on the platform you're running on, the frameworks you are using, and the libraries you consume. Although the majority of PnPjs users build for SharePoint Online, a significant number build for earlier versions of the platform as well as for their own node-based solutions or websites. Unfortunately, there is no way our polyfill library can support all these scenarios.

    -

    What we intended with the @pnp/polyfill-ie11 package was to provide a comprehensive group of all the polyfills that would be needed based on the complete PnPjs library. We are finding when we aggregate our polyfills with the polyfills provided in the SharePoint page and from other sources, things don't always work well. We cannot solve this for your specific situations except by providing you transparency into the polyfills which we know are necessary for our packages. You may need to adjust what polyfills your application uses based on the other libraries you are using.

    -

    To that end, we want to provide the list of polyfills we recommend here - along with the associated packages – with the goal of helping you to work out what combination of polyfills might work with your code. Also, if you haven't reviewed it yet, please check out the information on IE11 Mode for how to configure IE11 mode in the sp.setup as well as what limitations doing so will have on your usage of PnPjs.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    imports
    import "core-js/stable/array/from";
    import "core-js/stable/array/fill";
    import "core-js/stable/array/iterator";
    import "core-js/stable/promise";
    import "core-js/stable/reflect";
    import "es6-map/implement";
    import "core-js/stable/symbol";
    import "whatwg-fetch";
    -

    The following NPM packages are what we use to do the above indicated imports -|package| -|---| -|core-js| -|es6-map| -|whatwg-fetch|

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/concepts/selective-imports/index.html b/docs/v3/v2/concepts/selective-imports/index.html deleted file mode 100644 index 3de62dbe5..000000000 --- a/docs/v3/v2/concepts/selective-imports/index.html +++ /dev/null @@ -1,2360 +0,0 @@ - - - - - - - - - - - - - - - - - - Selective Imports - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Selective Imports

    -

    As the libraries have grown to support more of the SharePoint and Graph API they have also grown in size. On one hand this is good as more functionality becomes available but you had to include lots of code you didn't use if you were only doing simple operations. To solve this we introduced selective imports in v2. This allows you to only import the parts of the sp or graph library you need, allowing you to greatly reduce your overall solution bundle size - and enables treeshaking.

    -

    This concept works well with custom bundling to create a shared package tailored exactly to your needs.

    -

    If you would prefer to not worry about selective imports please see the section on presets.

    -

    Old way

    -
    // the sp var came with all library functionality already attached
    -// meaning treeshaking couldn't reduce the size
    -import { sp } from "@pnp/sp";
    -
    -const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)();
    -
    -

    New Way

    -
    // the sp var now has almost nothing attached at import time and relies on
    -import { sp } from "@pnp/sp";
    -// we need to import each of the pieces we need to "attach" them for chaining
    -// here we are importing the specific sub modules we need and attaching the functionality for lists to web and items to list
    -import "@pnp/sp/webs";
    -import "@pnp/sp/lists/web";
    -import "@pnp/sp/items/list";
    -
    -const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)();
    -
    -

    Above we are being very specific in what we are importing, but you can also import entire sub-modules and be slightly less specific

    -
    // the sp var now has almost nothing attached at import time and relies on
    -import { sp } from "@pnp/sp";
    -// we need to import each of the pieces we need to "attach" them for chaining
    -// here we are importing the specific sub modules we need and attaching the functionality for lists to web and items to list
    -import "@pnp/sp/webs";
    -import "@pnp/sp/lists";
    -import "@pnp/sp/items";
    -
    -const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)();
    -
    -

    The above two examples both work just fine but you may end up with slightly smaller bundle sizes using the first. Consider this example:

    -
    // this import statement will attach content-type functionality to list, web, and item
    -import "@pnp/sp/content-types";
    -
    -// this import statement will only attach content-type functionality to web
    -import "@pnp/sp/content-types/web";
    -
    -

    If you only need to access content types on the web object you can reduce size by only importing that piece.

    -
    // this will fail
    -import { sp } from "@pnp/sp";
    -import "@pnp/sp/webs";
    -import { IList } from "@pnp/sp/lists";
    -
    -// do this instead
    -import { sp } from "@pnp/sp";
    -import "@pnp/sp/webs";
    -import "@pnp/sp/lists";
    -import { IList } from "@pnp/sp/lists";
    -
    -const lists = await sp.web.lists();
    -
    -

    Presets

    -

    Sometimes you don't care as much about bundle size - testing or node development for example. In these cases we have provided what we are calling presets to allow you to skip importing each module individually.

    -

    SP

    -

    For the sp library there are two presets "all" and "core". The all preset mimics the behavior in v1 and includes everything in the library already attached to the sp var.

    -
    import { sp } from "@pnp/sp/presets/all";
    -
    -// sp.* exists as it did in v1, tree shaking will not work
    -const lists = await sp.web.lists();
    -
    -

    The "core" preset includes sites, webs, lists, and items.

    -
    import { sp } from "@pnp/sp/presets/core";
    -
    -// sp.* exists as it did in v1, tree shaking will not work
    -const lists = await sp.web.lists();
    -
    -

    Graph

    -

    The graph library contains a single preset, "all" mimicking the v1 structure.

    -
    import { graph } from "@pnp/graph/presets/all";
    -
    -// graph.* exists as it did in v1, tree shaking will not work
    -
    -
    -

    While we may look to add additional presets in the future you are encouraged to look at making your own custom bundles as a preferred solution.

    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/concepts/settings/index.html b/docs/v3/v2/concepts/settings/index.html deleted file mode 100644 index bc98100c1..000000000 --- a/docs/v3/v2/concepts/settings/index.html +++ /dev/null @@ -1,2551 +0,0 @@ - - - - - - - - - - - - - - - - - - Settings - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Project Settings

    -

    This article discusses creating a project settings file for use in local development and debugging of the libraries. The settings file contains authentication and other settings to enable you to run and debug the project locally.

    -

    The settings file is a JavaScript file that exports a single object representing the settings of your project. You can view the example settings file in the project root.

    -

    Settings File Format (>= 2.0.13)

    -

    Starting with version 2.0.13 we have added support within the settings file for MSAL authentication for both SharePoint and Graph. You are NOT required to update your existing settings file unless you want to use MSAL authentication with a Graph application. The existing id/secret settings continue to work however we recommend updating when you have an opportunity. For more information coinfiguring MSAL please review the section in the authentication section for node.

    -

    MSAL configuration has two parts, these are the initialization which is passed directly to the MsalFetchClient (and on to the underlying msal-node instance) and the scopes. The scopes are always "https://{tenant}.sharepoint.com/.default" or "https://graph.microsoft.com/.default" depending on what you are calling.

    -
    -

    If you are calling Microsoft Graph sovereign or gov clouds the scope may need to be updated.

    -
    -
    const privateKey = `-----BEGIN RSA PRIVATE KEY-----
    -your private key, read from a file or included here
    ------END RSA PRIVATE KEY-----
    -`;
    -
    -var msalInit = {
    -    auth: {
    -        authority: "https://login.microsoftonline.com/{tenant id}",
    -        clientCertificate: {
    -            thumbprint: "{certificate thumbnail}",
    -            privateKey: privateKey,
    -        },
    -        clientId: "{AAD App registration id}",
    -    }
    -}
    -
    -var settings = {
    -    testing: {
    -        enableWebTests: true,
    -        testUser: "i:0#.f|membership|user@consto.com",
    -        sp: {
    -            url: "{required for MSAL - absolute url of test site}",
    -            notificationUrl: "{ optional: notification url }",
    -            msal: {
    -                init: msalInit,
    -                scopes: ["https://{tenant}.sharepoint.com/.default"]
    -            },
    -        },
    -        graph: {
    -            msal: {
    -                init: msalInit,
    -                scopes: ["https://graph.microsoft.com/.default"]
    -            },
    -        },
    -    },
    -}
    -
    -module.exports = settings;
    -
    -

    The settings object has a single sub-object testing which contains the configuration used for debugging and testing PnPjs. The parts of this object are described in detail below.

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    enableWebTestsFlag to toggle if tests are run against the live services or not. If this is set to false none of the other sections are required.
    testUserAAD login account to be used when running tests.
    spSettings used to configure SharePoint (sp library) debugging and tests
    graphSettings used to configure Microsoft Graph (graph library) debugging and tests
    -

    SP values

    - - - - - - - - - - - - - - - - - - - - - -
    namedescription
    urlThe url of the site to use for all requests. If a site parameter is not specified a child web will be created under the web at this url. See scripts article for more details.
    notificationUrlUrl used when registering test subscriptions
    msalInformation about MSAL authentication setup
    -

    Graph value

    -

    The graph values are described in the table below and come from registering an AAD Application. The permissions required by the registered application are dictated by the tests you want to run or resources you wish to test against.

    - - - - - - - - - - - - - -
    namedescription
    msalInformation about MSAL authentication setup
    -

    Settings File Format (<= 2.0.12)

    -
    var settings = {
    -
    -    testing: {
    -        enableWebTests: true,
    -        sp: {
    -            id: "{ client id }",
    -            secret: "{ client secret }",
    -            url: "{ site collection url }",
    -            notificationUrl: "{ optional: notification url }",
    -        },
    -        graph: {
    -            tenant: "{tenant.onmicrosoft.com}",
    -            id: "{your app id}",
    -            secret: "{your secret}"
    -        },
    -    }
    -}
    -
    -module.exports = settings;
    -
    - - - - - - - - - - - - - - - - - - - - - -
    enableWebTestsFlag to toggle if tests are run against the live services or not. If this is set to false none of the other sections are required.
    spSettings used to configure SharePoint (sp library) debugging and tests
    graphSettings used to configure Microsoft Graph (graph library) debugging and tests
    -

    SP values

    -

    The sp values are described in the table below and come from registering a legacy SharePoint add-in.

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    namedescription
    idThe client id of the registered application
    secretThe client secret of the registered application
    urlThe url of the site to use for all requests. If a site parameter is not specified a child web will be created under the web at this url. See scripts article for more details.
    notificationUrlUrl used when registering test subscriptions
    -

    Graph values

    -

    The graph values are described in the table below and come from registering an AAD Application. The permissions required by the registered application are dictated by the tests you want to run or resources you wish to test against.

    - - - - - - - - - - - - - - - - - - - - - -
    namedescription
    tenantTenant to target for authentication and data (ex: contoso.onmicrosoft.com)
    idThe application id
    secretThe application secret
    -

    Create Settings.js file

    -
      -
    1. Copy the example file and rename it settings.js. Place the file in the root of your project.
    2. -
    3. Update the settings as needed for your environment.
    4. -
    -
    -

    If you are only doing SharePoint testing you can leave the graph section off and vice-versa. Also, if you are not testing anything with hooks you can leave off the notificationUrl.

    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/config-store/configuration/index.html b/docs/v3/v2/config-store/configuration/index.html deleted file mode 100644 index 3e29e0cf8..000000000 --- a/docs/v3/v2/config-store/configuration/index.html +++ /dev/null @@ -1,2226 +0,0 @@ - - - - - - - - - - - - - - - - - - configuration - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/config-store/configuration

    -

    The main class exported from the config-store package is Settings. This is the class through which you will load and access your -settings via providers.

    -
    import { Web } from "@pnp/sp/presets/all";
    -import { Settings, SPListConfigurationProvider } from "@pnp/config-store";
    -
    -// create an instance of the settings class, could be static and shared across your application
    -// or built as needed.
    -const settings = new Settings();
    -
    -// you can add/update a single value using add
    -settings.add("mykey", "myvalue");
    -
    -// you can also add/update a JSON value which will be stringified for you as a shorthand
    -settings.addJSON("mykey2", {
    -    field: 1,
    -    field2: 2,
    -    field3: 3,
    -});
    -
    -// and you can apply a plain object of keys/values that will be written as single values
    -// this results in each enumerable property of the supplied object being added to the settings collection
    -settings.apply({
    -    field: 1,
    -    field2: 2,
    -    field3: 3,
    -});
    -
    -// and finally you can load values from a configuration provider
    -const w = Web("https://mytenant.sharepoint.com/sites/dev");
    -const provider = new SPListConfigurationProvider(w, "myconfiglistname");
    -
    -// this will load values from the supplied list
    -// by default the key will be from the Title field and the value from a column named Value
    -await settings.load(provider);
    -
    -// once we have loaded values we can then read them
    -const value = settings.get("mykey");
    -
    -// or read JSON that will be parsed for you from the store
    -const value2 = settings.getJSON("mykey2");
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/config-store/index.html b/docs/v3/v2/config-store/index.html deleted file mode 100644 index 9d276e595..000000000 --- a/docs/v3/v2/config-store/index.html +++ /dev/null @@ -1,2239 +0,0 @@ - - - - - - - - - - - - - - - - - - config-store - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/config-store

    -

    npm version

    -

    This module provides a way to load application configuration from one or more providers and share it across an application in a consistent way. A provider can be anything - but we have included one to load information from a SharePoint list. This library is most helpful for larger applications where a formal configuration model is needed.

    -

    Getting Started

    -

    Install the library and required dependencies

    -

    npm install @pnp/sp @pnp/config-store --save

    -

    See the topics below for usage:

    - - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/config-store/providers/index.html b/docs/v3/v2/config-store/providers/index.html deleted file mode 100644 index 667143119..000000000 --- a/docs/v3/v2/config-store/providers/index.html +++ /dev/null @@ -1,2275 +0,0 @@ - - - - - - - - - - - - - - - - - - providers - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/config-store/providers

    -

    Currently there is a single provider included in the library, but contributions of additional providers are welcome.

    -

    SPListConfigurationProvider

    -

    This provider is based on a SharePoint list it reads all of the rows and makes them available as a TypedHash<string>. By default the column names used are Title for key and "Value" for value, but you can update these as needed. Additionally, the settings class supports the idea of last value in wins - so you can easily load multiple configurations. This helps to support a common scenario in the enterprise where you might have one main list for global configuration but some settings can be set at the web level. In this case you would first load the global, then the local settings and any local values will take precedence.

    -
    import { Web } from "@pnp/sp/presets/all";
    -import { Settings, SPListConfigurationProvider } from "@pnp/config-store";
    -
    -// create a new provider instance
    -const w = Web("https://mytenant.sharepoint.com/sites/dev");
    -const provider = new SPListConfigurationProvider(w, "myconfiglistname");
    -
    -const settings = new Settings();
    -
    -// load our values from the list
    -await settings.load(provider);
    -
    -

    CachingConfigurationProvider

    -

    Because making requests on each page load is very inefficient you can optionally use the caching configuration provider, which wraps a -provider and caches the configuration in local or session storage.

    -
    import { Web } from "@pnp/sp/presets/all";
    -import { Settings, SPListConfigurationProvider } from "@pnp/config-store";
    -
    -// create a new provider instance
    -const w = Web("https://mytenant.sharepoint.com/sites/dev");
    -const provider = new SPListConfigurationProvider(w, "myconfiglistname");
    -
    -// get an instance of the provider wrapped
    -// you can optionally provide a key that will be used in the cache to the asCaching method
    -const wrappedProvider = provider.asCaching();
    -
    -// use that wrapped provider to populate the settings
    -await settings.load(wrappedProvider);
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/contributing/debug-tests/index.html b/docs/v3/v2/contributing/debug-tests/index.html deleted file mode 100644 index 27212451c..000000000 --- a/docs/v3/v2/contributing/debug-tests/index.html +++ /dev/null @@ -1,2323 +0,0 @@ - - - - - - - - - - - - - - - - - - Writing Tests - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Writing Tests

    -

    With version 2 we have made a significant effort to improve out test coverage. To keep that up, all changes submitted will require one or more tests be included. For new functionality at least a basic test that the method executes is required. For bug fixes please include a test that would have caught the bug (i.e. fail before your fix) and passes with your fix in place.

    -

    How to write Tests

    -

    We use Mocha and Chai for our testing framework. You can see many examples of writing tests within the ./test folder. Here is a sample with extra comments to help explain what's happening, taken from ./test/sp/items.ts:

    -
    import { getRandomString } from "@pnp/core";
    -import { testSettings } from "../main";
    -import { expect } from "chai";
    -import { sp } from "@pnp/sp";
    -import "@pnp/sp/lists/web";
    -import "@pnp/sp/items/list";
    -import { IList } from "@pnp/sp/lists";
    -
    -describe("Items", () => {
    -
    -    // any tests that make a web request should be withing a block checking if web tests are enabled
    -    if (testSettings.enableWebTests) {
    -
    -        // a block scoped var we will use across our tests
    -        let list: IList = null;
    -
    -        // we use the before block to setup
    -        // executed before all the tests in this block, see the mocha docs for more details
    -        // mocha prefers using function vs arrow functions and this is recommended
    -        before(async function () {
    -
    -            // execute a request to ensure we have a list
    -            const ler = await sp.web.lists.ensure("ItemTestList", "Used to test item operations");
    -            list = ler.list;
    -
    -            // in this case we want to have some items in the list for testing so we add those
    -            // only if the list was just created
    -            if (ler.created) {
    -
    -                // add a few items to get started
    -                const batch = sp.web.createBatch();
    -                list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` });
    -                list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` });
    -                list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` });
    -                list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` });
    -                list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` });
    -                await batch.execute();
    -            }
    -        });
    -
    -        // this test has a label "get items" and is run via an async function
    -        it("get items", async function () {
    -
    -            // make a request for the list's items
    -            const items = await list.items();
    -
    -            // report that we expect that result to be an array with more than 0 items
    -            expect(items.length).to.be.gt(0);
    -        });
    -
    -        // ... remainder of code removed
    -    }
    -}
    -
    -

    General Guidelines for Writing Tests

    -
      -
    • Tests should operate within the site defined in testSettings
    • -
    • Tests should be able to run multiple times on the same site, but do not need to cleanup after themselves
    • -
    • Each test should be self contained and not depend on other tests, they can depend on work done in before or beforeAll
    • -
    • When writing tests you can use "only" and "skip" from mochajs to focus on only the tests you are writing
    • -
    • Be sure to review the various options when running your tests
    • -
    • If you are writing a test and the endpoint doesn't support app only permissions, you can skip writing a test - but please note that in the PR description
    • -
    -

    Next Steps

    -

    Now that you've written tests to cover your changes you'll need to update the docs.

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/contributing/debugging/index.html b/docs/v3/v2/contributing/debugging/index.html deleted file mode 100644 index fce32ec56..000000000 --- a/docs/v3/v2/contributing/debugging/index.html +++ /dev/null @@ -1,2514 +0,0 @@ - - - - - - - - - - - - - - - - - - Debugging - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - - - -

    Debugging

    -

    Using the steps in this article you will be able to locally debug the library internals as well as new features you are working on.

    -

    Before proceeding be sure you have reviewed how to setup for local configuration and debugging.

    -

    Debugging Library Features

    -

    The easiest way to debug the library when working on new features is using F5 in Visual Studio Code. This uses launch.json to build and run the library using ./debug/launch/main.ts as the entry point.

    -

    Basic SharePoint Testing

    -

    You can start the base debugging case by hitting F5. Before you do place a break point in ./debug/launch/sp.ts. You can also place a break point within any of the libraries or modules. Feel free to edit the sp.ts file to try things out, debug suspected issues, or test new features, etc - but please don't commit any changes as this is a shared file. See the section on creating your own debug modules.

    -

    All of the setup for the node client is handled within sp.ts using the settings from the local configuration.

    -

    Basic Graph Testing

    -

    Testing and debugging Graph calls follows the same process as outlined for SharePoint, however you need to update main.ts to import graph instead of sp. You can place break points anywhere within the library code and they should be hit.

    -

    All of the setup for the node client is handled within graph.ts using the settings from the local configuration.

    -

    How to: Create a Debug Module

    -

    If you are working on multiple features or want to save sample code for various tasks you can create your own debugging modules and leave them in the debug/launch folder locally. The gitignore file is setup to ignore any files that aren't already in git.

    -

    Using ./debug/launch/sp.ts as a reference create a file in the debug/launch folder, let's call it mydebug.ts and add this content:

    -
    // note we can use the actual package names for our imports (ex: @pnp/logging)
    -import { Logger, LogLevel, ConsoleListener } from "@pnp/logging";
    -// using the all preset for simplicity in the example, selective imports work as expected
    -import { sp, ListEnsureResult } from "@pnp/sp/presets/all";
    -
    -declare var process: { exit(code?: number): void };
    -
    -export async function MyDebug() {
    -
    -  // configure your options
    -  // you can have different configs in different modules as needed for your testing/dev work
    -  sp.setup({
    -    sp: {
    -      fetchClientFactory: () => {
    -        return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret);
    -      },
    -    },
    -  });
    -
    -  // run some debugging
    -  const list = await sp.web.lists.ensure("MyFirstList");
    -
    -  Logger.log({
    -    data: list.created,
    -    level: LogLevel.Info,
    -    message: "Was list created?",
    -  });
    -
    -  if (list.created) {
    -
    -    Logger.log({
    -      data: list.data,
    -      level: LogLevel.Info,
    -      message: "Raw data from list creation.",
    -    });
    -
    -  } else {
    -
    -    Logger.log({
    -      data: null,
    -      level: LogLevel.Info,
    -      message: "List already existed!",
    -    });
    -  }
    -
    -  process.exit(0);
    -}
    -
    -

    Update main.ts to launch your module

    -

    First comment out the import for the default example and then add the import and function call for yours, the updated launch/main.ts should look like this:

    -
    // ...
    -
    -// comment out the example
    -// import { Example } from "./example";
    -// Example();
    -
    -import { MyDebug } from "./mydebug"
    -MyDebug();
    -
    -// ...
    -
    -
    -

    Remember, please don't commit any changes to the shared files within the debug folder. (Unless you've found a bug that needs fixing in the original file)

    -
    -

    Debug

    -

    Place a break point within the mydebug.ts file and hit F5. Your module should run and your break point hit. You can then examine the contents of the objects and see the run time state. Remember, you can also set breakpoints within the package src folders to see exactly how things are working during your debugging scenarios.

    -

    Debug Module Next Steps

    -

    Using this pattern you can create and preserve multiple debugging scenarios in separate modules locally - they won't be added to git. You just have to update main.ts to point to the one you want to run.

    -

    In Browser Debugging

    -

    You can also serve files locally to debug as a user in the browser by serving code using ./debug/serve/main.ts as the entry. The file is served as https://localhost:8080/assets/pnp.js, allowing you to create a single page in your tenant for in browser testing. The remainder of this section describes the process to setup a SharePoint page to debug in this manner.

    -

    Start the local serve

    -

    This will serve a package with ./debug/serve/main.ts as the entry.

    -

    gulp serve

    -

    Add reference to library

    -

    Within a SharePoint page add a script editor web part and then paste in the following code. The div is to give you a place to target with visual updates should you desire.

    -
    <script src="https://localhost:8080/assets/pnp.js"></script>
    -<div id="pnp-test"></div>
    -
    -

    You should see an alert with the current web's title using the default main.ts. Feel free to update main.ts to do whatever you would like, but remember not to commit changes to the shared files.

    -

    Debug

    -

    Refresh the page and open the developer tools in your browser of choice. If the pnp.js file is blocked due to security restrictions you will need to allow it.

    -

    Next Steps

    -

    You can make changes to the library and immediately see them reflected in the browser. All files are watched so changes will be available as soon as webpack reloads the package. This allows you to rapidly test the library in the browser.

    -

    Now you can learn about extending the library.

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/contributing/documentation/index.html b/docs/v3/v2/contributing/documentation/index.html deleted file mode 100644 index fd2a5cf8c..000000000 --- a/docs/v3/v2/contributing/documentation/index.html +++ /dev/null @@ -1,2291 +0,0 @@ - - - - - - - - - - - - - - - - - - Update Documentation - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Documentation

    -

    Just like with tests we have invested much time in updating the documentation and when you make a change to the library you should update the associated documentation as part of the pull request.

    -

    Writing Docs

    -

    Our docs are all written in markdown and processed using MkDocs. You can use code blocks, tables, and other markdown formatting. You can review the other articles for examples on writing docs. Generally articles should focus on how to use the library and where appropriate link to official outside documents as needed. Official documentation could be Microsoft, other library project docs such as MkDocs, or other sources.

    -

    Building Docs Locally

    -

    Building the documentation locally can help you visualize change you are making to the docs. What you see locally will be what you see online. Documentation is built using MkDocs. You will need to latest version of Python (tested on version 3.7.1) and pip. If you're on the Windows operating system, make sure you have added Python to your Path environment variable.

    -

    When executing the pip module on Windows you can prefix it with python -m. -For example:

    -

    python -m pip install mkdocs-material

    -
      -
    • Install MkDocs
        -
      • pip install mkdocs
      • -
      -
    • -
    • Install the Material theme
        -
      • pip install mkdocs-material
      • -
      -
    • -
    • install the mkdocs-markdownextradata-plugin - this is used for the version variable
        -
      • pip install mkdocs-markdownextradata-plugin (doesn't work on Python v2.7)
      • -
      -
    • -
    • install redirect plugin - used to redirect from moved pages
        -
      • pip install mkdocs-redirects
      • -
      -
    • -
    • Serve it up
        -
      • mkdocs serve
      • -
      • Open a browser to http://127.0.0.1:8000/
      • -
      -
    • -
    -
    -

    Please see the official mkdocs site for more details on working with mkdocs

    -
    -

    Next Steps

    -

    After your changes are made, you've added/updated tests, and updated the docs you're ready to submit a pull request!

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/contributing/extending-the-library/index.html b/docs/v3/v2/contributing/extending-the-library/index.html deleted file mode 100644 index c638f44bd..000000000 --- a/docs/v3/v2/contributing/extending-the-library/index.html +++ /dev/null @@ -1,2521 +0,0 @@ - - - - - - - - - - - - - - - - - - Extending the library - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    - -
    - - -
    -
    - - - - - - - -

    Extending PnPjs

    -
    -

    This article is targeted at people wishing to extend PnPjs itself, usually by adding a method or property.

    -
    -

    At the most basic level PnPjs is a set of libraries used to build and execute a web request and handle the response from that request. Conceptually each object in the fluent chain serves as input when creating the next object in the chain. This is how configuration, url, query, and other values are passed along. To get a sense for what this looks like see the code below. This is taken from inside the webs submodule and shows how the "webs" property is added to the web class.

    -
    // TypeScript property, returning an interface
    -public get webs(): IWebs {
    -    // using the Webs factory function and providing "this" as the first parameter
    -    return Webs(this);
    -}
    -
    -

    Understanding Factory Functions

    -

    PnPjs v2 is designed to only expose interfaces and factory functions. Let's look at the Webs factory function, used above as an example. All factory functions in sp and graph have a similar form.

    -
    // create a constant which is a function of type ISPInvokableFactory having the name Webs
    -// this is bound by the generic type param to return an IWebs instance
    -// and it will use the _Webs concrete class to form the internal type of the invocable
    -export const Webs = spInvokableFactory<IWebs>(_Webs);
    -
    -

    The ISPInvokableFactory type looks like:

    -
    export type ISPInvokableFactory<R = any> = (baseUrl: string | ISharePointQueryable, path?: string) => R;
    -
    -

    And the matching graph type:

    -
    <R>(f: any): (baseUrl: string | IGraphQueryable, path?: string) => R
    -
    -

    The general idea of a factory function is that it takes two parameters. The first is either a string or Queryable derivative which forms base for the new object. The second is the next part of the url. In some cases (like the webs property example above) you will note there is no second parameter. Some classes are decorated with defaultPath, which automatically fills the second param. Don't worry too much right now about the deep internals of the library, let's instead focus on some concrete examples.

    -
    import { Web } from "@pnp/sp/webs";
    -
    -// create a web from an absolute url
    -const web = Web("https://tenant.sharepoint.com");
    -
    -// as an example, create a new web using the first as a base
    -// targets: https://tenant.sharepoint.com/sites/dev
    -const web2 = Web(web, "sites/dev");
    -
    -// or you can add any path components you want, here as an example we access the current user property
    -const cu = Web(web, "currentuser");
    -const currentUserInfo = cu();
    -
    -

    Now hey you might say - you can't create a request to current user using the Web factory. Well you can, since everything is just based on urls under the covers the actual factory names don't mean anything other than they have the appropriate properties and method hung off them. This is brought up as you will see in many cases objects being used to create queries within methods and properties that don't match their "type". It is an important concept when working with the library to always remember we are just building strings.

    -

    Class structure

    -

    Internally to the library we have a bit of complexity to make the whole invocable proxy architecture work and provide the typings folks expect. Here is an example implementation with extra comments explaining what is happening. You don't need to understand the entire stack to add a property or method

    -
    /*
    -The concrete class implementation. This is never exported or shown directly
    -to consumers of the library. It is wrapped by the Proxy we do expose.
    -
    -It extends the _SharePointQueryableInstance class for which there is a matching
    -_SharePointQueryableCollection. The generic parameter defines the return type
    -of a get operation and the invoked result.
    -
    -Classes can have methods and properties as normal. This one has a single property as a simple example
    -*/
    -export class _HubSite extends _SharePointQueryableInstance<IHubSiteInfo> {
    -
    -    /**
    -     * Gets the ISite instance associated with this hub site
    -     */
    -    // the tag decorator is used to provide some additional telemetry on what methods are
    -    // being called.
    -    @tag("hs.getSite")
    -    public async getSite(): Promise<ISite> {
    -
    -        // we execute a request using this instance, selecting the SiteUrl property, and invoking it immediately and awaiting the result
    -        const d = await this.select("SiteUrl")();
    -
    -        // we then return a new ISite instance created from the Site factory using the returned SiteUrl property as the baseUrl
    -        return Site(d.SiteUrl);
    -    }
    -}
    -
    -/*
    -This defines the interface we export and expose to consumers.
    -In most cases this extends the concrete object but may add or remove some methods/properties
    -in special cases
    -*/
    -export interface IHubSite extends _HubSite { }
    -
    -/*
    -This defines the HubSite factory function as discussed above
    -binding the spInvokableFactory to a generic param of IHubSite and a param of _HubSite.
    -
    -This is understood to mean that HubSite is a factory function that returns a types of IHubSite
    -which the spInvokableFactory will create using _HubSite as the concrete underlying type.
    -*/
    -export const HubSite = spInvokableFactory<IHubSite>(_HubSite);
    -
    -

    Add a Property

    -

    In most cases you won't need to create the class, interface, or factory - you just want to add a property or method. An example of this is sp.web.lists. web is a property of sp and lists is a property of web. You can have a look at those classes as examples. Let's have a look at the fields on the _View class.

    -
    export class _View extends _SharePointQueryableInstance<IViewInfo> {
    -
    -    // ... other code removed
    -
    -    // add the property, and provide a return type
    -    // return types should be interfaces
    -    public get fields(): IViewFields {
    -        // we use the ViewFields factory function supplying "this" as the first parameter
    -        // this will create a url like ".../fields/viewfields" due to the defaultPath decorator
    -        // on the _ViewFields class. This is equivalent to: ViewFields(this, "viewfields")
    -        return ViewFields(this);
    -    }
    -
    -    // ... other code removed
    -}
    -
    -
    -

    There are many examples throughout the library that follow this pattern.

    -
    -

    Add a Method

    -

    Adding a method is just like adding a property with the key difference that a method usually does something like make a web request or act like a property but take parameters. Let's look at the _Items getById method:

    -
    @defaultPath("items")
    -export class _Items extends _SharePointQueryableCollection {
    -
    -    /**
    -    * Gets an Item by id
    -    *
    -    * @param id The integer id of the item to retrieve
    -    */
    -    // we declare a method and set the return type to an interface
    -    public getById(id: number): IItem {
    -        // here we use the tag helper to add some telemetry to our request
    -        // we create a new IItem using the factory and appending the id value to the end
    -        // this gives us a valid url path to a single item .../items/getById(2)
    -        // we can then use the returned IItem to extend our chain or execute a request
    -        return tag.configure(Item(this).concat(`(${id})`), "is.getById");
    -    }
    -
    -    // ... other code removed
    -}
    -
    -

    Web Request Method

    -

    A second example is a method that performs a request. Here we use the _Item recycle method as an example:

    -
    /**
    - * Moves the list item to the Recycle Bin and returns the identifier of the new Recycle Bin item.
    - */
    -// we use the tag decorator to add telemetry
    -@tag("i.recycle")
    -// we return a promise
    -public recycle(): Promise<string> {
    -    // we use the spPost method to post the request created by cloning our current instance IItem using
    -    // the Item factory and adding the path "recycle" to the end. Url will look like .../items/getById(2)/recycle
    -    return spPost<string>(this.clone(Item, "recycle"));
    -}
    -
    -

    Augment Using Selective Imports

    -

    To understand is how to extend functionality within the selective imports structures look at list.ts file in the items submodule. Here you can see the code below, with extra comments to explain what is happening. Again, you will see this pattern repeated throughout the library so there are many examples available.

    -
    // import the addProp helper
    -import { addProp } from "@pnp/queryable";
    -// import the _List concrete class from the types module (not the index!)
    -import { _List } from "../lists/types";
    -// import the interface and factory we are going to add to the List
    -import { Items, IItems } from "./types";
    -
    -// This module declaration fixes up the types, allowing .items to appear in intellisense
    -// when you import "@pnp/sp/items/list";
    -declare module "../lists/types" {
    -    // we need to extend the concrete type
    -    interface _List {
    -        readonly items: IItems;
    -    }
    -    // we need to extend the interface
    -    // this may not be strictly necessary as the IList interface extends _List so it
    -    // should pick up the same additions, but we have seen in some cases this does seem
    -    // to be required. So we include it for safety as it will all be removed during
    -    // transpilation we don't need to care about the extra code
    -    interface IList {
    -        readonly items: IItems;
    -    }
    -}
    -
    -// finally we add the property to the _List class
    -// this method call says add a property to _List named "items" and that property returns a result using the Items factory
    -// The factory will be called with "this" when the property is accessed. If needed there is a fourth parameter to append additional path
    -// information to the property url
    -addProp(_List, "items", Items);
    -
    -

    General Rules for Extending PnPjs

    -
      -
    • Only expose interfaces to consumers
    • -
    • Use the factory functions except in very special cases
    • -
    • Look for other properties and methods as examples
    • -
    • Simple is always preferable, but not always possible - use your best judgement
    • -
    • If you find yourself writing a ton of code to solve a problem you think should be easy, ask
    • -
    • If you find yourself deep within the core classes or odata library trying to make a change, ask - changes to the core classes are rarely needed
    • -
    -

    Next Steps

    -

    Now that you have extended the library you need to write a test to cover it!

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/contributing/index.html b/docs/v3/v2/contributing/index.html deleted file mode 100644 index c72642646..000000000 --- a/docs/v3/v2/contributing/index.html +++ /dev/null @@ -1,2270 +0,0 @@ - - - - - - - - - - - - - - - - - - Contributing - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Contributing to PnPjs

    -

    Thank you for your interest in contributing to PnPjs. We have updated our contribution section to make things easier to get started, debug the library locally, and learn how to extend the functionality.

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    SectionDescription
    Setup Dev MachineCovers setting up your machine to ensure you are ready to debug the solution
    Local Debug ConfigurationDiscusses the steps required to establish local configuration used for debugging and running tests
    DebuggingDescribes how to debug PnPjs locally
    Extending the libraryBasic examples on how to extend the library such as adding a method or property
    Writing TestsHow to write and debug tests
    Update DocumentationDescribes the steps required to edit and locally view the documentation
    Submit a Pull RequestOutlines guidance for submitting a pull request
    -

    Need Help?

    -

    The PnP "Sharing Is Caring" initiative teaches the basics around making changes in GitHub, submitting pull requests to the PnP & Microsoft 365 open-source repositories such as PnPjs.

    -

    Every month, we provide multiple live hands-on sessions that walk attendees through the process of using and contributing to PnP initiatives.

    -

    To learn more and register for an upcoming session, please visit the Sharing is Caring website.

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/contributing/local-debug-configuration/index.html b/docs/v3/v2/contributing/local-debug-configuration/index.html deleted file mode 100644 index 900e13f6e..000000000 --- a/docs/v3/v2/contributing/local-debug-configuration/index.html +++ /dev/null @@ -1,2284 +0,0 @@ - - - - - - - - - - - - - - - - - - Local Debug Configuration - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Local Debugging Configuration

    -

    This article covers the local setup required to debug the library and run tests. This only needs to be done once (unless you update the app registrations, then you just need to update the settings.js file accordingly).

    -

    Create settings.js

    -

    Both local debugging and tests make use of a settings.js file located in the root of the project. Ensure you create a settings.js files by copying settings.example.js and renaming it to settings.js. -For more information the settings file please see Settings

    -

    Minimal Configuration

    -

    You can control which tests are run by including or omitting sp and graph sections. If sp is present and graph is not, only sp tests are run. Include both and all tests are run, respecting the enableWebTests flag.

    -

    The following configuration file allows you to run all the tests that do not contact services.

    -
     var sets = {
    -     testing: {
    -         enableWebTests: false,
    -     }
    - }
    -
    -module.exports = sets;
    -
    -

    Test your setup

    -

    If you hit F5 in VSCode now you should be able to see the full response from getting the web's title in the internal console window. If not, ensure that you have properly updated the settings file and registered the add-in perms correctly.

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/contributing/pull-requests/index.html b/docs/v3/v2/contributing/pull-requests/index.html deleted file mode 100644 index 9c67e59e9..000000000 --- a/docs/v3/v2/contributing/pull-requests/index.html +++ /dev/null @@ -1,2261 +0,0 @@ - - - - - - - - - - - - - - - - - - Submit a Pull Request - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Submitting Pull Requests

    -

    Pull requests may be large or small - adding whole new features or fixing some misspellings. Regardless, they are all appreciated and help improve the library for everyone! By following the below guidelines we'll have an easier time merging your work and getting it into the next release.

    -
      -
    • Target your pull requests to the version-2 branch
    • -
    • Add/Update any relevant docs articles in the relevant package's docs folder related to your changes
    • -
    • Include a test for any new functionality and ensure all existing tests are passing by running npm test
    • -
    • Ensure linting checks pass by typing npm run lint
    • -
    • Ensure everything works for a build by running npm run package
    • -
    • Keep your PRs as simple as possible and describe the changes to help the reviewer understand your work
    • -
    • If you have an idea for a larger change to the library please open an issue and let's discuss before you invest many hours - these are very welcome but want to ensure it is something we can merge before you spend the time :)
    • -
    -
    -

    If you need to target a PR for version 1, please target the "version-1" branch

    -
    -

    Sharing is Caring - Pull Request Guidance

    -

    The PnP "Sharing Is Caring" initiative teaches the basics around making changes in GitHub, submitting pull requests to the PnP & Microsoft 365 open-source repositories such as PnPjs.

    -

    Every month, we provide multiple live hands-on sessions that walk attendees through the process of using and contributing to PnP initiatives.

    -

    To learn more and register for an upcoming session, please visit the Sharing is Caring website.

    -

    Next Steps

    -

    Now that you've submitted your PR please keep an eye on it as we might have questions. Once an initial review is complete we'll tag it with the expected version number for which it is targeted.

    -

    Thank you for helping PnPjs grow and improve!!

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/contributing/setup-dev-machine/index.html b/docs/v3/v2/contributing/setup-dev-machine/index.html deleted file mode 100644 index 5b6131020..000000000 --- a/docs/v3/v2/contributing/setup-dev-machine/index.html +++ /dev/null @@ -1,2284 +0,0 @@ - - - - - - - - - - - - - - - - - - Setup Dev Machine - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    Setting up your Developer Machine

    -

    If you are a longtime client side developer you likely have your machine already configured and can skip to forking the repo and debugging.

    -

    Setup your development environment

    -

    These steps will help you get your environment setup for contributing to the core library.

    -
      -
    1. -

      Install Visual Studio Code - this is the development environment we use so the contribution sections expect you are as well. If you prefer you can use Visual Studio or any editor you like.

      -
    2. -
    3. -

      Install Node JS - this provides two key capabilities; the first is the nodejs server which will act as our development server (think iisexpress), the second is npm a package manager (think nuget).

      -
      -

      This library requires node >= 10.18.0

      -
      -
    4. -
    5. -

      On Windows: Install Python

      -
    6. -
    7. -

      [Optional] Install the tslint extension in VS Code:

      -
        -
      1. Press Shift + Ctrl + "p" to open the command panel
      2. -
      3. Begin typing "install extension" and select the command when it appears in view
      4. -
      5. Begin typing "tslint" and select the package when it appears in view
      6. -
      7. Restart Code after installation
      8. -
      -
    8. -
    -

    Fork The Repo

    -

    All of our contributions come via pull requests and you'll need to fork the repository

    -
      -
    1. -

      Now we need to fork and clone the git repository. This can be done using your console or using your preferred Git GUI tool.

      -
    2. -
    3. -

      Once you have the code locally, navigate to the root of the project in your console. Type the following command:

      -

      npm install

      -
    4. -
    5. -

      Follow the guidance to complete the one-time local configuration required to debug and run tests.

      -
    6. -
    7. -

      Then you can follow the guidance in the debugging article.

      -
    8. -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/css/extra.css b/docs/v3/v2/css/extra.css deleted file mode 100644 index 6391b1ff7..000000000 --- a/docs/v3/v2/css/extra.css +++ /dev/null @@ -1,33 +0,0 @@ -.md-logo { - height: 32px; - width: 150px; - padding: 0 0.25 0.5 !important; -} - -.md-header{ - height: 75px; -} - -.md-container{ - padding-top: 70px; -} - -.md-sidebar[data-md-state="lock"]{ - padding-top: 75px; -} - -.md-logo img { - width: 100% !important; - height: auto !important; - margin-top: -0.25em; -} - -.md-footer { - margin-top: 5em; -} - -@media only screen and (max-width: 76.1875em) { - .md-nav--primary .md-nav__title--site .md-nav__button { - width: 150px; - } -} \ No newline at end of file diff --git a/docs/v3/v2/debug-tests/index.html b/docs/v3/v2/debug-tests/index.html deleted file mode 100644 index 00fa06798..000000000 --- a/docs/v3/v2/debug-tests/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Redirecting... - - - - - - -Redirecting... - - diff --git a/docs/v3/v2/debugging/index.html b/docs/v3/v2/debugging/index.html deleted file mode 100644 index cc419d1b0..000000000 --- a/docs/v3/v2/debugging/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Redirecting... - - - - - - -Redirecting... - - diff --git a/docs/v3/v2/documentation/index.html b/docs/v3/v2/documentation/index.html deleted file mode 100644 index 58a139e9c..000000000 --- a/docs/v3/v2/documentation/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Redirecting... - - - - - - -Redirecting... - - diff --git a/docs/v3/v2/getting-started-dev/index.html b/docs/v3/v2/getting-started-dev/index.html deleted file mode 100644 index a93ff396c..000000000 --- a/docs/v3/v2/getting-started-dev/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - Redirecting... - - - - - - -Redirecting... - - diff --git a/docs/v3/v2/getting-started/index.html b/docs/v3/v2/getting-started/index.html deleted file mode 100644 index 8eda6173a..000000000 --- a/docs/v3/v2/getting-started/index.html +++ /dev/null @@ -1,2789 +0,0 @@ - - - - - - - - - - - - - - - - - - Getting Started - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - - - -

    Getting Started

    -

    These libraries are geared towards folks working with TypeScript but will work equally well for JavaScript projects. To get started you need to install the libraries you need via npm. Many of the packages have a peer dependency to other packages with the @pnp namespace meaning you may need to install more than one package. All packages are released together eliminating version confusion - all packages will depend on packages with the same version number.

    -

    If you need to support older browsers please review the article on polyfills for required functionality.

    -

    Install

    -

    First you will need to install those libraries you want to use in your application. Here we will install the most frequently used packages. This step applies to any environment or project.

    -

    npm install @pnp/sp @pnp/graph --save

    -

    Next we can import and use the functionality within our application. Below is a very simple example, please see the individual package documentation -for more details and examples.

    -
    import { getRandomString } from "@pnp/core";
    -
    -(function() {
    -
    -  // get and log a random string
    -  console.log(getRandomString(20));
    -
    -})()
    -
    -

    Getting Started with SharePoint Framework

    -

    The @pnp/sp and @pnp/graph libraries are designed to work seamlessly within SharePoint Framework projects with a small amount of upfront configuration. If you are running in 2016 or 2019 on-premises please read this note on a workaround for the included TypeScript version. If you are targeting SharePoint online you do not need to take any additional steps.

    -

    Establish Context

    -

    Because SharePoint Framework provides a local context to each component we need to set that context within the library. This allows us to determine request urls as well as use the SPFx HttpGraphClient within @pnp/graph. There are two ways to provide the SPFx context to the library. Either through the setup method imported from @pnp/core or using the setup method on either the @pnp/sp or @pnp/graph main export. All three are shown below and are equivalent, meaning if you are already importing the sp variable from @pnp/sp or the graph variable from @pnp/graph you should use their setup method to reduce imports.

    -

    The setup is always done in the onInit method to ensure it runs before your other life-cycle code. You can also set any other settings at this time.

    -

    Using @pnp/core setup

    -
    import { setup as pnpSetup } from "@pnp/core";
    -
    -// ...
    -
    -protected onInit(): Promise<void> {
    -
    -  return super.onInit().then(_ => {
    -
    -    // other init code may be present
    -
    -    pnpSetup({
    -      spfxContext: this.context
    -    });
    -  });
    -}
    -
    -// ...
    -
    -
    -

    Using @pnp/sp setup

    -
    import { sp } from "@pnp/sp/presets/all";
    -
    -// ...
    -
    -protected onInit(): Promise<void> {
    -
    -  return super.onInit().then(_ => {
    -
    -    // other init code may be present
    -
    -    sp.setup({
    -      spfxContext: this.context
    -    });
    -  });
    -}
    -
    -// ...
    -
    -
    -

    Sp setup also supports passing just the SPFx context object directly as this is the most common case

    -
    import { sp } from "@pnp/sp/presets/all";
    -
    -// ...
    -
    -protected async onInit(): Promise<void> {
    -
    -  await super.onInit();
    -
    -  // other init code may be present
    -
    -  sp.setup(this.context);
    -}
    -
    -// ...
    -
    -
    -

    Using @pnp/graph setup

    -
    import { graph } from "@pnp/graph/presets/all";
    -
    -// ...
    -
    -protected onInit(): Promise<void> {
    -
    -  return super.onInit().then(_ => {
    -
    -    // other init code may be present
    -
    -    graph.setup({
    -      spfxContext: this.context
    -    });
    -  });
    -}
    -
    -// ...
    -
    -
    -

    Establish context within an SPFx service

    -

    Because you do not have full access to the context object within a service you need to setup things a little differently. If you do not need AAD tokens you can leave that part out and specify just the pageContext.

    -
    import { ServiceKey, ServiceScope } from "@microsoft/sp-core-library";
    -import { PageContext } from "@microsoft/sp-page-context";
    -import { AadTokenProviderFactory } from "@microsoft/sp-http";
    -import { sp } from "@pnp/sp";
    -import "@pnp/sp/webs";
    -import "@pnp/sp/lists/web";
    -
    -export interface ISampleService {
    -  getLists(): Promise<any[]>;
    -}
    -
    -export class SampleService {
    -
    -  public static readonly serviceKey: ServiceKey<ISampleService> = ServiceKey.create<ISampleService>('SPFx:SampleService', SampleService);
    -
    -  constructor(serviceScope: ServiceScope) {
    -
    -    serviceScope.whenFinished(() => {
    -
    -      const pageContext = serviceScope.consume(PageContext.serviceKey);
    -      const tokenProviderFactory = serviceScope.consume(AadTokenProviderFactory.serviceKey);
    -
    -      // we need to "spoof" the context object with the parts we need for PnPjs
    -      sp.setup({
    -        spfxContext: {
    -          aadTokenProviderFactory: tokenProviderFactory,
    -          pageContext: pageContext,
    -        }
    -      });
    -
    -      // This approach also works if you do not require AAD tokens
    -      // you don't need to do both
    -      // sp.setup({
    -      //   sp : {
    -      //     baseUrl : pageContext.web.absoluteUrl
    -      //   }
    -      // });
    -    });
    -  }
    -  public getLists(): Promise<any[]> {
    -    return sp.web.lists();
    -  }
    -}
    -
    -

    Connect to SharePoint from Node

    -
    -

    Please see the main article on how we support node versions that require commonjs modules.

    -
    -

    npm i @pnp/sp-commonjs @pnp/nodejs-commonjs

    -

    This will install the logging, common, odata, sp, and nodejs packages. You can read more about what each package does starting on the packages page. -Once these are installed you need to import them into your project, to communicate with SharePoint from node we'll need the following imports:

    -
    import { sp } from "@pnp/sp-commonjs";
    -import { SPFetchClient } from "@pnp/nodejs-commonjs";
    -
    -

    Once you have imported the necessary resources you can update your code to setup the node fetch client as well as make a call to SharePoint.

    -
    // configure your node options (only once in your application)
    -sp.setup({
    -    sp: {
    -        fetchClientFactory: () => {
    -            return new SPFetchClient("{site url}", "{client id}", "{client secret}");
    -        },
    -    },
    -});
    -
    -// make a call to SharePoint and log it in the console
    -sp.web.select("Title", "Description")().then(w => {
    -    console.log(JSON.stringify(w, null, 4));
    -});
    -
    -

    Connect to Microsoft Graph From Node

    -

    Similar to the above you can also make calls to the Graph api from node using the libraries. Again we start with installing the required resources. You can see -./debug/launch/graph.ts for a live example.

    -
    npm i @pnp/graph-commonjs @pnp/nodejs-commonjs
    -
    -

    Now we need to import what we'll need to call graph

    -
    import { graph } from "@pnp/graph-commonjs";
    -import { AdalFetchClient } from "@pnp/nodejs-commonjs";
    -
    -

    Now we can make our graph calls after setting up the Adal client. Note you'll need to setup an AzureAD App registration with the necessary permissions.

    -
    graph.setup({
    -    graph: {
    -        fetchClientFactory: () => {
    -            return new AdalFetchClient("{mytenant}.onmicrosoft.com", "{application id}", "{application secret}");
    -        },
    -    },
    -});
    -
    -// make a call to Graph and get all the groups
    -graph.groups().then(g => {
    -    console.log(JSON.stringify(g, null, 4));
    -});
    -
    -

    Getting Started outside SharePoint Framework

    -

    In some cases you may be working in a way such that we cannot determine the base url for the web. In this scenario you have two options.

    -

    Set baseUrl through setup

    -

    Here we are setting the baseUrl via the sp.setup method. We are also setting the headers to use verbose mode, something you may have to do when -working against unpatched versions of SharePoint 2013 as discussed here. -This is optional for 2016 or SharePoint Online. The library does not support setting the headers to use nometadata as we rely on the metadata in the response to do some of the more complicated functions. Some of the pure data calls will probably work but it is not a supported configuration.

    -
    import { sp } from "@pnp/sp/presets/all";
    -
    -sp.setup({
    -  sp: {
    -    headers: {
    -      Accept: "application/json;odata=verbose",
    -    },
    -    baseUrl: "{Absolute SharePoint Web URL}"
    -  },
    -});
    -
    -const w = await sp.web();
    -
    -

    Create Web instances directly

    -

    Using this method you create the web directly with the url you want to use as the base.

    -
    import { Web } from "@pnp/sp/presets/all";
    -
    -const web = Web("{Absolute SharePoint Web URL}");
    -const w = await web();
    -
    -

    Next Steps

    -

    Be sure to review the article describing all of the available settings across the libraries.

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/calendars/index.html b/docs/v3/v2/graph/calendars/index.html deleted file mode 100644 index 51b014348..000000000 --- a/docs/v3/v2/graph/calendars/index.html +++ /dev/null @@ -1,2569 +0,0 @@ - - - - - - - - - - - - - - - - - - calendars - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - - - -

    @pnp/graph/calendars

    -

    Calendars exist in Outlook and can belong to either a user or group. With @pnp/graph@<=2.0.6, only events for a user and group's default calendar could be fetched/created/updated. In versions 2.0.7 and up, all calendars and their events can be fetched.

    -

    More information can be found in the official Graph documentation:

    - -

    ICalendar, ICalendars

    -

    Invokable Banner Selective Imports Banner

    - - - - - - - - - - - - - - - - - -
    ScenarioImport Statement
    Selective 1import { graph } from "@pnp/graph";
    import "@pnp/graph/calendars";
    Preset: Allimport { graph } from "@pnp/graph/presets/all";
    -

    Get All Calendars For a User

    -
    import { graph } from '@pnp/graph';
    -import '@pnp/graph/calendars';
    -import '@pnp/graph/users';
    -
    -const calendars = await graph.users.getById('user@tenant.onmicrosoft.com').calendars();
    -
    -const myCalendars = await graph.me.calendars();
    -
    -
    -

    Get a Specific Calendar For a User

    -
    import { graph } from '@pnp/graph';
    -import '@pnp/graph/calendars';
    -import '@pnp/graph/users';
    -
    -const CALENDAR_ID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA-xUBMMopY1NkrWA0qGcXHsHAG4I-wMXjoRMkgRnRetM5oIAAAIBBgAAAG4I-wMXjoRMkgRnRetM5oIAAAIsYgAAAA==';
    -
    -const calendar = await graph.users.getById('user@tenant.onmicrosoft.com').calendars.getById(CALENDAR_ID)();
    -
    -const myCalendar = await graph.me.calendars.getById(CALENDAR_ID)();
    -
    -

    Get a User's Default Calendar

    -
    import { graph } from '@pnp/graph';
    -import '@pnp/graph/calendars';
    -import '@pnp/graph/users';
    -
    -const calendar = await graph.users.getById('user@tenant.onmicrosoft.com').calendar();
    -
    -const myCalendar = await graph.me.calendar();
    -
    -

    Get Events For a User's Default Calendar

    -
    import { graph } from '@pnp/graph';
    -import '@pnp/graph/calendars';
    -import '@pnp/graph/users';
    -
    -// You can get the default calendar events
    -const events = await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events();
    -// or get all events for the user
    -const events = await graph.users.getById('user@tenant.onmicrosoft.com').events();
    -
    -// You can get my default calendar events
    -const events = await graph.me.calendar.events();
    -// or get all events for me
    -const events = await graph.me.events();
    -
    -

    Get Events By ID

    -

    You can use .events.getByID to search through all the events in all calendars or narrow the request to a specific calendar.

    -
    import { graph } from '@pnp/graph';
    -import '@pnp/graph/calendars';
    -import '@pnp/graph/users';
    -
    -const CalendarID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA==';
    -
    -const EventID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA-xUBMMopY1NkrWA0qGcXHsHAG4I-wMXjoRMkgRnRetM5oIAAAIBBgAAAG4I-wMXjoRMkgRnRetM5oIAAAIsYgAAAA==';
    -
    -// Get events by ID
    -const event = await graph.users.getById('user@tenant.onmicrosoft.com').events.getByID(EventID);
    -
    -const events = await graph.me.events.getByID(EventID);
    -
    -// Get an event by ID from a specific calendar
    -const event = await graph.users.getById('user@tenant.onmicrosoft.com').calendars.getByID(CalendarID).events.getByID(EventID);
    -
    -const events = await graph.me.calendars.getByID(CalendarID).events.getByID(EventID);
    -
    -
    -

    Create Events

    -

    This will work on any IEvents objects (e.g. anything accessed using an events key).

    -
    import { graph } from '@pnp/graph';
    -import '@pnp/graph/calendars';
    -import '@pnp/graph/users';
    -
    -await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events.add(
    -{
    -  "subject": "Let's go for lunch",
    -  "body": {
    -    "contentType": "HTML",
    -    "content": "Does late morning work for you?"
    -  },
    -  "start": {
    -      "dateTime": "2017-04-15T12:00:00",
    -      "timeZone": "Pacific Standard Time"
    -  },
    -  "end": {
    -      "dateTime": "2017-04-15T14:00:00",
    -      "timeZone": "Pacific Standard Time"
    -  },
    -  "location":{
    -      "displayName":"Harry's Bar"
    -  },
    -  "attendees": [
    -    {
    -      "emailAddress": {
    -        "address":"samanthab@contoso.onmicrosoft.com",
    -        "name": "Samantha Booth"
    -      },
    -      "type": "required"
    -    }
    -  ]
    -});
    -
    -

    Update Events

    -

    This will work on any IEvents objects (e.g. anything accessed using an events key).

    -
    import { graph } from '@pnp/graph';
    -import '@pnp/graph/calendars';
    -import '@pnp/graph/users';
    -
    -const EVENT_ID = 'BBMkAGZjNmY6MDM3LWI3YTYtNERhZC05Y2FkLTgyZjcwZjE4OTI5ZQBGAAAAAAD8VQTDKKWNTY61gNKhnFzLBwBuCP8DF46ETJIEZ0XrTOaCAAAAAAENAABuCP8DF46ETJFEZ0EnTOaCAAFvdoJvAAA=';
    -
    -await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events.getById(EVENT_ID).update({
    -    reminderMinutesBeforeStart: 99,
    -});
    -
    -

    Delete Event

    -

    This will work on any IEvents objects (e.g. anything accessed using an events key).

    -
    import { graph } from '@pnp/graph';
    -import '@pnp/graph/calendars';
    -import '@pnp/graph/users';
    -
    -const EVENT_ID = 'BBMkAGZjNmY6MDM3LWI3YTYtNERhZC05Y2FkLTgyZjcwZjE4OTI5ZQBGAAAAAAD8VQTDKKWNTY61gNKhnFzLBwBuCP8DF46ETJIEZ0XrTOaCAAAAAAENAABuCP8DF46ETJFEZ0EnTOaCAAFvdoJvAAA=';
    -
    -await graph.users.getById('user@tenant.onmicrosoft.com').events.getById(EVENT_ID).delete();
    -
    -await graph.me.events.getById(EVENT_ID).delete();
    -
    -

    Get Calendar for a Group

    -
    import { graph } from '@pnp/graph';
    -import '@pnp/graph/calendars';
    -import '@pnp/graph/groups';
    -
    -const calendar = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').calendar();
    -
    -

    Get Events for a Group

    -
    import { graph } from '@pnp/graph';
    -import '@pnp/graph/calendars';
    -import '@pnp/graph/groups';
    -
    -// You can do one of
    -const events = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').calendar.events();
    -// or
    -const events = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').events();
    -
    -

    Get Calendar View

    -

    Added in 2.0.7 -Gets the events in a calendar during a specified date range.

    -
    import { graph } from '@pnp/graph';
    -import '@pnp/graph/calendars';
    -import '@pnp/graph/users';
    -
    -// basic request, note need to invoke the returned queryable
    -const view = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView("2020-01-01", "2020-03-01")();
    -
    -// you can use select, top, etc to filter your returned results
    -const view2 = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView("2020-01-01", "2020-03-01").select("subject").top(3)();
    -
    -// you can specify times along with the dates
    -const view3 = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView("2020-01-01T19:00:00-08:00", "2020-03-01T19:00:00-08:00")();
    -
    -const view4 = await graph.me.calendarView("2020-01-01", "2020-03-01")();
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/contacts/index.html b/docs/v3/v2/graph/contacts/index.html deleted file mode 100644 index 2edd521d8..000000000 --- a/docs/v3/v2/graph/contacts/index.html +++ /dev/null @@ -1,2673 +0,0 @@ - - - - - - - - - - - - - - - - - - contacts - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - - - -

    @pnp/graph/contacts

    -

    The ability to manage contacts and folders in Outlook is a capability introduced in version 1.2.2 of @pnp/graph. Through the methods described -you can add and edit both contacts and folders in a users Outlook.

    -

    More information can be found in the official Graph documentation:

    - -

    IContact, IContacts, IContactFolder, IContactFolders

    -

    Invokable Banner Selective Imports Banner

    - - - - - - - - - - - - - - - - - -
    ScenarioImport Statement
    Selective 1import { graph } from "@pnp/graph";
    import "@pnp/graph/contacts";
    Preset: Allimport { graph } from "@pnp/graph/presets/all";
    -

    Set up notes

    -

    To make user calls you can use getById where the id is the users email address. -Contact ID, Folder ID, and Parent Folder ID use the following format "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA="

    -

    Get all of the Contacts

    -

    Gets a list of all the contacts for the user.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const contacts = await graph.users.getById('user@tenant.onmicrosoft.com').contacts();
    -
    -const contacts2 = await graph.me.contacts();
    -
    -
    -

    Get Contact by Id

    -

    Gets a specific contact by ID for the user.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const contactID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=";
    -
    -const contact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID)();
    -
    -const contact2 = await graph.me.contacts.getById(contactID)();
    -
    -
    -

    Add a new Contact

    -

    Adds a new contact for the user.

    -
    import { graph } from "@pnp/graph";
    -import { EmailAddress } from "@microsoft/microsoft-graph-types";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const addedContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.add('Pavel', 'Bansky', [<EmailAddress>{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);
    -
    -const addedContact2 = await graph.me.contacts.add('Pavel', 'Bansky', [<EmailAddress>{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);
    -
    -
    -

    Update a Contact

    -

    Updates a specific contact by ID for teh designated user

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const contactID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=";
    -
    -const updContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID).update({birthday: "1986-05-30" });
    -
    -const updContact2 = await graph.me.contacts.getById(contactID).update({birthday: "1986-05-30" });
    -
    -
    -

    Delete a Contact

    -

    Delete a contact from the list of contacts for a user.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const contactID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=";
    -
    -const delContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID).delete();
    -
    -const delContact2 = await graph.me.contacts.getById(contactID).delete();
    -
    -
    -

    Get all of the Contact Folders

    -

    Get all the folders for the designated user's contacts

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const contactFolders = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders();
    -
    -const contactFolders2 = await graph.me.contactFolders();
    -
    -
    -

    Get Contact Folder by Id

    -

    Get a contact folder by ID for the specified user

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const folderID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=";
    -
    -const contactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID)();
    -
    -const contactFolder2 = await graph.me.contactFolders.getById(folderID)();
    -
    -
    -

    Add a new Contact Folder

    -

    Add a new folder in the users contacts

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const parentFolderID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAAAAAEOAAA=";
    -
    -const addedContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.add("New Folder", parentFolderID);
    -
    -const addedContactFolder2 = await graph.me.contactFolders.add("New Folder", parentFolderID);
    -
    -
    -

    Update a Contact Folder

    -

    Update an existing folder in the users contacts

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const folderID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=";
    -
    -const updContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).update({displayName: "Updated Folder" });
    -
    -const updContactFolder2 = await graph.me.contactFolders.getById(folderID).update({displayName: "Updated Folder" });
    -
    -
    -

    Delete a Contact Folder

    -

    Delete a folder from the users contacts list. Deleting a folder deletes the contacts in that folder.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const folderID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=";
    -
    -const delContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).delete();
    -
    -const delContactFolder2 = await graph.me.contactFolders.getById(folderID).delete();
    -
    -
    -

    Get all of the Contacts from the Contact Folder

    -

    Get all the contacts in a folder

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const folderID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=";
    -
    -const contactsInContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).contacts();
    -
    -const contactsInContactFolder2 = await graph.me.contactFolders.getById(folderID).contacts();
    -
    -
    -

    Get Child Folders of the Contact Folder

    -

    Get child folders from contact folder

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const folderID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=";
    -
    -const childFolders = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders();
    -
    -const childFolders2 = await graph.me.contactFolders.getById(folderID).childFolders();
    -
    -
    -

    Add a new Child Folder

    -

    Add a new child folder to a contact folder

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const folderID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=";
    -
    -const addedChildFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.add("Sub Folder", folderID);
    -
    -const addedChildFolder2 = await graph.me.contactFolders.getById(folderID).childFolders.add("Sub Folder", folderID);
    -
    -

    Get Child Folder by Id

    -

    Get child folder by ID from user contacts

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const folderID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=";
    -const subFolderID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqIZAAA=";
    -
    -const childFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.getById(subFolderID)();
    -
    -const childFolder2 = await graph.me.contactFolders.getById(folderID).childFolders.getById(subFolderID)();
    -
    -

    Add Contact in Child Folder of Contact Folder

    -

    Add a new contact to a child folder

    -
    import { graph } from "@pnp/graph";
    -import { EmailAddress } from "./@microsoft/microsoft-graph-types";
    -import "@pnp/graph/users"
    -import "@pnp/graph/contacts"
    -
    -const folderID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=";
    -const subFolderID = "AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqIZAAA=";
    -
    -const addedContact = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.getById(subFolderID).contacts.add('Pavel', 'Bansky', [<EmailAddress>{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);
    -
    -const addedContact2 = await graph.me.contactFolders.getById(folderID).childFolders.getById(subFolderID).contacts.add('Pavel', 'Bansky', [<EmailAddress>{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);
    -
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/directoryobjects/index.html b/docs/v3/v2/graph/directoryobjects/index.html deleted file mode 100644 index a9d1518ad..000000000 --- a/docs/v3/v2/graph/directoryobjects/index.html +++ /dev/null @@ -1,2402 +0,0 @@ - - - - - - - - - - - - - - - - - - directory objects - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - - - -

    @pnp/graph/directoryObjects

    -

    Represents an Azure Active Directory object. The directoryObject type is the base type for many other directory entity types.

    -

    More information can be found in the official Graph documentation:

    - -

    IDirectoryObject, IDirectoryObjects

    -

    Invokable Banner Selective Imports Banner

    - - - - - - - - - - - - - - - - - -
    ScenarioImport Statement
    Selective 1import { graph } from "@pnp/graph";
    import "@pnp/graph/directory-objects";
    Preset: Allimport { graph } from "@pnp/sp/presets/all";
    -

    The groups and directory roles for the user

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -
    -const memberOf = await graph.users.getById('user@tenant.onmicrosoft.com').memberOf();
    -
    -const memberOf2 = await graph.me.memberOf();
    -
    -
    -

    Return all the groups the user, group or directoryObject is a member of. Add true parameter to return only security enabled groups

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/groups"
    -
    -const memberGroups = await graph.users.getById('user@tenant.onmicrosoft.com').getMemberGroups();
    -
    -const memberGroups2 = await graph.me.getMemberGroups();
    -
    -// Returns only security enabled groups
    -const memberGroups3 = await graph.me.getMemberGroups(true);
    -
    -const memberGroups4 = await graph.groups.getById('user@tenant.onmicrosoft.com').getMemberGroups();
    -
    -
    -

    Returns all the groups, administrative units and directory roles that a user, group, or directory object is a member of. Add true parameter to return only security enabled groups

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -import "@pnp/graph/groups";
    -
    -const memberObjects = await graph.users.getById('user@tenant.onmicrosoft.com').getMemberObjects();
    -
    -const memberObjects2 = await graph.me.getMemberObjects();
    -
    -// Returns only security enabled groups
    -const memberObjects3 = await graph.me.getMemberObjects(true);
    -
    -const memberObjects4 = await graph.groups.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').getMemberObjects();
    -
    -

    Check for membership in a specified list of groups

    -

    And returns from that list those groups of which the specified user, group, or directory object is a member

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -import "@pnp/graph/groups";
    -
    -const checkedMembers = await graph.users.getById('user@tenant.onmicrosoft.com').checkMemberGroups(["c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741","2001bb09-1d46-40a6-8176-7bb867fb75aa"]);
    -
    -const checkedMembers2 = await graph.me.checkMemberGroups(["c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741","2001bb09-1d46-40a6-8176-7bb867fb75aa"]);
    -
    -const checkedMembers3 = await graph.groups.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').checkMemberGroups(["c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741","2001bb09-1d46-40a6-8176-7bb867fb75aa"]);
    -
    -

    Get directoryObject by Id

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/directory-objects";
    -
    -const dirObject = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26');
    -
    -
    -

    Delete directoryObject

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/directory-objects";
    -
    -const deleted = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').delete()
    -
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/groups/index.html b/docs/v3/v2/graph/groups/index.html deleted file mode 100644 index ef7438b34..000000000 --- a/docs/v3/v2/graph/groups/index.html +++ /dev/null @@ -1,2488 +0,0 @@ - - - - - - - - - - - - - - - - - - groups - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - - - -

    @pnp/graph/groups

    -

    Groups are collections of users and other principals who share access to resources in Microsoft services or in your app. All group-related operations in Microsoft Graph require administrator consent.

    -

    Note: Groups can only be created through work or school accounts. Personal Microsoft accounts don't support groups.

    -

    You can learn more about Microsoft Graph Groups by reading the Official Microsoft Graph Documentation.

    -

    IGroup, IGroups

    -

    Invokable Banner Selective Imports Banner

    - - - - - - - - - - - - - - - - - - - - - -
    ScenarioImport Statement
    Selective 1import { graph } from "@pnp/graph";
    import {Group, GroupType, Groups, IGroup, IGroupAddResult, IGroups} from "@pnp/graph/groups";
    Selective 2import { graph } from "@pnp/graph";
    import "@pnp/graph/groups";
    Preset: Allimport { graph, Group, GroupType, Groups, IGroup, IGroupAddResult, IGroups } from "@pnp/graph/presets/all";
    -

    Add a Group

    -

    Add a new group.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/groups";
    -import { GroupType } from '@pnp/graph/groups';
    -
    -const groupAddResult = await graph.groups.add("GroupName", "Mail_NickName", GroupType.Office365);
    -const group = await groupAddResult.group();
    -
    -

    Delete a Group

    -

    Deletes an existing group.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/groups";
    -
    -await graph.groups.getById("7d2b9355-0891-47d3-84c8-bf2cd9c62177").delete();
    -
    -

    Update Group Properties

    -

    Updates an existing group.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/groups";
    -
    -await graph.groups.getById("7d2b9355-0891-47d3-84c8-bf2cd9c62177").update({ displayName: newName, propertyName: updatedValue});
    -
    -

    Add favorite

    -

    Add the group to the list of the current user's favorite groups. Supported for Office 365 groups only.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/groups";
    -
    -await graph.groups.getById("7d2b9355-0891-47d3-84c8-bf2cd9c62177").addFavorite();
    -
    -

    Remove favorite

    -

    Remove the group from the list of the current user's favorite groups. Supported for Office 365 Groups only.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/groups";
    -
    -await graph.groups.getById("7d2b9355-0891-47d3-84c8-bf2cd9c62177").removeFavorite();
    -
    -

    Reset Unseen Count

    -

    Reset the unseenCount of all the posts that the current user has not seen since their last visit. Supported for Office 365 groups only.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/groups";
    -
    -await graph.groups.getById("7d2b9355-0891-47d3-84c8-bf2cd9c62177").resetUnseenCount();
    -
    -

    Subscribe By Mail

    -

    Calling this method will enable the current user to receive email notifications for this group, about new posts, events, and files in that group. Supported for Office 365 groups only.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/groups";
    -
    -await graph.groups.getById("7d2b9355-0891-47d3-84c8-bf2cd9c62177").subscribeByMail();
    -
    -

    Unsubscribe By Mail

    -

    Calling this method will prevent the current user from receiving email notifications for this group about new posts, events, and files in that group. Supported for Office 365 groups only.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/groups";
    -
    -await graph.groups.getById("7d2b9355-0891-47d3-84c8-bf2cd9c62177").unsubscribeByMail();
    -
    -

    Get Calendar View

    -

    Get the occurrences, exceptions, and single instances of events in a calendar view defined by a time range, from the default calendar of a group.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/groups";
    -
    -const startDate = new Date("2020-04-01");
    -const endDate = new Date("2020-03-01");
    -
    -const events = graph.groups.getById("7d2b9355-0891-47d3-84c8-bf2cd9c62177").getCalendarView(startDate, endDate);
    -
    -

    Group Photo Operations

    -

    See Photos

    -

    Get the Team Site for a Group

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/groups";
    -import "@pnp/graph/sites/group";
    -
    -const teamSite = await graph.groups.getById("7d2b9355-0891-47d3-84c8-bf2cd9c62177").sites.root();
    -const url = teamSite.webUrl
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/index.html b/docs/v3/v2/graph/index.html deleted file mode 100644 index 96c026744..000000000 --- a/docs/v3/v2/graph/index.html +++ /dev/null @@ -1,2333 +0,0 @@ - - - - - - - - - - - - - - - - - - graph - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/graph

    -

    npm version

    -

    This package contains the fluent api used to call the graph rest services.

    -

    Getting Started

    -

    Install the library and required dependencies

    -

    npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save

    -

    Import the library into your application and access the root sp object

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/groups";
    -
    -(function main() {
    -
    -    // here we will load the current web's properties
    -    graph.groups().then(g => {
    -
    -        console.log(`Groups: ${JSON.stringify(g, null, 4)}`);
    -    });
    -})()
    -
    -

    Getting Started with SharePoint Framework

    -

    Install the library and required dependencies

    -

    npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save

    -

    Import the library into your application, update OnInit, and access the root sp object in render

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/groups";
    -
    -// ...
    -
    -public onInit(): Promise<void> {
    -
    -  return super.onInit().then(_ => {
    -
    -    // other init code may be present
    -
    -    graph.setup({
    -      spfxContext: this.context
    -    });
    -  });
    -}
    -
    -// ...
    -
    -public render(): void {
    -
    -    // A simple loading message
    -    this.domElement.innerHTML = `Loading...`;
    -
    -    // here we will load the current web's properties
    -    graph.groups().then(groups => {
    -
    -        this.domElement.innerHTML = `Groups: <ul>${groups.map(g => `<li>${g.displayName}</li>`).join("")}</ul>`;
    -    });
    -}
    -
    -

    Getting Started on Nodejs

    -

    Install the library and required dependencies

    -

    npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph @pnp/nodejs --save

    -

    Import the library into your application, setup the node client, make a request

    -
    import { graph } from "@pnp/graph";
    -import { AdalFetchClient } from "@pnp/nodejs";
    -import "@pnp/graph/groups";
    -
    -// do this once per page load
    -graph.setup({
    -    graph: {
    -        fetchClientFactory: () => {
    -            return new AdalFetchClient("{tenant}.onmicrosoft.com", "AAD Application Id", "AAD Application Secret");
    -        },
    -    },
    -});
    -
    -// here we will load the groups information
    -graph.groups().then(g => {
    -
    -    console.log(`Groups: ${JSON.stringify(g, null, 4)}`);
    -});
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/insights/index.html b/docs/v3/v2/graph/insights/index.html deleted file mode 100644 index c3ea73bd7..000000000 --- a/docs/v3/v2/graph/insights/index.html +++ /dev/null @@ -1,2478 +0,0 @@ - - - - - - - - - - - - - - - - - - insights - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - - - -

    @pnp/graph/insights

    -

    This module helps you get Insights in form of Trending, Used and Shared. The results are based on relationships calculated using advanced analytics and machine learning techniques.

    -

    IInsights

    -

    Invokable Banner Selective Imports Banner

    - - - - - - - - - - - - - - - - - -
    ScenarioImport Statement
    Selectiveimport { graph } from "@pnp/graph";
    import "@pnp/graph/insights";
    Preset: Allimport "@pnp/graph/presets/all";
    - -

    Returns documents from OneDrive and SharePoint sites trending around a user.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/insights";
    -import "@pnp/graph/users";
    -
    -const trending = await graph.me.insights.trending()
    -
    -const trending = await graph.users.getById("userId").insights.trending()
    -
    - -

    Using the getById method to get a trending document by Id.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/insights";
    -import "@pnp/graph/users";
    -
    -const trendingDoc = await graph.me.insights.trending.getById('Id')()
    -
    -const trendingDoc = await graph.users.getById("userId").insights.trending.getById('Id')()
    -
    - -

    Using the resources method to get the resource from a trending document.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/insights";
    -import "@pnp/graph/users";
    -
    -const resource = await graph.me.insights.trending.getById('Id').resource()
    -
    -const resource = await graph.users.getById("userId").insights.trending.getById('Id').resource()
    -
    -

    Get all Used documents

    -

    Returns documents viewed and modified by a user. Includes documents the user used in OneDrive for Business, SharePoint, opened as email attachments, and as link attachments from sources like Box, DropBox and Google Drive.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/insights";
    -import "@pnp/graph/users";
    -
    -const used = await graph.me.insights.used()
    -
    -const used = await graph.users.getById("userId").insights.used()
    -
    -

    Get a Used document by Id

    -

    Using the getById method to get a used document by Id.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/insights";
    -import "@pnp/graph/users";
    -
    -const usedDoc = await graph.me.insights.used.getById('Id')()
    -
    -const usedDoc = await graph.users.getById("userId").insights.used.getById('Id')()
    -
    -

    Get the resource from Used document

    -

    Using the resources method to get the resource from a used document.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/insights";
    -import "@pnp/graph/users";
    -
    -const resource = await graph.me.insights.used.getById('Id').resource()
    -
    -const resource = await graph.users.getById("userId").insights.used.getById('Id').resource()
    -
    -

    Get all Shared documents

    -

    Returns documents shared with a user. Documents can be shared as email attachments or as OneDrive for Business links sent in emails.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/insights";
    -import "@pnp/graph/users";
    -
    -const shared = await graph.me.insights.shared()
    -
    -const shared = await graph.users.getById("userId").insights.shared()
    -
    -

    Get a Shared document by Id

    -

    Using the getById method to get a shared document by Id.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/insights";
    -import "@pnp/graph/users";
    -
    -const sharedDoc = await graph.me.insights.shared.getById('Id')()
    -
    -const sharedDoc = await graph.users.getById("userId").insights.shared.getById('Id')()
    -
    -

    Get the resource from a Shared document

    -

    Using the resources method to get the resource from a shared document.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/insights";
    -import "@pnp/graph/users";
    -
    -const resource = await graph.me.insights.shared.getById('Id').resource()
    -
    -const resource = await graph.users.getById("userId").insights.shared.getById('Id').resource()
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/invitations/index.html b/docs/v3/v2/graph/invitations/index.html deleted file mode 100644 index 847f749f3..000000000 --- a/docs/v3/v2/graph/invitations/index.html +++ /dev/null @@ -1,2273 +0,0 @@ - - - - - - - - - - - - - - - - - - invitations - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/graph/invitations

    -

    The ability invite an external user via the invitation manager

    -

    IInvitations

    -

    Invokable Banner Selective Imports Banner

    - - - - - - - - - - - - - - - - - -
    ScenarioImport Statement
    Selectiveimport { graph } from "@pnp/graph";
    import "@pnp/graph/invitations";
    Preset: Allimport "@pnp/graph/presets/all";
    -

    Create Invitation

    -

    Using the invitations.create() you can create an Invitation. -We need the email address of the user being invited and the URL user should be redirected to once the invitation is redeemed (redirect URL).

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/invitations"
    -
    -const invitationResult = await graph.invitations.create('external.user@email-address.com', 'https://tenant.sharepoint.com/sites/redirecturi');
    -
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/onedrive/index.html b/docs/v3/v2/graph/onedrive/index.html deleted file mode 100644 index 9e2f58a5c..000000000 --- a/docs/v3/v2/graph/onedrive/index.html +++ /dev/null @@ -1,2617 +0,0 @@ - - - - - - - - - - - - - - - - - - onedrive - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - - - -

    @pnp/graph/onedrive

    -

    The ability to manage drives and drive items in Onedrive is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described -you can manage drives and drive items in Onedrive.

    -

    IInvitations

    -

    Invokable Banner Selective Imports Banner

    - - - - - - - - - - - - - - - - - -
    ScenarioImport Statement
    Selectiveimport { graph } from "@pnp/graph";
    import "@pnp/graph/onedrive";
    Preset: Allimport "@pnp/graph/presets/all";
    -

    Get the default drive

    -

    Using the drive() you can get the default drive from Onedrive

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -const drives = await graph.users.getById('user@tenant.onmicrosoft.com').drives();
    -
    -const drives = await graph.me.drives();
    -
    -
    -

    Get all of the drives

    -

    Using the drives() you can get the users available drives from Onedrive

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -const drives = await graph.users.getById('user@tenant.onmicrosoft.com').drives();
    -
    -const drives = await graph.me.drives();
    -
    -
    -

    Get drive by Id

    -

    Using the drives.getById() you can get one of the available drives in Outlook

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -const drive = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId');
    -
    -const drive = await graph.me.drives.getById('driveId');
    -
    -
    -

    Get the associated list of a drive

    -

    Using the list() you get the associated list

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -const list = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').list();
    -
    -const list = await graph.me.drives.getById('driveId').list();
    -
    -
    -

    Get the recent files

    -

    Using the recent() you get the recent files

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -const files = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').recent();
    -
    -const files = await graph.me.drives.getById('driveId').recent();
    -
    -
    -

    Get the files shared with me

    -

    Using the sharedWithMe() you get the files shared with the user

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -const shared = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').sharedWithMe();
    -
    -const shared = await graph.me.drives.getById('driveId').sharedWithMe();
    -
    -
    -

    Get the Root folder

    -

    Using the root() you get the root folder

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -const root = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root();
    -
    -const root = await graph.me.drives.getById('driveId').root();
    -
    -
    -

    Get the Children

    -

    Using the children() you get the children

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -const rootChildren = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root.children();
    -
    -const rootChildren = await graph.me.drives.getById('driveId').root.children();
    -
    -const itemChildren = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').children();
    -
    -const itemChildren = await graph.me.drives.getById('driveId').root.items.getById('itemId').children();
    -
    -
    -

    Add folder or item

    -

    Using the add you can add a folder or an item

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -import { DriveItem as IDriveItem } from "@microsoft/microsoft-graph-types";
    -
    -const addFolder = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root.children.add('New Folder', <IDriveItem>{folder: {}});
    -
    -const addFolder = await graph.me.drives.getById('driveId').root.children.add('New Folder', <IDriveItem>{folder: {}});
    -
    -
    -

    Search items

    -

    Using the search() you can search for items, and optionally select properties

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -const search = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId')root.search('queryText')();
    -
    -const search = await graph.me.drives.getById('driveId')root.search('queryText')();
    -
    -
    -

    Get specific item in drive

    -

    Using the items.getById() you can get a specific item from the current drive

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -const item = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId');
    -
    -const item = await graph.me.drives.getById('driveId').items.getById('itemId');
    -
    -
    -

    Get thumbnails

    -

    Using the thumbnails() you get the thumbnails

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -const thumbs = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').thumbnails();
    -
    -const thumbs = await graph.me.drives.getById('driveId').items.getById('itemId').thumbnails();
    -
    -
    -

    Delete drive item

    -

    Using the delete() you delete the current item

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -const thumbs = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').delete();
    -
    -const thumbs = await graph.me.drives.getById('driveId').items.getById('itemId').delete();
    -
    -
    -

    Update drive item

    -

    Using the update() you update the current item

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -const update = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').update({name: "New Name"});
    -
    -const update = await graph.me.drives.getById('driveId').items.getById('itemId').update({name: "New Name"});
    -
    -
    -

    Move drive item

    -

    Using the move() you move the current item, and optionally update it

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/onedrive";
    -
    -// Requires a parentReference to the new folder location
    -const move = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').move({ parentReference: { id: 'itemId'}}, {name: "New Name"});
    -
    -const move = await graph.me.drives.getById('driveId').items.getById('itemId').move({ parentReference: { id: 'itemId'}}, {name: "New Name"});
    -
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/outlook/index.html b/docs/v3/v2/graph/outlook/index.html deleted file mode 100644 index 8fc6803cc..000000000 --- a/docs/v3/v2/graph/outlook/index.html +++ /dev/null @@ -1,2363 +0,0 @@ - - - - - - - - - - - - - - - - - - outlook - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/graph/outlook

    -

    Represents the Outlook services available to a user. Currently, only interacting with categories is supported.

    -

    You can learn more by reading the Official Microsoft Graph Documentation.

    -

    IUsers, IUser, IPeople

    -

    Invokable Banner Selective Imports Banner

    - - - - - - - - - - - - - - - - - - - - - -
    ScenarioImport Statement
    Selective 1import { graph } from "@pnp/graph";
    import {Outlook, IOutlook, MasterCategories, IMasterCategories, OutlookCategory, IOutlookCategory} from "@pnp/graph/outlook";
    Selective 2import { graph } from "@pnp/graph";
    import "@pnp/graph/outlook";
    Preset: Allimport { graph, Outlook, IOutlook, MasterCategories, IMasterCategories } from "@pnp/graph/presets/all";
    -

    Get All Categories User

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -import "@pnp/graph/outlook";
    -
    -// Delegated permissions
    -const categories = await graph.me.outlook.masterCategories();
    -// Application permissions
    -const categories = await graph.users.getById('{user id}').outlook.masterCategories();
    -
    -

    Add Category User

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -import "@pnp/graph/outlook";
    -
    -// Delegated permissions
    -await graph.me.outlook.masterCategories.add({
    -  displayName: 'Newsletters', 
    -  color: 'preset2'
    -});
    -// Application permissions
    -await graph.users.getById('{user id}').outlook.masterCategories.add({
    -  displayName: 'Newsletters', 
    -  color: 'preset2'
    -});
    -
    -

    Update Category

    -

    Known Issue Banner Testing has shown that displayName cannot be updated.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -import "@pnp/graph/outlook";
    -import { OutlookCategory } from "@microsoft/microsoft-graph-types";
    -
    -const categoryUpdate: OutlookCategory = {
    -    color: "preset5"
    -}
    -
    -// Delegated permissions
    -const categories = await graph.me.outlook.masterCategories.getById('{category id}').update(categoryUpdate);
    -// Application permissions
    -const categories = await graph.users.getById('{user id}').outlook.masterCategories.getById('{category id}').update(categoryUpdate);
    -
    -

    Delete Category

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -import "@pnp/graph/outlook";
    -
    -// Delegated permissions
    -const categories = await graph.me.outlook.masterCategories.getById('{category id}').delete();
    -// Application permissions
    -const categories = await graph.users.getById('{user id}').outlook.masterCategories.getById('{category id}').delete();
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/photos/index.html b/docs/v3/v2/graph/photos/index.html deleted file mode 100644 index f8868620e..000000000 --- a/docs/v3/v2/graph/photos/index.html +++ /dev/null @@ -1,2351 +0,0 @@ - - - - - - - - - - - - - - - - - - photos - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/graph/photos

    -

    A profile photo of a user, group or an Outlook contact accessed from Exchange Online or Azure Active Directory (AAD). It's binary data not encoded in base-64.

    -

    You can learn more about Microsoft Graph users by reading the Official Microsoft Graph Documentation.

    -

    IPhoto

    -

    Selective Imports Banner

    - - - - - - - - - - - - - - - - - - - - - -
    ScenarioImport Statement
    Selective 1import { graph } from "@pnp/graph";
    import {IPhoto, Photo} from "@pnp/graph/photos";
    Selective 2import { graph } from "@pnp/graph";
    import "@pnp/graph/photos";
    Preset: Allimport { graph, IPhoto, Photo } from "@pnp/sp/presets/all";
    -

    Current User Photo

    -

    This example shows the getBlob() endpoint, there is also a getBuffer() endpoint to support node.js

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -import "@pnp/graph/photos";
    -
    -const photoValue = await graph.me.photo.getBlob();
    -const url = window.URL || window.webkitURL;
    -const blobUrl = url.createObjectURL(photoValue);
    -document.getElementById("photoElement").setAttribute("src", blobUrl);
    -
    -

    Current Group Photo

    -

    This example shows the getBlob() endpoint, there is also a getBuffer() endpoint to support node.js

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/groups";
    -import "@pnp/graph/photos";
    -
    -const photoValue = await graph.groups.getById("7d2b9355-0891-47d3-84c8-bf2cd9c62177").photo.getBlob();
    -const url = window.URL || window.webkitURL;
    -const blobUrl = url.createObjectURL(photoValue);
    -document.getElementById("photoElement").setAttribute("src", blobUrl);
    -
    -

    Set User Photo

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -import "@pnp/graph/photos";
    -
    -const input = <HTMLInputElement>document.getElementById("thefileinput");
    -const file = input.files[0];
    -await graph.me.photo.setContent(file);
    -
    -

    Set Group Photo

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -import "@pnp/graph/photos";
    -
    -const input = <HTMLInputElement>document.getElementById("thefileinput");
    -const file = input.files[0];
    -await graph.me.photo.setContent(file);
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/planner/index.html b/docs/v3/v2/graph/planner/index.html deleted file mode 100644 index 07b933a07..000000000 --- a/docs/v3/v2/graph/planner/index.html +++ /dev/null @@ -1,2628 +0,0 @@ - - - - - - - - - - - - - - - - - - planner - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - - - -

    @pnp/graph/planner

    -

    The ability to manage plans and tasks in Planner is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described -you can add, update and delete items in Planner.

    -

    IInvitations

    -

    Invokable Banner Selective Imports Banner

    - - - - - - - - - - - - - - - - - -
    ScenarioImport Statement
    Selectiveimport { graph } from "@pnp/graph";
    import "@pnp/graph/planner";
    Preset: Allimport "@pnp/graph/presets/all";
    -

    Get Plans by Id

    -

    Using the planner.plans.getById() you can get a specific Plan. -Planner.plans is not an available endpoint, you need to get a specific Plan.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const plan = await graph.planner.plans.getById('planId')();
    -
    -
    -

    Add new Plan

    -

    Using the planner.plans.add() you can create a new Plan.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const newPlan = await graph.planner.plans.add('groupObjectId', 'title');
    -
    -
    -

    Get Tasks in Plan

    -

    Using the tasks() you can get the Tasks in a Plan.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const planTasks = await graph.planner.plans.getById('planId').tasks();
    -
    -
    -

    Get Buckets in Plan

    -

    Using the buckets() you can get the Buckets in a Plan.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const planBuckets = await graph.planner.plans.getById('planId').buckets();
    -
    -
    -

    Get Details in Plan

    -

    Using the details() you can get the details in a Plan.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const planDetails = await graph.planner.plans.getById('planId').details();
    -
    -
    -

    Delete Plan

    -

    Using the delete() you can get delete a Plan.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const delPlan = await graph.planner.plans.getById('planId').delete('planEtag');
    -
    -
    -

    Update Plan

    -

    Using the update() you can get update a Plan.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const updPlan = await graph.planner.plans.getById('planId').update({title: 'New Title', eTag: 'planEtag'});
    -
    -
    -

    Get Task by Id

    -

    Using the planner.tasks.getById() you can get a specific Task. -Planner.tasks is not an available endpoint, you need to get a specific Task.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const task = await graph.planner.tasks.getById('taskId')();
    -
    -
    -

    Add new Task

    -

    Using the planner.tasks.add() you can create a new Task.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const newTask = await graph.planner.tasks.add('planId', 'title');
    -
    -
    -

    Get Details in Task

    -

    Using the details() you can get the details in a Task.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const taskDetails = await graph.planner.tasks.getById('taskId').details();
    -
    -
    -

    Delete Task

    -

    Using the delete() you can get delete a Task.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const delTask = await graph.planner.tasks.getById('taskId').delete('taskEtag');
    -
    -
    -

    Update Task

    -

    Using the update() you can get update a Task.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const updTask = await graph.planner.tasks.getById('taskId').update({properties, eTag:'taskEtag'});
    -
    -
    -

    Get Buckets by Id

    -

    Using the planner.buckets.getById() you can get a specific Bucket. -planner.buckets is not an available endpoint, you need to get a specific Bucket.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const bucket = await graph.planner.buckets.getById('bucketId')();
    -
    -
    -

    Add new Bucket

    -

    Using the planner.buckets.add() you can create a new Bucket.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const newBucket = await graph.planner.buckets.add('name', 'planId');
    -
    -
    -

    Update Bucket

    -

    Using the update() you can get update a Bucket.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const updBucket = await graph.planner.buckets.getById('bucketId').update({name: "Name", eTag:'bucketEtag'});
    -
    -
    -

    Delete Bucket

    -

    Using the delete() you can get delete a Bucket.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const delBucket = await graph.planner.buckets.getById('bucketId').delete(eTag:'bucketEtag');
    -
    -
    -

    Get Bucket Tasks

    -

    Using the tasks() you can get Tasks in a Bucket.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/planner"
    -
    -const bucketTasks = await graph.planner.buckets.getById('bucketId').tasks();
    -
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/search/index.html b/docs/v3/v2/graph/search/index.html deleted file mode 100644 index 04968637f..000000000 --- a/docs/v3/v2/graph/search/index.html +++ /dev/null @@ -1,2264 +0,0 @@ - - - - - - - - - - - - - - - - - - search - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/graph/search

    -

    The search module allows you to access the Microsoft Graph Search API. You can read full details of using the API, for library examples please see below.

    -

    Selective Imports Banner

    - - - - - - - - - - - - - - - - - -
    ScenarioImport Statement
    Selectiveimport { graph } from "@pnp/graph";
    import "@pnp/graph/search";
    Preset: Allimport "@pnp/graph/presets/all";
    -

    Call graph.query

    -

    This example shows calling the search API via the query method of the root graph object.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/search";
    -
    -const results = await graph.query({
    -    entityTypes: ["site"],
    -    query: {
    -        queryString: "test"
    -    },
    -});
    -
    -
    -

    Note: This library allows you to pass multiple search requests to the query method as the value consumed by the server is an array, but it only a single requests works at this time. Eventually this may change and no updates will be required.

    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/subscriptions/index.html b/docs/v3/v2/graph/subscriptions/index.html deleted file mode 100644 index 1d189093b..000000000 --- a/docs/v3/v2/graph/subscriptions/index.html +++ /dev/null @@ -1,2333 +0,0 @@ - - - - - - - - - - - - - - - - - - subscriptions - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - -

    @pnp/graph/subscriptions

    -

    The ability to manage subscriptions is a capability introduced in version 1.2.9 of @pnp/graph. A subscription allows a client app to receive notifications about changes to data in Microsoft Graph. Currently, subscriptions are enabled for the following resources:

    -
      -
    • Mail, events, and contacts from Outlook.
    • -
    • Conversations from Office Groups.
    • -
    • Drive root items from OneDrive.
    • -
    • Users and Groups from Azure Active Directory.
    • -
    • Alerts from the Microsoft Graph Security API.
    • -
    -

    Get all of the Subscriptions

    -

    Using the subscriptions(). If successful this method returns a 200 OK response code and a list of subscription objects in the response body.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/subscriptions"
    -
    -const subscriptions = await graph.subscriptions();
    -
    -
    -

    Create a new Subscription

    -

    Using the subscriptions.add(). Creating a subscription requires read scope to the resource. For example, to get notifications messages, your app needs the Mail.Read permission. To learn more about the scopes visit this url.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/subscriptions"
    -
    -const addedSubscription = await graph.subscriptions.add("created,updated", "https://webhook.azurewebsites.net/api/send/myNotifyClient", "me/mailFolders('Inbox')/messages", "2019-11-20T18:23:45.9356913Z");
    -
    -
    -

    Get Subscription by Id

    -

    Using the subscriptions.getById() you can get one of the subscriptions

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/subscriptions"
    -
    -const subscription = await graph.subscriptions.getById('subscriptionId')();
    -
    -
    -

    Delete a Subscription

    -

    Using the subscriptions.getById().delete() you can remove one of the Subscriptions

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/subscriptions"
    -
    -const delSubscription = await graph.subscriptions.getById('subscriptionId').delete();
    -
    -
    -

    Update a Subscription

    -

    Using the subscriptions.getById().update() you can update one of the Subscriptions

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/subscriptions"
    -
    -const updSubscription = await graph.subscriptions.getById('subscriptionId').update({changeType: "created,updated,deleted" });
    -
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/teams/index.html b/docs/v3/v2/graph/teams/index.html deleted file mode 100644 index 2d96fae1e..000000000 --- a/docs/v3/v2/graph/teams/index.html +++ /dev/null @@ -1,2619 +0,0 @@ - - - - - - - - - - - - - - - - - - teams - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - - - -

    @pnp/graph/teams

    -

    The ability to manage Team is a capability introduced in the 1.2.7 of @pnp/graph. Through the methods described -you can add, update and delete items in Teams.

    -

    Teams the user is a member of

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users"
    -import "@pnp/graph/teams"
    -
    -const joinedTeams = await graph.users.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').joinedTeams();
    -
    -const myJoinedTeams = await graph.me.joinedTeams();
    -
    -
    -

    Get Teams by Id

    -

    Using the teams.getById() you can get a specific Team.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/teams"
    -
    -const team = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528')();
    -
    -

    Create new Team/Group - Method #1

    -

    The first way to create a new Team and corresponding Group is to first create the group and then create the team. -Follow the example in Groups to create the group and get the GroupID. Then make a call to create the team from the group.

    -

    Create a Team via a specific group

    -

    Here we get the group via id and use createTeam

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/teams"
    -import "@pnp/graph/groups"
    -
    -const createdTeam = await graph.groups.getById('679c8ff4-f07d-40de-b02b-60ec332472dd').createTeam({
    -"memberSettings": {
    -    "allowCreateUpdateChannels": true
    -},
    -"messagingSettings": {
    -        "allowUserEditMessages": true,
    -"allowUserDeleteMessages": true
    -},
    -"funSettings": {
    -    "allowGiphy": true,
    -    "giphyContentRating": "strict"
    -}});
    -
    -

    Create new Team/Group - Method #2

    -

    The second way to create a new Team and corresponding Group is to do so in one call. This can be done by using the createTeam method.

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/teams"
    -
    -const team = {
    -        "template@odata.bind": "https://graph.microsoft.com/v1.0/teamsTemplates('standard')",
    -        "displayName": "PnPJS Test Team",
    -        "description": "PnPJS Test Team’s Description",
    -        "members": [
    -            {
    -                "@odata.type": "#microsoft.graph.aadUserConversationMember",
    -                "roles": ["owner"],
    -                "user@odata.bind": "https://graph.microsoft.com/v1.0/users('{owners user id}')",
    -            },
    -        ],
    -    };
    -
    -const createdTeam: ITeamCreateResultAsync = await graph.teams.create(team);
    -//To check the status of the team creation, call getOperationById for the newly created team.
    -const createdTeamStatus = await graph.teams.getById(createdTeam.teamId).getOperationById(createdTeam.operationId);
    -
    -

    Clone a Team

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/teams"
    -
    -const clonedTeam = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').cloneTeam(
    -'Cloned','description','apps,tabs,settings,channels,members','public');
    -
    -
    -

    Get Teams Async Operation

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/teams"
    -
    -const clonedTeam = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').cloneTeam(
    -'Cloned','description','apps,tabs,settings,channels,members','public');
    -const clonedTeamStatus = await graph.teams.getById(clonedTeam.teamId).getOperationById(clonedTeam.operationId);
    -
    -

    Archive a Team

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/teams"
    -
    -const archived = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').archive();
    -
    -

    Unarchive a Team

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/teams"
    -
    -const archived = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').unarchive();
    -
    -

    Get all channels of a Team

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/teams"
    -
    -const channels = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels();
    -
    -

    Get channel by Id

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/teams"
    -
    -const channel = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype')();
    -
    -
    -

    Create a new Channel

    -
    import { graph } from "@pnp/graph";
    -
    -const newChannel = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.create('New Channel', 'Description');
    -
    -
    -

    Get installed Apps

    -
    import { graph } from "@pnp/graph";
    -
    -const installedApps = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps();
    -
    -
    -

    Add an App

    -
    import { graph } from "@pnp/graph";
    -
    -const addedApp = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps.add('https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a');
    -
    -
    -

    Remove an App

    -
    import { graph } from "@pnp/graph";
    -
    -const removedApp = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps.remove();
    -
    -
    -

    Get Tabs from a Channel

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/teams"
    -
    -const tabs = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').
    -channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs();
    -
    -
    -

    Get Tab by Id

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/teams"
    -
    -const tab = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').
    -channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs.getById('Id')();
    -
    -
    -

    Add a new Tab

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/teams"
    -
    -const newTab = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').
    -channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs.add('Tab','https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a',<TabsConfiguration>{});
    -
    -
    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/graph/users/index.html b/docs/v3/v2/graph/users/index.html deleted file mode 100644 index 275ad0991..000000000 --- a/docs/v3/v2/graph/users/index.html +++ /dev/null @@ -1,2461 +0,0 @@ - - - - - - - - - - - - - - - - - - users - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
    - -
    - -
    - -
    - -
    - - - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - - - - - -
    -
    - - - - - - - -

    @pnp/graph/users

    -

    Users are Azure Active Directory objects representing users in the organizations. They represent the single identity for a person across Microsoft 365 services.

    -

    You can learn more about Microsoft Graph users by reading the Official Microsoft Graph Documentation.

    -

    IUsers, IUser, IPeople

    -

    Invokable Banner Selective Imports Banner

    - - - - - - - - - - - - - - - - - - - - - -
    ScenarioImport Statement
    Selective 1import { graph } from "@pnp/graph";
    import {IUser, IUsers, User, Users, IPeople, People} from "@pnp/graph/users";
    Selective 2import { graph } from "@pnp/graph";
    import "@pnp/graph/users";
    Preset: Allimport { graph,IUser, IUsers, User, Users, IPeople, People } from "@pnp/graph/presets/all";
    -

    Current User

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -
    -const currentUser = await graph.me();
    -
    -

    Get All Users in the Organization

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -
    -const allUsers = await graph.users();
    -
    -

    Get a User by email address (or user id)

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -
    -const matchingUser = await graph.users.getById('jane@contoso.com')();
    -
    -

    Update Current User

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -
    -await graph.me.update({
    -    displayName: 'John Doe'
    -});
    -
    -

    People

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -
    -const people = await graph.me.people();
    -
    -// get the top 3 people
    -const people = await graph.me.people.top(3)();
    -
    -

    People

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -
    -const people = await graph.me.people();
    -
    -// get the top 3 people
    -const people = await graph.me.people.top(3)();
    -
    -

    Manager

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -
    -const manager = await graph.me.manager();
    -
    -

    Direct Reports

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -
    -const reports = await graph.me.directReports();
    -
    -

    Photo

    -
    import { graph } from "@pnp/graph";
    -import "@pnp/graph/users";
    -import "@pnp/graph/photos";
    -
    -const currentUser = await graph.me.photo();
    -const specificUser = await graph.users.getById('jane@contoso.com').photo();
    -
    -

    User Photo Operations

    -

    See Photos

    - - - - - - - -
    -
    -
    -
    - - - - -
    - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/img/ConsoleListenerColors.png b/docs/v3/v2/img/ConsoleListenerColors.png deleted file mode 100644 index 0b07afbf760012af5c9c2446d7e0247674e5b78e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4349 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 diff --git a/docs/v3/v2/img/Logo.png b/docs/v3/v2/img/Logo.png deleted file mode 100644 index 73dc570b38549a22788bd8911d7da860bb191ae4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3151 zcmaJ^c`zI57AI7tZbI!v-EP((s>I&dE1|g2EC>>PQA?vmQ?=cSYILD`Em8YLgG3Q+ zsW!HvQnZv>S{XFfqSmUYrJlYwZ|1&v%OBtQzWJRqb7sEz&F^>4Z8w)QpaV(=goK1Z z2uC}VkdUy>-us5Q*j_ZYQs3OWfR`OT!i9uny?-9zk?4bUM{zgvkHS}Wv6EY7>h5Lcd1|mi`O!L6_d)YM$MfG%_)-x+!CYiJE9~i9~U5n6DqPM2>%zrJv>f7IUVIe z?1II(ipuv<^6wcw`LK1COAG~<+}{4WT~~YUt&3I-7DaOP6)kjvLA%fe?5#cD1KkHnUW&3Sob8ZCV;1e(&r#hgHi> z9kiNOKM|x1d}QuX1%#8QK5VNeSBZP6EBLi^-gw6)k|*cdZh!B^Ad9LB$%;Sxtc~6k zC@PIehWUkTjKfVF1Mi-fDGBEc*tY8OIs{%s}MKPBzmR~mSVr%o92)fGY(?x3^ zlNV=>Ekq=yh2^+a@xOntFjvX3%#-PT+(5|rqV_Zjn(ouWY?brpt<)y?&*@La10RQW zZq~VbiwELvEFeCT2zgGLmxSr#)AXM3K!J@(YFYPpDHN1ngAdHkzv60iefP402e2eV zwu54=*m-M>eZM2@DSCP zObW7%xe9K|V#aX81JmLik|6hL8!9$`E7`8oc~za+T>P7|9-b$=M6wu(mq_;sppwE# zfm`-2N-&q1zf z{2GC)?YsTH%_@H071(%Uki(Ke5k87pzlVm*2imvyh(A*3npr6Z_q?%X#%SM2GG<=q z7K#pb8;D;Vsvp!6oOU_UyefBu?4*$>kA16tmENjEbB+V|jhOAP z{ziXH)W_GY*UBFGPjN4NtSqsG;L(S3l5GXGT_Cw-Q_L#IBN{)w8BCm_oxmSs$2?Ug z=|<(Tmmo`etbtN8qG>%m{vdNgbcxQCX(WtFi3DLpdFZi@Y(Bw`_vZxg| zO}>%Tnszy~my|JqmSv&y?=De!b4tZ6tb;1b*KI0KlMe_Aw&ym6LkrA_dIxdn1j76CI*401aXq-`z#e+ut7Y?xTnIMl$(C086e+IOAn~rfczNBX z;*AdyqVlLo0|w)*6rEAZnmN5pO4g$_+XT_T)Pbun+Y@a>qLgjBR;UeU2!&92c?bo} zupVBY6!cz;J>Keu0by~kRh|_ObwOlspVPQ4mYeE*uL|JwzPLpL!?p{&-luHEf`zrJ z7z#d~Xri3+%#!!ltVi6UE3cmdLWYRCoW@MHxQ^A5^m}}W zr6hTFM>K$y(0pd>*dxydhW<9S8So=Wfq!CLaBflcEI}B)phABLe&y1lT|y2?+Dp#p zwy>jgbz0L!0TN8}lq0(h#q1YpZYv9CN6oWR(UoK<(GiqJ;f)*E`_1wbKTTK?F6oM# z(x8jj`c4_?8QX-`{BZ`Or~nIMJHM-5_L*;#^ezjdnB}?fLsa!XH6#k^lo5{U*n1<6 zCO6$~c0N0K%6X&%+WaEald0HclfS$7Pbt)!zi&w4o)Z6?j{je!e>r(!`;6ANrO6b4 z%~}MbAAXl-|I-c7Hve>!8_fVI&`9=TL~Iir9;eA3*NnS%@PAsyf3_mcSd`8_j7;)s zTxwZvgk7mRWm$`4W}t$`i3PF0(>m(M!)c(g@Ci0Fn$hoBd%Qeytb%XpFVA~wP(R2$ z`9%!?0U0BiOw=4Fmrx8|AD+4J2J*vSMwILgVJv2#eAVh%wcNF5FBVifSG1$k*a>3P zjC^G+a({-(@N0N6A|UyMzVjYs?L~l<4E~ZbFzYcdTY95qWmm)XAgj3HmBpy z@dV}*NQ^^`M(K)1 zyq@^5K6)1)Zy#|C3L4<)MfYW&>w?kV<->^p z{vlS;8b(2TD;q_xyfW}AqSs3l)08w7)Oa7&;nOZ7cX%qYvGQ}I=4jB5^|Jsjt@5QQ z)PoEfXwg$*UNA^1{4yeyODaJP))#dKM<=D0a)QC*lwfhNnsrU84C^h>0Fqwfu)6dD zmX&AAdGlsjb-u3&9^f-n5@e!-9#WS(-|SE4lA+e#ql2s0AwcWlN);*GtfFJx8!w*$ z%0yzP&taF#q_X%1x=&MI?wZJVm7LU+Zqr=V;QX(8ybE46Xla`gDLuF(xJ>$WWpWHf+b1O|*LMZq|8x)#?56}0SMBxIz8U(q8ovN{&jS{11Y{c&0Dx9=@BCpm^AHtC46X*i<1c+ zj<-HL6iF(ryEMGsnsvB36E5xzu!|U1Rb#^#iAx8^mmWi3q3#j4gKWqOG?^XHqq}d(WHCB@A75PG9|r2 z)C};5K8=Mwc<~Jc=?QCyXk^)+R=2wZseL{h2K@!QoYagwgAHGDF8qpfH|WrbR^f#t z-px1~S2f!!Awtcd-7YknI)9qq=vPBy!3^A+E#%C=&XV5W28R*?+_mPbi`!#O2r3x! zQ+HXff5eXrwf3z~?vk}THnOE38#h1uW6Lpfk+=br2)-O2DfM-2_qH+*z>v4gbc?C9ar=R{@>k1m4l zl}5K1E|@9qe`;{ zv-S2Yz$>g&4lUmBaFdv1ne;F-2v_M|qivJ3Vxq?jzuyy<<>xm%^J3?_UqEj6uCKod z-y55vpWJVZ*1~2_cnh-7L5a4nnZd)_t--Qq$VP*KQ zIr|^V_*cNz*l52{ydCf_H4~Zg^A^I*Q<{t)66*!o?_n$Ob9)rK}4K3Y1O@|P{CXdqM&0D$)w z!!?atH`P?o8dBmCG*ke}w+?{7Q0ZbZuC6=f;N34jX1rA;C>2S82_9jZ)?@7~no!6f#@3u14 z{W|~MOH=?ow{B&@P_}lQuz62q;ro1awr;xao1zcJ#;+Zwd)^d}zQVMIaqCr;jK8*f zUu51}RXSOXo$pE4t1g?a_uO8Y?yY|Hxd}(Za7Mp|G~0?lC1TN6Q$F8G;MdF0udVpn zn{>(XQ(x`t#dk!t@G}N=mCNr7ZWdeg*S%RCCEf4MFsQHkHd*7g`l-LZdgC)WfZ^=5 zhMKMUt~il<0}Zv?i-XyEFR#6=`>{Iq+VS(i+xoqYnbwo=v)3CN4z`!x7vFo=`1WXT zbH4ZG^`^$3NBi5WpWih#{rUv}3(nwxEOs+~kP8Jf{!rPanE-^Q;A|lBj@@h!n@z!N zFqiw%YzR-V;2i#3g56xGU|zvom}u3~T)0Gs;CzJiN4xn*`Gtb{D5c${`Dhea=nFxe z#r{i-_JzVPvHG&hU*gc3LSN%=-Le1r!q}$pYl4~k^4CO6u+T!%g9Q79WV^h=g%qc% z<%LvihtOi$>H8q!VusJ|^5RPzSa^vT$l|b+iN8>^locVnvXo8G6kg7WzvHl+ zn`Be8oR{XlvYby07G5dHNpM&xEXXTbDJrR2St&jtbqKGPRDN_=Ev;E7S}kkXU0Hoa z28*nb+E^Ud%DXNUuT}KPuCBcv)D-zvIdaGG+nX_);%`+`?yKLbXM#o6YrZ5nuGcQ* z6|dK=RjsbqZ+3`mH0*qI+<3dcP`uH2yt}&51fUh&Bm-HUHk%;=C7Uf!xwXw!gqG-5 z8`8*WtDViZWUGVAV{NOGCqz`L+%v)Hd$(YI$@d=7>b38^5}l&kebS>&+x_y3CEEi^ zdu!Y8P_$w@gX*l#J44z6r8~p=a^H5|qqW3-jNCGE{_(-sw)DqGGmmdSMlm5`yJHU$ zop;CW@=JFooT|U=PGUR7_NE?>I`2(;E|%^!fAZP;w)YuFE51Jy$og=97B5h?KNlgl zzCTaU5$oE$9WzdAWwtKK*{+U&ge>v(6><=4;s#aF*hj`ubwXCRaapw%W*b8O-u z`b28hE;3kQ(+}Zu5A|nZ0~jf5S(E`_2A~F<0ucB2Hz{9382@WwgTTPw7XT_|0Qj%{ z=qUT;P*(rV8`>s!(dK41u`aexpSnu*O9ck_L`DQhMTR6M5XwpmU%xJEXsBsvX?*vt zZ*p>ccJ}ka!q=6RM zhq^p^wmA^SE-bxCvTP1Jb(SxQXxR#+rUj{cB4+r&G0epJhd-bX+7-b96bOq_S zgY-N>dR}J@ocYyF`P9wNsTwP28(lGcfI_LMs%lV%y1J&ix|WWPzMkGSef?_&2G_4$ zGrWHNhN0oj8#iv>ym{x=Eu%Yk?iw4LnwpxMnccg0&r*T?6XukUl<%@8)Ta2m^(NSh2AhgZ}tHPmj6@JDZ$oeB|H67`c7 z`e%LmPGRULNzb0)D5n7xZiLG3BUQjSRnRmzc#6(%2om^_3O@@7Uj&8CQ4=-*F?&i#@=LeZY$YK;qHekP55h-uo$iwpnA2#b2?N+fN#Q;c)(begXdefdK(Q zfq}un!6CuH_>hoLJf538ygoF z7at%0B0fIh#f!v*1j?3_l$4y5l#-mBnv#;1mX@BLo{^sZG9!bs5sAdi%*?E;tn93; zob2q}oSfX;+`PQJ{Jgw^{QSa#g2KYWqN1YW;^LCx;?k0m($dnhva(mNUXe(oauTVc zqT==I*Oir(Zz?ORs;a81t7~d%YHMrj>gww2>l+#x-oAa?*x1{OWvs2Oefzdf8JnA%TU+11 zf8XBOp{%~${r&yJ!^7j_W6J8IB)R2(mgEPL{{KG77j^t-{r`QE2hs0z|0~Je1qMt0 zE6Ix=TH9Nq2B{G}|44G*cOBgSNOFPnVrDYTY@|R)5<^A$ljI8`#^ryK{G^X97t9k= zc$+!Q^7Wr2mmPmv`6tOWO%5LfSjQOA-p;bD`kmxQC2#A0C;9Yl6v|S|K~F`*kdxT( zJIVP^B%-fTl6*>;PYB|W8#27iR&sCjT0csc84Can-{)(fT%Dq1K0W=NCXv< z`LBxY^5~w1659d`zhS9#Pqk%gv2i{tpd|STP|dIk#eZHp4-*t19x0(gbFuF+(+K`N z?1@#FL+O~+4I>+!k#Na@r3FiRLROeCO!T%%^i}F^C<0XQ*q_@E$freTz~WHwyl?!5 zwFyQ_M&n{d&iC`77qSYBvt4Ws*#q2)7H4!k^YQ{26xYZou$Z$%@L7AV-$~wizODeB zH)sWwOqb@@V```z))%dizG(%oR@&|pxy*&2yui5dRY%kuF#idk2ib;7>T_*&Nfb(N zLxon;m%1e1Rr0k`wQ+>6Q570{;RITDa2l5e&)p0}tB+W%iLHw%TEEaZM`Liw7-Fqf z{SJD+ok{~3rz7?5%UnhtQn4L#4t9>MJD(|#Bbk{Rg;TM(=sQHqPRc8(u&H3S?vaXw z^6T?=I59ybqNKCwg{vAQ7{p|ORDD@V7c^SM!vK;{R@#V~K0dj@*j|QcQ~(3q>|E;( ze?M}&?Q|z*_R^rL(cK`)+gzJ1;zUT0hU6YNDD<2ZkQX`>&wN|`L=r@M+nHG^%}O7k z!88BlRA=DbtvQ|CYKzEI+g1EjPjxS#Ko=viT121t=H?z^$iR4<`T>JMXx`eKnnY5A zv)gKiyk(f!HR!xLIu;P1_Wr4WOuK}ZASOJAUza(auN|<3mr&wDsa?#ltJh~~xO(I0 z8|{~<)`_8e8?sZH{Z|U-RKHEjA=)LF;*k-WS^m79c^0cQxoxD8wVI}~ zne81RR;Hl5nCT5FD z2LTqUJRiZUgge!a@@EN&?b2Y@po+OMjfU>GJiw{~!?Zi-;WOHtOZ}0RJ|GEyM2+P! zF3JOX;fx2e%ZlT@NT}7F7eHeazw9L{ zaZ%zXdy9M=D1^_zVIYj57X>O!vc3rNX)$d%A+?LbN$O4xdSRg6W|4>w;TI&JiK@sv z)kvxI`wBam(sV3b3k(X4N zD{1UT>6cr4!CC-9#b^X5oHaYV`i9p&rNCXFrqun8ui*o<#(3{oHIf zi;;|e-q*@-kJ0H)o$y%8f{hY0HM5L~3KCe53FGDolQCfm(4U!S)x>Klc@0HBw4>op zo(>Vi;b4y>$fSZGddr)=ChsEhADx;wp)zDfg(%^gX|z;2Wu(F*ZcMc@jRKsN0-2Ea z`uOQe4a#(1^iEAn&{oBa#~I#|2tj?Y1kqwpk~>?`>+7;-8U=%tD-lzDhgmg;gQ{+$ z-nb7HdvEOv4ZdpLE_%<`PGhwhzz!pX`kvzl(+-wCZP&Ay&DaBU;k6Jm#b$d_`^~bz zIjV&tByw#QN-G#-=Sqlp<|EfIFXo~Y(Tvj>qv1e2EDgBXH)kn+`7v2=YaSpc~y>gRJp2UN=UNs=PbO9p%6~XZHaqL|Vm(b4b(5`ycztoE;cabDFR0C4~OOd*`%<(@JP5 z6q(1L#oa%9yQe~Sw>exRE|sgxc()fK~|=c@ax$rM;lksf^29qe*&7H#^uV zZQ~2DPfAF?)9J-oIq)OTaxVn8MlE)W!wq*XJMfC9(k>>|x0%x+ZFBN$Mu{aha zHIt}3;6CLtP+9b%^kI+)ECm(qSn`g0N*;A;F!R1^(lN)Jyw<5a!$>cr6YJDlEm5hd z=yi9ww+x9L$|ol=uz(IS`_aDEm4{668zG<1;;;U+WOa$>@N7N%i$Hrmc6ceXfcgwr z8Fw^$@%fKlBz|Pj3y*;~NfoEVRHgEcX%k;Y&jovh=UWz5GFzB-X`6vJa{S=(2aq|kpUfqXD7Ct z9`4Qony_I+py&%PF)U6WkE54IR$80ZSR?D608S^-L8@RfGmxDbhzc*1UKTb=U|2*m zCZpgcn~d@9039B9J=tHg8kQJ&g@fon#me^je%5HFng zdEU?+3&dj~P9(r}dE|(-`B<*`)K~MvMA$nt%__n+7w!9z*0t^fXqQNDf`FUgU>`@# zX~B4{POJ(qrj!M9790{P&!OfSj(Xu$ss)MM$E!+uYxvzeY`=E`!rb-*)RJK-05~X) zZg(a~%?~zOh0T(RoBkf;>uiJZQ<(1U6(i znZ6f?WC^z9uwmd@u;c{7r)E6TM`81X=U&EgVcmqTe&Ym25IXV7ra>CQ(q~zqV@uaBbTZF=7 zB6~{K<&>PeDS69@Y2(QnKa%suQ_2ofNbITQ$tgwbw@QRkYm!szYEv7=6D#kgDn3bl zdpWJ`ZdylSauZ#e-1)T5@w9=1w87fM^LO3k65SN@(-hj%hE8hJr^eIk>2RepQIBms zJo7!q>~Bsir?1pztkIY%XmxP*>y1)YF>GEJsE60_$WxIo);jF z4Kn%!kV$~+2q1wrS;R4O#3DoSK7462M0JmF@<2G#-m3s#l(8`$-ozHYa~auIbI$|| zFXPFbmq88+Mo!DZ%@mMzxQT63%)`undG9OFF31snpd$x)bu3t{nJ<#KQ~&Un-VJrF$Fn=fZIM&xtUxc{-FXj zS+2p6QKXgev=dtG`dX_Q@T4H}?DA{q>B<}4dEvSqTL)g{dwIK0y$@<%{z8|Y&=IEv zeA*V_9jp$t6L7i}28IbD{4i|Bfe6ukkSr57QXRGodeb2J#)l!tu_#l&Acp~xH6)vL zJ}mzTmnGbj#a`pb&6a)1BwKC;ez`8&g1U&Hmo2;EUpxlaSq$^?Mv6>U=QyT0hUNG( z1iks1!wJm2Q3t=}#dzm1h(0ejeI*wYW@Z5hCpy%dALd$&`eisoW$GcbP3yXPQ}cTR zoevAJCgzXA%%5;nf8;>EJgEuuJZ~=Q?ed7Kd#*o%y_J&!-c#X?VR7&LYX>IF5ElYWA*CJBS_qJW~Z@X^R z&MMq4W!-M#lwugs4pwb9sWB@us`nbs_k&nKl2dU>28+z zNU3wsu+zh*^Jz)v$v|_bFSPSHv}-`73$N6ns^TUj`dZqNGp#;M-LA_$wkrXeG$WKj zxh15P$~g)k$2YJgD)so>rM!RlGG1*k4S%V#{04LMl1jG<7*^$0OoXlw!*{ziwdes8 z@F|j_Q*nHaez(7V&z<9H5L-?3VJ4&Y!(hj((9|rEiL5AwY^kE!IMZ4sVPqW_G87B% zC)NrsOIi;*&;lSEJTeW1LALLa8a)p($N>c$yPQ{S&%AQ>^ffCXg1ivl$#bH7H=vLc11K zEF8VlMP|E0Q=wb@fs;UUx4B`WS;6Q18N6pI&M74oiqQqhV;Fy6K^M$Na(r5ZM3Ev> zC1TK06(-7&{2*T#Ckt)6=lvm2c#sysFj);B0AM(|pKlv|wDIi@I%X&8jRmE5g?wWV zRUQdf?ujgAi|!wcaURQTK&$(X=PHgTb91EjkNd8Uzck>;itNfUpIGvo2uPbKie$^| zpHNwyc;(DiE;cEqH~GeZtvYg&|ES`_<=3N^IV`a74+tcI0B<5q@~=(Wr*&)eb}yV< z=9qj#Cr^Scq8{`qBj0|T;&kjWQhaIReP@pyS;{qP$=OTePz726+1FP&EB1}Pg>_?M zCe4x4-=>k_eVi#dy?r>|!#;d*&HJgEiz`n?MQfjH*DBQY(~{_BL#+0k8xA8o5T$*3 z!tbtg2!u35erX=lTV9xu!;|9s0wnbU)!SRtLJ4_HJw+@f>uFht92*Hf`Qe%mDQ#w zt(J$4WHDrv(o|Lqbf4ruejfJu&SLYkwNUzLT8WI|$jWTPVR#1o6?*x@!*3zo`s^YCW$6Zd!N6pA*1tqG97oOuO!keRpZUnyAFkI= zn^8+2mCd+*{?r=dODueOZR6LqI@W>A*Kg{!&E9cE)$Bv(4$bQMKb60`moiwN`uG(V zz6JP_a%G+Mdd0d=P9H^$8`TA2BbfCfO{(Gtun)H4A8P9sZk!BV7x`j(^^M)z z#Xe!=z{*Dn=+ZZfUAOeopp4e3@b|_|lO~^ck-BT`7Tx~v%D{X3Sw8!SO7f$2_VYJDX?5Mfp7z8{?;U}{xLox_rN4CqUMyeY z8_fMzM?f!C)hFzqWFh?4s#@8De{=*YZMd_1|IrbMa*_x)yY%cQ;jNv#(W^flfeXER z_i6-l*RAKq%W9@7N`?^z#y?}Hyf!voRl5K z{Jo0+qUkdR05zj%&k@ubmma)exR6Xi4q;ES;@(qx*)m^fp#72@#%jW?*)NuHW^B^*pBK!)ya}JYZHKWjG>G_!x6M=Hx z4AW+SF6l@B&dMgO!s-54#pP}CbiGy8VV-JVUz^h$q6PMq9L7t#w1QQ+G%-joVS)gX z$Y3TXlDhmw>FgFL&Ck~y3Y-;)Hs!abzBb;hZ4P=^&aUz7tKn8H9@I&5|F7 z(+s^1W{cH$|2!YIJCJ=#iqF_LXMRREO~Qw<Cx#eIWgxA8@Zdp(Kt&JXnP3;x0QBs<|eQhTuFh>LXA!sapS4SzHoqI>_7 zNPeeMi0hkFj53PJP9^Ae*Xy*0=kSQf&2*dNkJ8DNhSxs;uB$G|um&QEbl%vE;ABI2 z5Ikfm>Y0)Jj~6mkO#~x3bhC?ecZyyr`-{0q)PT#jk_v155<8N^g@FC1QzcG}`Ogpn zO)%8qZvl7V7lXXa}FfVDsP?{sT4MRd)SJO z2mnhy^9yA!Xr`}2w@NrL$;47SH@2b!eBp$kd)OA2RN~#EC?ZbB>=jEPe#3V=yuI8+ zg;4>av0=umyzxo6L0+|}vB!J7pe3T?i{F-Kd*J-%h#nppNChFJr1T72t5Zt0;z`~v zflq{8UOVASgx|tzf$ZRJm}|ug=Tl!9WWNBchl!u78rVhU)O#Of7@Q>bo)3_u>A(IW z>rsm2eVbt7(*YycM%2Yc8-IP_0idDk%dLncg^OPX0>%4Nwni2La8GXoT|W>{m9nax z1rEgz^kwmWSkw|()t968W6!!Bf~*wgc9`jud4vVJJqS2EIRn0GG2~#;pr)wNyK_U- z8KJ*AA(bloBGtB1Jmx$NQdSkmJ~v3kDK_sNMWPjrBE#Hk{Lg;CgXIg-qi;c|xTY}V zS8*+?{`+22p>*!$!M*^!if~#DB^31xnpWHs)AXE%_EJGVgTcr=qHj!AF(W_NsDYnu z>56VG-J|tDPR(g<$E5mq@v`R%XBb_=Tj^nBoY@S&;;%6|#S@E6Ixh_G%*Zp4iiKaa zk3yOtgOOV6y(m_ET)%C?l-VL1p_ub2nc*p-m5M7S+5&g|v(UwZZcy1HnN5FgHR1u< z>WW+8X!`l0BY{b|tm0>0bAC-_4BR8cY{>^!(U*?V5!Q;#ObW$H|tP;s-Uh zCxdPq$L50X=4m8u555fP;$v)iI<#@_d)dXxjJSr53EKxhK=!_CuGHTB`B(P%KmRPV zy=Xmq^>}Xq_H+JR)=|h5ii=M6ih=f3o#j!k1E|pzh2)ca*idV_yUh4^a)Ib2v%AO$U`Gk zwF4!(m@aw-Ir&)=;(TugKz{JRu%N4-cwick%PKg=1Hx4;HpYWN`d|4XP%>i!3Sn&= z$Pg3kSP)dL`{4p6PA@ScyazSoU|2i3j~&UJOo=tjun9INs-fIVZLL=mHFUKcO&%~S|2hkFB(F{5*?KEv_sy%No31}l^bSP2<4w2&fm=)f-+_kz;(-lc zqG=((e^K7N=Yp|9i6_K}bO8_ZE`%J0@njSJjo^1}JCZ-d1vE>y%A@SCndo;p>Ebfe zEA2RO9taK#_T_=3EK@7j%Jg(N4S=18ZK9SD{vjxM43=hkhEW9#3h{_}hh;1vF&3cU z4mdi889Hubc%TG7U9E^Q4!(a;J#$ykF&LHf-1JHDt-{0;Mln2%~82@QJ=_?Uxa5n@bmmWRpU1W=! zxceMx30g*neZI^*dpY9^JH(CzNFR4&O?2fPb=^lHcs&5`aO$Q}W!BW&2agZv)Yd20H4be;5aoal<|xQB3cJ<40NiJ z3!Eg@Pa2BWBRv{&sYeOf z&=E~+6FKwoVy1c6>9_+rk9KPDiAzD7bWJlEmxiC*V|6c-aJQDbxr$<>xf{DkVvMlQ z)StN2kwm|Tpr5&^o`Qp4DFk=O^B-QY{6T_nd%~$-GtTe;i?Hxj0-if8ca**G3rk$Y z9Ynesja(L3dhoJotH(21&szbE4rtGiYLO8FJOjmVECD;gCfI0|1!!m6g@#@RFj^-k z1QZnap=5f{30t_-3uCYo%Ij!U1pG47?amU3#S(%l$(*VXqImjM4e3@U3C3`mk+a;8 z>ORu@^qJUlPN`^Ck#et(005d^rnD|H| z>>PsegbZ&-gfP^b9y4qnu?MLZF1sE?)Jz%_s~8yUJA z&#yKHoB9A_pz*qhggTMry25BTKt@>hr$N{SdiM*q2o*xnqk56sJn&us5L6PeI$8Iv znEbw%OzNT;uDkbv-lf*R)O*w8%%+?sk@4ywT$r^n>{gi{`=gkTFrNHJ;Nu42G_t4< z5;fU&j_EDB;na1d_ORD2clu76wakxtwXbJ@9vqxn-kWbA3!BBK4Kti0a7Y4bB{2?_C*Yr)m;Mq9kBxA$Wapm zgju?Xg_ypM(h}zt=n`s-2m`{#fm?(0&f=3U10P-2KVnP4r<6un%tqx1&75hYGLRNu zgO+i~$L6GBc8i5*Tmbbn185I+lR(qU1HVmJv3k8yauRZptJRcdTt;+UzN9t5pbrJT zs@gZM;nSA&?cM#dS&$07Q!=~-gV7L63heGOfPEV`UCBE_AV58M`L&Vu9 zJY#b1?L~(Ck%23shZamU8y!?GlMGEAj8`Y&(H%@pW2f$QoNoHada;9jV}$cpJNL%$ z*>~-{O(%m3V%>W6pA6Fslk-78h)|m+aMEDz`HKTT4BqVdzPYAY$~fy9O`!R@$r$C4 zC3CNr>dK5&Z!dVg7b4!ruwI2-oq1f`m)X^Ksmj0f5?yF_4>C4^RA0jt-vgP3Y8}5yTMdy@ zU$UDwSUdw<;9eNeLnx-eJ#@EyH}=ri7JuG&@45cgD=O4y;Qg=7MV`b)K_4=S3_m6^ z`mcUYe7PAZ-t>Te`L=IUd}C7rY%=)_+4`G=r8(#ahOvkYf1eYs`d0`^`>Lc@J z))TW;k5ZAEGO#yy%O$C?+SoQI1qz};MeOjjwZn(&dq?8mh7QL*dVL#DX?-!>n#6ec zeEXZ+%D84jj^D$GEE4?5UC7=hwDfZrz!f!v@(01Y&~fNl)nG>MD}ii=bLwHI#Vp#A z4CghYxRsv)yAfpV*z*R|D7G74brZzQ8w1&I+NcG{(B`Ej-(0+wq3nB-=6ORRD+pu*)VY>wyrR* zJ5)|YnO{l(E*n_rB7*E-X^Tfs*sJo`E%|&+KQ}Vyf5w2BIrofvCEbnT?>sWHcbf#? zAm5_~jZyD|VCdOMTz%G`{~*T6-hrID9ae*KQ^jfInbED0aBffl7uBybE_e5#GMgk!K*$_J)K{ynqiF_ zteNKj6uCD~F!&hxfMPp^-8kb(Z03=%tEsDlFV*bAq?7=C5a)eEHFaiRmc&`tW4f5D zGA#D_Q%o;xChp6iEK*h`a&{H5xq_z~uz78llw5NlMh&h}01u__iEN1t-$Qw-mE58Q zM;qKSnD&$&k0Fq6canMChIa;OsQ>6aKmh2st>gs2P_Y5BC9hlQC`(yfk$Hx?tfQkx zaa4NLRR`5oKdP&KR#%@X3f z-jWCSQB%>-_Wc{NF$a*Pn|@gNTWoxOZJM6vkJxDOMfdh^vGH~6id^!fBu50mj8sQ7Y!`6v~>OxxJd&r z1&Hzkuz)z~BtX81pty+OMG;|1iYg@~dYR%$NysQuR4HjmMHwkY3ZkW`QZiR`6)veM z%V{WIvr@kHn?kC|U(>&0{CCTVLS(hHbSX#XFBVC0oeWe={!Sz*u9K0`Z`aAh#Ehal znVa8JF~Z)_v^LjqGS_i7*LA+9?_yzr`Qt!6|9^F$?CqWYu0cI~==ygD%H7@bj|TM* z6Y5_Yl-7NmfeQisB+>b{|HIn>?k15Qrcuu@iO(@f&oRjq_38O@KgyY95>ig_m?#pH zP1b}%(O1W!1((Y26pty`Iumb`9pz9G?NCZ^EK9;xCt+)dPnwBOS_-`S|Gnk(m*Vs< z%jrK+oc^8P^dHGh|3A&9a@^?O<)+rwwzjt4eiOxR`k#K&Z^P-oDNaKa%V~J{{rmS6 z)#)Fu(4S|>1qD&-qYgZB86A}mY@FEPphkc@yc(1IZzuL8-I}H z*1t@sA3uIjWT@R;ii)^@aBx6TqK=M!v&)}9fBsJ`@n2VnzdoU8Q?&nE{opSF`n=Ux zSNVLx|3QF`VgD}y+G0%`+1meK0<_$z3y2zxg1-dlls5%h1^*PFU(j-!*}HYnjQ$az zCqJxqy#7mo-stpq7_9_ISoX8iqAr)eW`)O)mDa+B(tiukLGBxe(|-i$3v^aYP~zid z7RNIEuR>bsLLeZoUZXyx=~I>*>HkN7&IeAG+_kKerV=Ti`hM%d!|rbs0h*!zXZ>#h z+K^d^e>NhSF8A(9N%HVVBK6JiZ_CV{c4luqn^4ew>C#c9r>`;Z?k)OYq`g-db8w;? z0K4nbG)w+70DSzsnRMe9zG%fXNKTUSl0M&bFju37x%+Pc+II5A9X58Gv>^cC>*)t! z3K2_kIL^fyE59>KT~cS2`_nMG3Ie)w2$AqnMcH_R3DfH;ccD`y|3jvkYYOT9Q$7!S!Bq&m%=g_q#7JD=hX zI-U1M9s!*mynzs%NaH~ZPm?$4cgQ)WngY)z0`oH@EMv8`lQs>RUw%Cqze~(DkkrWRVGImZc!|uJ zLA`vyE>WNtAGa)U%sx_ximz|e%nfo|(D&!!aOzY7OHt~T4coAI zP1oHyQ#9kJoQkD5>gMlX$TUbFYzs(C32^RP*Uf0TYml7{zzgC{SCyWciLGf*ksa5Q za@UCO-?gOi6`evPC)E~!c2X8dh-9bQ!lLB)-SB)g?G?}2XcyiP;fl1_b}gR)KYGz< zGeTEo!F_*QK5FL~I{w>?s1`wQv^8O&cpo8sAqnz0K=^T*B6;QZG!MOK8JDUXl^a&P zw@$mpzfQDh5R0CZ#_e@5mAvGkL1tEU&cPQBKL`Gg?Q+)_R zP|pI$wq?TT%7gfc{&Wm%X0Y`|`kTjONCDdiz=#Gyb;ll{E)Qa|lEbNE{iz|zpINkr z0zv2?k4Ck2&Vk#w3ng=mW7I)>EIffXy9#OZ?Nn>PfQvzcWTYm5j$5gmN+E5ImKCl3 zaZRI3#-plYM517!+{LTGB5~ zK)LsEro1gkZU1YifwVm6RZl@EHILtE50oX}CoDa?PY&IW6McbKRRp}df`70nTeIL7 zE!sPqx}K(HqbLBKnCgSri2Z;YB(RjfR)f`PZ(~%Y7puQcpek@Y>l4>;wWy%83RA+E zY99*|btYX70mPdPQOXRwK%fSNB()7s$y`;R)468q86bov$7;;+Ad@^*1PSQjXyVSZ zY_?_=GXaS4dOHuKpoIh724?LFqIoY5X1I=Jw2s&=vD47LT#aeXEB}Br(N}*-(n2Iz zZ4k2OY+vTcC@V9S@(6L_p{ItZu1RDX9ZG5J4hs@sfUMK}^CF_+k^x&oDk1pId&2p9 zjrk26li9`;wXg*qrQ21-Vkk(*>qm!U!#6YS*yykTaniBbhfm7(Pz7oCDA^t3G2Q z(Q|8mo+5?9c<%5#!V9yzN+CWuj~KLs(+GPZZmrXm8lS_q386QKx1$n-qtL)}Yb1#N zVDl3tQ0Nd}PwWLxRVD*$Agev;Ei*pQ&NEB`+yAIyLWz&-2hRoZEBjL{O`CVl#qd5I zU#X>*;63d^F8cv{La$*40B2%>%#J?yl^S_ZJBx1Iqq}kC)L0ZSOAvI{Nq}%YcB|== zFd1=;Y5bF~o~NfjH&oXT&xolNW>{)HL4*8(w;>f}W%5?9fJ31gWXvhO=@uS+Uf{3f zI5-2jSr8V}_#?f|Hk;JUsmn``jHvD7&1@BXKmwqW=D_?N;o)-q_7f#f009A1x1Hq0 zZhy%HC|tRV&@2ZTNo1Mksf2nfA83NoO=P- zKckHZT&FsH)9lIXbyj9$!W;x2`9-9l`o2ON`v;Yvpo|Nd^!oQ^5G6@>UdnFb?{xW} z-kr?JG_VT1lQsk{Tq>>1&lNa_5cf&`_&A^T8n&k7iBBMy%j~|tQLe;Xrs%3OS>W1M znCbD{=;i%#_gt)9bIz=m5P<6h?Fx+HVVD=ZUfW|d@IBYghrt~mctPcaIc4;mR}tZJ zDxYge!jX?UwGQgG)!ud%XZrbzSL=ooqv9P8YXb_z{@t_Zjup4PsZ)EW^u`&6!&w@=xjeGEP&%f+EXEk0!@J%-_LsMUxEwJsmmqcVoBM)Yc{{?XQIWq z=8%i>-B0Up97o04so!U~gx74-m&$GQR$B{r={0ld-N6gDPQ@82oM+cOON`GcRNWZ$ zM|39CiK9g|yc=DXAsPKtJ%HAO8)?-@Ak)c&)VOeV6sZ{>@>a#eGIn(GMCieXXUX2OCw3S;=V%q2Vg%Ya`~AL z_pY4#w#S_G=$9uihKBVl)AQ|4?-#Vpu2@(#fconqgCX#{UDGt5(hnf^NyXrHNO zP>|P8rlbngu<6NC@CYd%s42+DW_)q4!tV?Z^HG-MEhcyC0Lh zXkcNDM4WIW1~twA2jgDP$P)I-2uQ(Bu%PNvvPTk} z<7(3;nxIGma-kOX2^ZbuaRcQc(1PcCM#g?7OTBCsFF0?=kj`X|13dWl z>*2$gt86NNZsuvnn?^XyLpo?~h&N#hb-ET-4al15YiuZCo9Up3WI6}oX`P z7cY4|4|^XLCYV$rXn->&V?@782NM7-8u!x7|7|nF%uh3Y4sV|?y4$MYlwV9s82z9G z$b07Mpkk3dSiqQ#W>c^$BRUIAUXClFR6(iO&o3@OqT=LAd5T2*3H^%D?>=a_Qk^^_6MWg?35FFS>Nq?NRR-N4?z>!h>X`BjGCm zo?S$BE{P~z;C*%&d+{qmEK?&@AIC>8I7X>>s$U|JS)sIYYXgXY;GmNfypV^|v#*#w zDyGz0;FY!PhLT)$OSOC5p(X>!b3)h~_gi6+x1{XD^d^xiVggsFm_iCjs}^~EEiIxB z`IL4E90a@w6RIF$5=((QffzID9Y)!m)~|>I!8_PVqN{!>$>Gs)dJ(!lRVSrx7oM-R zh~%a#qdz5~AHeI#GMqYB3UrhmZB0h6Lb(CXB2eRZi2H3>RCLds~EV=!b~bb zrP@LDl>47(4k$SCOB3H>Bdm$e=St&M+`!zT1I`fCJ2G~eEg`J|U*^ZA17Th++;AfV z$v`Ynu&YGAU5*4+qLkHHRc-%!ddaA1Mf5PP$dycOr96mBfG@E3N=wzdvJsO^zI8T2 z-xWJTQ$3vQu?+BGJSscmAAE0qVCsK=0uOEJiE^%jKEv6q63|%#^5sOSX6a*OsNO5F19*=&3hbjOlOPX;i9SCNl z893bq2HKngxmqW4;p99h1tX*ctxSMz^ z?mpv*As;`a?JoYxd&bT_Q<4tQHfht=caeACU8?~%uh-rJwb~&}CoF0}IvB|bIE+gc za)=BWt3`l^9<{$q(+Ss=c7iX^5t_lca!nkKjpnYX17tLf34PCm(rM^aO`wyG+B;{* zWZDrJ=!SyoGrB6LH^{i29Fl^V!!;ZX0F^M&OJwYX6O=`TcL2y1Iw}{ZG2@HxXI+IbP`Ep4-5f6V5RM_JM|~m3j)Z=bdGD2|D6>vm$^=%q4os zxZ42a%OFt9_i$|BuEulji{Xlt{=HTGdsT7WDexnFP^dlhBkP154c%Bc@J_bJ)ffQ{ zLQaw4CN#MC9$bGd>NTGCDv^Wk15gkeGHb3?yQS+j;{}ft^fi@_M#Q*i1Mh8muvdG+ zjQ}DJlgnm8HC_pw8k%dhyF$YxQ`!?Fa3{}p0*)ji8%-nQsu()1Sr{AORa%Cex|#;8 z{qj6x$mu8(8N`G?O*Cp{w)7?oEA#b#wCiJ|j{x8rHb(S%ucI-d8h>RPR|=t`-UfoT z0B-1v1Obo>Y`*QR{t0@~?eq>vq|OC929GVyl7TX&fO=SbmTr%~CUgJK7>yT?n;5n) z=nw@QU+@l0h#LOS< z1oWlb8jb?+&NYmRIc`5IVu6N|QN!8MF)ms_Kbh1X63d0GLZ~|JRLpuHhIDCUh>ZP0 zM7I`0Uy&~kk|7lpA6D%N3>L<}WE{ebSSMqb_5h!EeE4$x-OqN&I2AROuW<>$e4*Nv z;AMQ5I=~v`Ls0BPJg&nDJ3&Lf(;ji@cOSp+8wUdcC zepdCpLR~T7ej)KblbhRaG8a28 z7=Ndr^~iRBUEr86WK}JYxqrUZeZEL?v3?mBaP<1yyz5BilIrv5Dyqkn?-g$9*p&jh zOS!*5LnLIAJ~*7W6fjgXN`rb#UucAp@aQkKH|~t*Blj$hjV~eW7af!6$nX3ur!VJH z!9%rtZj0#IYnK;yBj*V4WA&ITtmnMO=BTMp4_aY#3MQ1Ee54-6ASRD2m%m$Hf~T=r z>V-1)%Nl!jW(h>e%%Ov5=%jks`-h|X^~}$&JKh%{cTRBmE;#rK2NO+Bfqkgty~$SH zwTL=U*6v%DQ)z2MNw}1I(X#kcjqM3Fx2b{iUiXop=t z*FtkXi$n(Ue#E0H#02W8m~fWd0-0C)!#e-wdR{%Oo{X8Z5%_X`!{_E5mCqX)`vL=Q z*9(gCxf0ZNck9J*mBC+IUZOu= z-u&s|@bgVt=kK-m<(lqzK9XID{J48|;4SG%WJ&2N3K=v#iW!>-1?W2$5cDQZ?j6OK^LGsdnC}2oxU&(P7lr@g0I^tM<;4N7%mRz#=rG?*|y`e zpVof;*;3AzGAaH752S3e>pYY)9;P6Q*r#!q>C=!aB_1}ItT5mcqEv|st+&PMPegEZ+@ilEHdyCg+sM_pTnYUzJ>+$@dlo4=dV5u8IB=r06q_st;yDuUHKh$R{ zu5)zJ!6EIVkdxQ76Aee`Jj6~u9#C)QEv3;w#(o(__D&Uq9}4c6IG;h{ zO;?kADr9f=UdN|fYa)Bs@${ef&U6PULw@R(s{2>DDJk8j(%V zS=`gMflonzNQ+O&$No*9va|Bxz7;p)4E!n!a$5Xu-R%xPR()@##sBW( z_of7X1UyI=R*cMj8&@#BW2(GhDeaC=bW`Kv%#Zr}KX_U#(ukwgrI{Pg`05#tITwR^ zVqf{mcC{gG_B}DoFUmcymsCy}Bs6!|^h5_lguGml86-VvI#PXmNVe zQn1v{RR~XuFSG6Rl@g46u`(_?djwIbLQzD2jw<}BKkNPIs90OiJGsqPFxA zm4j$$NsBG0BB?6<%G^t_9}+s!mi(p;S=H|;YqYCp#Xu3;^=0Hni;TT(qam(=FuoJ; z6!oRMX5wy;+^9>Yzx4u}DPfX5F=dk&lKSN78-l zRC3?;=h*Pn-adUv#Z6iKaPu>>zL49=Y0Ju_U4!m10$$N#_`oMdnAdz>R4QI^m`63B z$}&JP+D3gIVlX^X7HZ^i<7?uq)y3w!mw&2IAS%P{0u8lsdu-+SZ}oI3pR2R3m$P53 z?Gt$8ek06F2D{w~YPg_*_S~*4Kzv}ar)?rettLuxrYc^$sTArb+auX3P#re5f^?0d ziY6Q1u-3?Qc(CelLiMcppiyJFq$DIkp()Pd$pivuA^@@eD6ItbvkrF`|a|Z#>Yn`PDi4e8cqZ^9THp)n$p#4owuv&zwHuSgAtW-xbD8-|viiT`7Q}L^QARDBBvM)h8M~w|0;(YD7E$OT!}* zBFHiFPDQ8_J_P6OmeD&^okIAe;VgP=&x>2*9Q0Z9PMsVV0Z^@}yKbG;fut8%)n3OV zBVMFjca72)0$KYi0%}^_LDbXRKUU1^<>F8FNkeohEhPPbjZnO}ogP)$;DY=CbH^8z zDL0kw9`P+V78X3}VC&qMbZ9r|i`bCoQV4MI7?eupHRL_I({x+-RgMyrczxFQOI`HF8%Wz=ljq94`m`G>OL2{7KI}LX{`TDX`i;ZA?DsVn zKNp;WPyo>`91LRg?!(!Iuk9vsG-^Bu45l(f;iuU~wIyQ6U-lgu`xWBOwoicOtN{l? zpv2qJNfo8fVTs_6R~P4)YP_RB3n5N{x?KYcbc%HovW)H;J(Y`Z?sk_{xSl4(17;zg zHImnIg!FC?`P?K54Jsy&AQVK}*i3)vklS|?=RK)lF{h3Yxx^;tr=Q?SU*pzqEk0Z{ z5Bv7c+x%P(^HSt~Bg4BdIA!0QckGSg+4^u*?R<9Nb@rb6A1{)ZV{?3((;=iEiQkS1 zHr(p-jQ?yqrm?R+@5-eWezNLyZ=r9y4xDbY#eYfg>bP{F^UMt*-{-B_&PN9tEXKx=I~r)(onNs=)^nOv^?!k6N4mlEwasRB%1Y ze<@>sQx%5G4S)YH@rsd=H-8h#j{f149s6e}{7=r}uUzb%BJ4lISpQ|2w?evI zApE;!UMZ-oBj?`|!@DxXvmus01 zV@)>REK6Oicq^N8z9wc6`~ zM!)7Da8l5qR!a6cD}A|S(hFaFy4DMyA|WTLID-zlPc#=o2E@pc{JN(4z=$XM6*ta| z=%2{W;`Gd>xC&5s#8q$xJ|J?NFd?kP6S1im-dis2I@k`%)W-%!6(NwS?KL=&kd6Y`vq%M`ju-#2o4kGQqAD@+PjJ; z5k;{SGQYg|(FhO^6r^gyxO$_@q!(;ZJb_Ub>s`U9Ol))xB5p9EW7L-4@STZemi`Em zS``X5)*8#jeQsSB0POY*(t!az?M=`DDKY9KMuKa3|kl{>IV!)D9yrU5U)rQ|Ti);1b)qGP-t8n=duv~x?{(ApVZm|n&U|Y>Z;9k@ms=wWzO!$?h|+M~p_i@)Em!8H zGfj9@*WgpJbX-XPkbzqUgISu{=>&j7&?wfVl3cHvx;2t2WTK5POQ@*5gWo<1a zK2rgi^w_T2MxZzCGP0;$&)8n?AoS^>liDBvW=V_^V+YSm!=>lyuvbzbh@F{uVX+mA zj1yU^m$-vBbdtoU22X@qcFOOk%SiY{VQ$imWx0OJ1GbTZ)l?_~PK~y%^hkpS$;uw& zPvpM+LYGjUD3lY68g7GxstKk%;GtHRz2H-kbR4fTDItnR5e!i*G=4=y?r5sL&@Dqm zo43b(;s>rZR7c7?MI!eNG68=v7%5l@>hvl@xCGAXSw3VHBmkfb2$7&QGEzApigy6j zCGRwZ*e+xWFXL=Vfu%$eES%un$lZ+gtvrx&ZKPlfF;;ntvb#fe(EYRzqhl=a3GUHd zWA{yPPpjU(UHL_}Q}?sgCWm-4E)8N<0ki(`qNh5TT567p+cRVbpC^@JUQOo3t>Myw z4svffDZJ7If@PtQzyYEh6(ng7S0ADX-2^%iy4>?5OH0Ut-mZ{em1rEog5_GqfNn^< zu3C+Zr|U&Q@L`bX;toLrI9M^^aI4!w0`{~F1G{%6ItFSiAfk8D(w~jkk8cMFS4+kE z0m$mIr+_6M_S#et&dX*AdJVKIuq$y=$Snsy`L?O-s!_J zl?4xUMu=ccf4V8}FeMTzOQpe$e$@J@Zy^WNjnNj*lr3p zu-Pld^#Eoof(7&t7QfIa&txGavLj*UeRG8&u&_*Lrkk^_7K+`45GL##ZW{BqZm(gnrvC zEHdOIZ!vVJ98)DdgiD;0GerlrZR|x94@I<;z`mz2;q{dg}y205zdd7-OT;N#&7R(7}%ENF;sEa!K$;re-5%X@cHERD2HCZ@9 zdkC?jB-?m*yaE)#PIS3gX!Fd}b=Pqwf*8n}h6WOMblLXV2Z}@n5M!P9<-}X@O~TR{ z(3Le7LQ;%qVSS#8)&-ri9(~hyNG`T5VHQ&&=F1-9u*L3mm1g0Awr$5XOGUE1gN zTcVX>@!z7%%LNL9m3Lq@&{IdJ!_42goI9)uLp4sX3R@{@tyq$7JF2hQn6xHv?-4?5HT z)i$3Q?nDiI=V@q=%YV`Yu9|}jWqN6D>JZ>kW(kCz^9u26vcl4ad~c#L?z#?^rW8-W zm*9&4#acNF*;z;*h3~k!5#+YYlZbOZXnB&D9esm&<}F0`y*5%h2Zy1-kBh_^uf&31 zD79tC_$S2YXv9%BcZAO=SQg82Y$s)}=q02}C8YH3h#gkiIVDGtmiGetIFVwfYDsC9 z{Heo<9v5O5zkuHo5O+6Z#hi`@v5&wac_#yj+*mpNf~m~~rLg+s zF8)N3Riz?nfY#rVZ$C%7!%l~1)ymMBy2H&uTfqeu$2PiH;l`Orl_h>N5&@<+Q|g9H&xNJ0PP(6g22}H1ld zNJhR0w_pRIVuP(H(yu^FAl^2(Ijg}Km2mHX2h_-kiekUWW|n2QRpzobv*W}vj;@(` z9ohF)2#b<359yQRY^Hs?2VW+K{tDE}KcXUDN;0+rB zbisPCOhX`g=xo8Ztb(Ilx9t&$wmkc7W_fmOdrNHxxr4Z}Fqt&GlNjUJDiyhn4POn^ zLb3RmwDarwP9hF8E+srmg)Eib+zQjO1h8~Kss6|@a$a;(UT(`sa$QIEtdc~V6~I*< zuSOpo{b=}p{pj*=rbtIB_#||(7`win0(av5L=GM5DE`HA6zk$1v|@Kjlq5C-rBo~i z0T%-23pD$R_7}{I6iFiUGAv=?-QJhu68Bq+*oBv6uN1+vPA@hjxa5l*kth#@l#zKu zy;?*(^ULG;69Zht{J)oDU}eDuVnHqyVH4#Sl0?t9RD>E=T>f4$)|AbOR5m-=qB!GN zd7`8U)>)LKT6pWmUrj2!H?;b}hfkFjv`# zj(N*Cd#Jbi4)Rw1XvNKHkv9Em0Xz^R4Jc8nD9Zfgx7BxBs*x-35gZXgs=5EYvLXDI zvqbeF2F!|at5_OzgaLD7k{TdHCek!|H1|WeD8Bi&699n_6o;#CkNU}I)6k_%%*8|t zD313%3;P4dho?g3>00s(bQ@Luvxn3rpg@12xPY4X1&=5#!F~>{g%Y)%>FpPzA=la1 z6?{Mg!1t7e-DDWJf3KNBir}ko*0kH7M(^y`!JwTu*@j0@aa2$!fW1b9-KFY5Yk8Ka zyu$VS45A>jfv9aP)lh~>3zbKVfYh8aw}oiz{h?97#)RW^Rw=x5_1Fispho6>sevk@ zW<5>f=!KJpN(J@jJ&WQJbG`H&{TY~0B2Wa}*rT#*XEg5$QGbPDAiU~e>wn5^_5QD< z9498|EhVdgfTqq2@6$4@yB-F`HNY`cD?)PmaAbnFKy)kYNW3v@?<4ZHN9Wu=|I@H$gEJ3cY8zMLbYPW95PzTUAO<}*jTh+e1estTNZ7vV#3wvLf zNnr`Ml?EQtyo(ByNK`U54FH$Wv4@qQEN)VgzE4a7u|T{#I)EWDApJCK76iUZf$z6z zZ2y7@#_6okbtfJ+?YEAuwLE@aJGKFWVvz%t(v#)x$L)I}M`hJw!qh}uw6MOs(=03} zFgYhMzp_fH$VF;#0Jdy%_3_1KCji_*PkT+kh`d6rGXp)Ga3xkSBN|5M-mW*_I=H%x z&y-SVApHjsHYIH?4&C)0Rsj6Re%#O6`=fjKRt*x=E43A0UFdf5yOd&GuaGfnnR(5x1|4S6cdI2m z#ziGp5c(c4f`_4DOv9RlG#RW?soT&Dl28U8woLE>E9fHCmR@m4dc)mm} z;>!13Ag#eC5rg||Q3M0@XkfNMeO6Q8vvNMT_&|RMN;-#z8DwE|XoF*9D9$Q$orPLo z<(`fTu?EYd85Ss4j+%F=<~OU|%sPEv;8uKP5-Sk$wW`%oLx;fndi# zOnMg=g5a_5#6C4jpTc7t*;>OC@Fn&uJOMF0X4ydJ5u>B#2?oNGs98SfJOe`{HeJI( z7pR&r|Dt6)FA6~H@s!%dL@U4My##RDw_c{M9kqGd^qUa zDr)8=L0x%=x-H~w@De7r_VpR&Yxb*t-K%X~Utc;0b;BgoxIhPjgM`o4wiW?sZ2*&w z^M8v|duD_wU~44M9WUXqGN*_f4@3@0k60FPN?VfP-CB2q9^fbSXw)xd`1Otr*g`0ga5uG;~Q zf#@HBd@XP83u)eTA{#uio5&$d>o`uI@>2QrafGw337aOPXdcZvy*$g5T$9Y4wjD&i zZ(X6RdtL$1M`)tQF|FQOz>YKh?#Nz)wkgMOQp9ol+b1 zHcbRJnIU(Rj=98wM3S{dqWBsExn2p;G0ppi9i{9{kkTihqh4#-;yUqUTrzy^r% zXO-Mx@$H8h24d0(3+f?D9UFHI^@ra3^0EnV_QRkbUaj%_C+!|Wrnw>*L1f_;oDIyQ zE@#;aHtrxicVaJqqQ}lwk7@MI8=daEAnlxcmX1K)XAmI5^*|5t9iGhXL0HIfHNYlh zE;4Ejj8P+8j)#R>VOfX-BUYFkz8%2yEW6=q#=8%;@6z{-h)6@57!WZs-&5NAD7#_f zKm?nhnPi6o%VJ*!LKN62#1#zc0~@;!CPpab*cnXPpjcE$R9j#bw+oWV``TYk-do)*e5{1JvemKDJXXHjE&8Mk+c2_yYC3I{7Q6nv zgM-6Pr$U$v>~|vS6>&w`#oz+r*k)j;TNHHHcI*Run{X6#1&0)h;^Vaf=*`dP89W|q zsLw3yG9KP<24S+WTV%BCQRr)WT0aq@D)DVtlh{VaIuS6`oxD*8Ha9~z6CZEJmW=BW zxayVBWe-Fshl1IvJzjt>nqOXjbF$@2Hny>r2o1s%F|WHEfK9WJyose8!uPaa*ruJq zi!3bO|10o0gln;`%I^(NdpZ5C^2}WK6r1z(lp)|AvjeXT`!C2sL>9*{g+FI5`OgG7 z+8l~l1p^s!R>$DtD9 z*U#A^1Uj9n5AJefimkHvC`(aBuw;H&<4E()8xO2r;?0tM-nZQr*O|1;l(Ua=8*v~4 zZD5ChBgXh;=V-slFqc5KH#7Rg#NN2=M^>j}gAq>i zHkhA}3OM-qvVY#`qriED^F)T%uH$V2^??(?Svta`rP0eyu1t-9nWxtc8w*F2-^{+g zeZ1%LQQf6Xe6(Yv2L0<9mfL-YK9Pm(*=H>)nPR-iyvOG+RZ=Vuem>r6fm7=~c##tD7OI5!M{t_AtfJ4Ai< zi)q-Xd1fYeVyfP0+G_Nuo-$k}8zq>DWZMR2LZ8-w_MRl-IgE^;7p$4=1cRoyoG1>8 zDK9(XmGPV`Z5*}ZM?{cCT32t3w!^cQwHGESv@bKUiq5O;*e< zHBcs0ND9nj=ie789hKHwT&dSC8o{oD+1Op#an=N*25WHkl+(%7eOE&VJF)KM;@c;T z`DbB{o;s1+4odeQ%HF4DH1}GTpL^WRa+>?d`uN1*zq7aZskxN_lJ!$uWY#+sFB&W1P0F0VUq~@ZoC+~K+-RlyKFZAq^sn~kk{rQg3*%tjhj%T04 zDnf>P_9^Um(Pv|^r)$9Cz@t!p{xka0F!ghfHhK;GJ<>eZn)DW@;PuSgYY_AE-gL++ZZ?(pHf0wk#ZIf|g0_LRTKJVLuG5~5sd z2LGJU_14P=qKq%vaWdVrhm~u!UpY3FzfOj08Hesco>7JNzP=MKe7~r9&{9jUdoSEc z+gZGn`x^i*0c$GUpP?4AvT%an&sntsCy)|~W#x`nqhi0>Q^!+uCqw*%^LisEI=~W5mrH}W+o+pp5MftTRKS-;+dvWoV7h8lRTZo(S0dGITJ?G+eQj%Zb|!Rzq+$-yU}@*vpM_tsYF*D&x)DB zEAqR2h}s8^*L}+_8xlPXa8Uw$08GutI^V0-SY(3EOH#bIBb^xoBNZW!%o)4HyrF28 z7NJGi;2@}pm6i-KLnJ0m&;bN(rzkN40rX||l$ch6rG$}XtP;q0X9M`KKd<4~sPN&0 zA(ct~OvP5YV||I_ENS7}31(`+L&+2&8Fa6YIah>xHQ*Z7sSqhI#Z>GC(`_FC-#u;S#e*GP0*j1a2N7!pUnF_p*18zL1j)_ zbDit6?>P)JUCjkr%AiBJc6Mt=29slt8**?jySJubd^*o2F(k(-Djl+Wj6X)O;;m&e z-6FYwbQOaGgtcZO^&&{XuTg8N5v6+>)cL!DvLN#@LpTda2x>-117srVsYs>Qre|W&+W@nfa0qCaUViG=2NYfyEHh; zn$ud@Xu?sWpZ}?Uwm6fy5HM><5;%}D^zw#4d&S;w^9QKNEPD2hjGAz;fZ%k8%7_3D zoZOD}VT_1rYQd3=q9hm5Y3L}|ApB}ax&4RpF_ zk%SOf;>9oSTl7~3*7baWen-Yv`#*+M?D;wGvFBX>9{Un8(q59T@ePcVtUUbFu-LmF zIRg3h``H$kZ`WqFI6d^R7>45SYO+=0&(|7rS*;Op!==^EeO^n~50ANxyxRQa%<0B0 zieH=wo5o(*&J$1f?wxhtsn_uA*y;MlJ>z~ie{_CqF=;*UemU}r&KUMrK*#RAuaa(V z-AX)mF4()x7O`t=&O7z)<@cWJ;U{cZ-wg^uHdZNfhnr2dH8b!A-HaPQ9(hb~<* zflGGrSFUM!e_Njw)A)St@#@o!E9`ynw~T#rdXGWcg_GfQcs?EJQVtDL2S`HmN#b?U z+R7SY2OgGhUWl*ol!Zhj{rJK-N7969Ny|DEr%{S|AHw)@rFXkONFvk)H4$~TB}qzL^db~7$wG2G71n`l0b@a61*Xy-Ld_SoGW9wB*QNkRD@@UV4`@*wPMOty~L6xe?ML` zXR0wP&18FuF;w0mpv^R*G9kY*uT$|hQ!%v|eEn>mBS_1Em|yJMQjy%^FP$V{%;yx7 zq$<>15oCBnxwuK@v2JT_w6ggFscvtfyb@{Z6VGaFK=+x9Zf9# zE@}5#1}V1Vso6kEo?>r|DTfHAx6bhpH%j~CibTB33_z&zJ~*k0ltSG zR|_FY(X@Tv1^b3keYt}f2RhL2nL9rK7=e{-pi--NdhgBBlmp*W*D*E1Pq09r;>B36b$5!oCQNo&IChc?7?S_#%{B`sBtZUYacACPy zebb`@HEXwv?tvl!enAXwM0Tn$gVNJNa#zx+) zvWs+j66cina_yQ^QPkBAfx5HekY=-uNrW=npw?y}m+$Fl1kxbC`i}dv#L}m85ve=< zwk>F1o$uN|GuN{eko0kFf9c#awiA*qdmv5h`4`!)c`eiR*3`f7A`+mg4E$MrUCuRG z{D~KF3$VEb6!4ny>}fB`pf)caQp(d*_x|v;W9)p`xvNG z67*m)^iT@KvjlRY0^(oyyIVT22^`eSZI}j!Fu2{);ILj$#52(4AucOq;TLHg5N&%p zhU;Om4^D8qn8vlS9J`eMFP+mdqo8;ekT?w_eFTzbxSi8L${cWYf!i?srUEHokr5`b7St zOoWTKME_@ph?WSHQCHIQ9~>gzG!mi;^#02s@-NlP^Y&P7K?2(N-f!2?J-_$;4;F0; z_ID=juxa@18hWXo-Twx1@NVE#|MlMm37M4h1_=aw6yyQ-hqI(@6PwlG_*wbbU{l>x z53o8pX4u}By_0uKV*C$>2$kRB66()_g!nAmkwjsBm?^ovM!ZUNw?1)^>k#?sJVds- zCAq(~Dd;xE^|1(nOidi%$ryjO^oK*_4EX%TKdP6lI8i!mC<-$JHF0W>G7BD>AK~IH z2QEBhvp2si?n3LrFRCgC;fT0}pKp)tH)B#AAttSKK_^O%wFG};%1%cfN zfh-5k?pQB1u{yATX(=7bdu&>&lJ4i6@XpRJw|%BKWn-qikY*n4apmJ{{U>mU5iJvG z&A@e{35t;<(V&Pt!3g~(kaL(51BC!86?JA2<=f8%z3Y5grFUTtI8>)NS}5ub|N z8>0b@OB?S3d(^&;g}goW_5Fp}+g~~35nq?SexUNIZ?a-UPj5~nDBRhcOxeA>IYqNj z|2Ca<`1H4rx!!la%@m$r{x(aGSN}d&dhPW0d9GIM`$A3Q^7qBs9`zre?!P_#W2tHO z&X48RuggC^GkAAxvAaaiY_0Sv+}&Co-2Hj$3)5oP&$U;F&-`2;_P+abW9hv9!7VI};{(ZZa<)at!@V6MElh6( zp+1ziWv9d_I}VX3WgXbtDfyip&k=tmb&P7+B`vX%pfBWMMeFU7H&}_YcAFHeuzaE% zMM#>gD;8<&eUeNGl0C{-BG!{Y+tp7?@v)r}dDop z1rsZ4lhpdG%CRXW$3APlJN4PMrc)}ORs-tK>T{ZECe_aN4ahIoUt_3F?uxb=G<2!Y z?G>8Q$n3NA6sXT*c1;jUte%q8@8u6WO=#cidun>@UIA;3rQ2!s%;NIB!nrJ#-pjsc z@iq6Zvz>Ykr>ve|ExdPQ^Nd@s(HE=dmsSODaJCc-A8XU#%~de#bZ|Jx zRi1Ha+&`6Pf1t>Xuu-^e1)8Eneb^%>LwU|5TX$W8nVxY5V^& zYPp)T|I@VnUys_D=!nS3e`#Y%N{s#|pUW*|`UjT#7nA$X9;Sb&&A1IrH*e$>7GA%8 z{RVexbAjA{8{Gedb4$z0=w)T)<>i%?6~)z6Wq%ejapBy5BXhZf{=auJ{U7+;*4C!K z_}o7?1g`b$?@K~k|BLo^ZbcJUeAdgYWf~axP2+NRhUd?j+|SFG%ztXmUULO!TmpAw zgsV3jQ*rT-;e>-0a-wI}|auDyr<&vfmr zyuZ=4Lv4So9}h1aZ0VRiBUyFN?F*8-e$=`*WX?rK4XKRdo8$kWYlH24U%*A&XOZ-}JbOV`?55!=1Fwm5tbd-u+xhhIO}G;^YN9%$R5tjxT;b$p-5o*!S?Qz|gxB%B^M+zWU`$)7*&V-uH)W58=ugLcc=lGZSysn;DHy?&q+LL5ST`5 zo^ydgoT4`f9| zs>k=Lg2bT33BT<>AUuO4+)_kSGy+PD#C*GhJUDDDJjRH$D>XT_HcDP;p`=n|&u9+F zI3{s$sKxY3-da6VHL813uw10b2^x%GZLf|m1&cA@X3a!JV}YCjfZF?n;AhVxpreN4 zGyvUlhj54u8)n5QwUW@HxK6{= zpeRg`^bRkvNWwTN9%U^bjb9Z9o7y7;*YTnc^rFxnL-5t5NfFyQl3EKq*}4hOTf>He zQ}YH-=xT9mos1!-n-B=RL-allED%tq*R7OpdNnW~luEM|G@BGs;y9;c{iKrRwdtnL z&=R#RmOfOQBzie_Oj&vekbc$SL#i|roKbi#yiOIdR7}S^41r0njNzssE-%il#EZss z2z?8gkvf>w>n>@`!+kEkh%Jyy5FTP>xgf#dcBriZ!fi5R6n!{G$1FBDtH(eNCT%H; zWkg1j6QV_=DRBmcoqQD&G2*JAw3me?n6|Bt56+d{7P$R6`T^ce);f}EEb;XE<;o`* z%U955YscFGr*L2?&T9s(^}MMO5A$bF9jB-EQ9vxqTsm!ml*^46mBx5_o_nNiNsKiUu zx}%RsSX&-4Kv_x>iEg)9m5(M?o*5Yfc>KM@OEn%0*oJ1!v9vtle>GTB%uRl$PE{6L zj63;p%HX+wHWOzb(|Y#7B%SRlOaTHB^n5DQ!J>Z=TQ`x`2nW@m62#mm5=?Z!9a$$A z_Lja?)`IldYw>#W(c3S@N;)dzj|Gs^%vuvQF}<_J0bTfkr>~Qq0{MVKB8#ij!sKq3 zQWkw+X8CB6pZB)Z5?RC|R*M4^*R727#vN0?b95F9SHDx0=8aEvoZY=8(`X>BZ3#C` z(L-6}RXXIa={tmeHauoxxG!FUPmTCY&GPzOp_40l7Tu9LAT4}3Ez#8C%zG5bqT5i< zt2Exxz#Tf6h48Ac_nup%c`_(?@Q&9eo7m zK4|i(kwYL|NF9gn+>R|X&S=zGmcq%fyRn*uT0B_oM=h1Kfl>&Xr}5yB)r(@i)8|su z`*-f;N}}0LX)+sXRHjMAE!Cc|HV8TBe!*P$!z)&|^x$@ZpnJ-#%S6%iaDHOS-3T$E zvB-1$3}<|<4kdQ&+ij<~XpZ_QbVHPq=-As9WG5m0i%R3@lbnEVm^P)dowo^`g zuRvZ7zkg_dCl0H#{2?S_$Z7w*dCvEh)svBrF6@6$pZvbI_#|@t%Kk@L_8;qWC!?lr z3V&)meLKqWkG=T|M?SY7WP9sB_U*y)kuNKAG4GO6bOWv{*d_-6d<$d|h1nC&Ysot8u{g^pa>|I8rwWAhU0x92DR zuyLZB7^iF>FWnxeD$TF% z9j~Jqrd1?#Xg2;pO8n7XKD#=W`U=)gZn0q;tHzAvVKQqYN_L*YECoh*7hxk><-wfvljIE{L(xWh;-y6P3u!4k;{4u8GyN43w#u}@r%sdpqMm7xCq#$8rV4B2&bfp)cu)tj5Z|K+{nF)zXq~cijNo<{@Ov!#a zPMTis3h3Zj986gC$Uq&A?K&^ZOvg`1^er#j))~MYm)@0<-n-7Wpv+ook1aRRd_>JG z86the`KAq}NLM1ah%EbLWRK?Mgyqav>&!7jm)*UAyR(@ud9#qN8AJ6MCf!L*v-@eU zld(#3Z1V&N%#~q@#;k#dy}>iVl$p2ia5-0|AGGuu-o#BjOoN(!fCkt93a+DMh;%X& zDd5L6)_7~|C^gfV#=1p-C}LQfh>$HBlN=4cgGX5MvL%s$8Uov4BdivVwTo1cwGCdN zvKY8BP)U$V99upaAW~RIG4yB5YC-|u!Lbp^KiwucE#}vR{5C2ZlPQ*^0m!GZ4sj=% z&N3iL$Zr_dBtjZ@B$lomIM0{&nwmF`Pd@vVNr?#AB(Ry|a`Nrdes;MyAn*=wt2yIF z`wPv|xkW?F)F|1|d5VLON3Qc#M!fZKIh9t2RI~3|P6mL-yz7#V9_gVF+m(3EA$N0*gIFyN3 z0t=*K7%{O9&6c{YYn6gMN@M;$&g=qDYtTx2gFimyQwBr9-%udi8@gpEww?`EiLdEW zX4nsi@+F6CQcCH2sB9ROnbi@tL}i<%GDw+LM9z|W9Wocl$ir#;M@K3p+LOy>lRYV{ zBcx1cN5>s%84O=45u9UAVC&Ci+fN~yzeG^_!Jb&6KTeXsy9!L?4!*F@<{TUEOBFkpbTZ zlbw?K&=uxkS<00G0rxFDe1)2f7`wR-e4UiW^7Wsp6_-AodZIgZk&Hj zUPav8Br}s!`5TGwH{)#kl)B}fPEY>J4&C(krtX-BtT(n(eK+W`Ear^Y-F4`m=+`$~ z_Uf(W&Le@VQrE1O@wCaf)mf)jnXZWrKQxJPx^P@-gjOd;#ob&{Ef!; z1IJn?6Sj3ld;1Xa{h_-7=mh_wIFLNmPnQank7LZB`c_bxZpE^^q0(<&(9W?O7Jcq+ zeLT#ue9e8XeSJdCcQAf&V$uCd@B1VcxUd=h-|PD2qPY~_^nYT!r{c_|W^r%J>)t^f zF0JN!uO8exEXZ|~d4MK0pnr?g&~IQadBC)k)BMrE> z9Mbq)0|(ytl>t2g5vM@0GoZLxP}0kvQO z|7yGuK48*O`}p^GBbOX^6&G(tGVQFN_+62%es-+xm;d>I*LV{$NrOJ#_@z=PXg2#b z-UxK(lKrVtppQ48q^_3dHzOXmKpVqaUf!jTH};Ee&{Ya4Qd>Xf&Qq2jki?{p{j5m8 zm)>FabG+e|T}aOt4j%|v9;-ju^jQw1o5;eCgINZ>PS zZbg0B{c!#Wu_7*Z4}jUvyVq@a(PCpwNkphX#up+HW#s)N25#$J;lzHnUi28}srAL= zWB$@hDcxu|JztPDq~{C5Q*LR>gGE;9>N7>849&HOoJ^h1^~;xWQB%t#b(oJh$w1O{ zDcfSHekGS*L&h@=Z;M#Wv%l!QTJXtp!!yx2DfLC6$EMU;@oav>*$duXk*S3@y$BE|dM(@`xsMXCT%UiH#)qRn-7 z>bd@iQW@lX)!SAlbm6;7t?070>afJasP{E8O}B!p6>jXQK2o`=tyXp5S#(E*)|Fnh zvBU9uAr-nzhd*9542)8rG_~vPEI)SWhkBXiN$msEHix1o5>H3R*a9{ix1Uvn5U;H**{22tFIpg`Ogu%i@q=it4JWnt#=z z@V6BT6iOR~(%#FNYasi-z&r8R zH`o7V|I*Kb5c*l5{2(?S{;!%Gj1!@HX@Wi|;&86?-)MD8P5(R#I$jMkh{bbDZ*=_i zEa+@FPS!JI4>P;|d(<6nCl668C3(&n^TRg`JqJ4a{nY z3ub07sK2(~TFdWhRv^#dDl3Vw^@JQaF%wW&Jdg=3lvvR*9gkrK;Ev;#;BbJ7XXMaG ze8mxaFmmy}DXb{F%nX@jY~%R~9X{vxwq{CS;7joUNWgfY!k90m4`5^sSl5NQi<2vo zgL;?8iOVxD+jJsF#g(R!hlP&?# zIJ&}C)n}5?Z$0(_=XdC~{^bhJ}iO?aE(nCJ~?n!)G8mLLiEt{Temq4%BX;v367(UnYcPZhOztM zk974JgNVaM+$sY^twV2kjx|niX8;w27>g!w$(-@ zLOPl#BfbSNvA$A4#Zi_WT?)E);oYDBKk5QW6vPrbPVupd=b9$NtMaZdUbZkiQKAx9g$KYzn3#^=nLS$@)tRUV)_;>*xi$%lzgqLAGNHx;QU8@-$fwe|6T4%EO@H^KFgZW+tc>VkQK0ThHT2mtrsJCB2@lw)-+>4>^^K*V zvgCPkys8PK#1T@d#gL(59;%)(SUQ&WaUcibEy|jxb{ep&gvt@z;C8$WsPd^B1^JRD z0VYlszC@VZ6prc160Y2uSKK$dHeut*$exL(to09RU|+q6;pS#xtfsvvsCQDY^&+o9 z_*+-jMKIY6W&_HzAhHBfE5--O4FwQaXb{b%%7R@|!$5{1klYWp)gy{8wBkZ3cGKHb zxm?XfX1gIAA1@3K2;tPElm8LHSr z_76WUt~TJ)G;Jj5Cu2ElF9IgIgl!^pBg5)=ZGsDWR$Ypv(St#CUcZV=tjV)zFhiTO z3z4Z;VI|6kj7j&}tYu0MQ&_d42=|zd>rr%uHjWQBR?$AX@+*yt<|2yXgh;t1P`^di z3lxSDR)k7HH;wh#d#YgleT$VM7))}1FIV#5uxVU`Dn5yG;uWFtXq9WMue7P&htI>~ zNhK@5kB?I3QDWm>bJgU-2!L%x`B)x)ssiIFZ6GE~x>Vu;ccF0``LtV=-AZM>tcL*g z5;{op>d9gf4W@x3Ps;lx>d?;{P9&RIw*l9$>M1l@6>jELh37QjGi7Z`9ZA(@pBnA2 zA#5Z{=rH!Qq?1sJ&0VvZ)}-szp2wOTErV_6n?GH%@_%i=Xl8eHDe+mZJ+ ze#kwM^SSekih{4*6|U*_&(}{L+n4wAYSN#Z{M=;|q!4iaA;czHI zVYjTxR@gxd!dw}nJQ)+{(46jK{RjYz?4iPU4!PvXyK$YxQDK|o$8q>d`7q*u)9Se# zbwXTH-(MtlKXBtsRk z*{QD!x)~-{??u|R34j(vxqC45x}1LEVa!)9zeA~^(*OX{bY}+xL=k=v5laH|wAaT2 z)lG~iXjpE-km|l!xhv9nypjo+1;l(~(@zxPfV19)ioV$?-yQ*6VJ3!(wmancN8N)5 zM^zMM+Yqc*2k!j=C1Sdp7maVYXGl4ym`&o5CNwOI=8TF|^zy12PygLZl1iz^7FLy# z9RMSv_MFS<3+OA;UD6ew6r?A>fIW^Qi9%&DK{Enx`Zx`6L_xpBGaTkToChhuWf>II zgPzrx7pP*QiA3GJIEb_iq|gbEwC*H=itzDF7O{*F16?i$Qj2T`22kzBGD#2Pq*jQ4 zjKLx(zdivePiExJsNix$5!nnZ;lLv@*Hr=tygiN-`K~>CPw}&`LEl@Ass0Jc*iK-- zlZ4cd#|%Vein3t!1_RXW~u{J9?{89WHk;$$3AiMAx&$MUi2^x+SZlhB7Q zqUo{VEEx_Eum&kWLTH~N(Uhpea1|atWGVVU_Cy@pG8T$FOG3&OiQhfhx;Nbszz(to zuK+`e!8R2M4|p^suo#E}Y|#aE)Lz1e7Ye^)9tnIWa;i}-q;yk|!YgPuQn|P{5c{oR+mM9fiK?sVGNbqj1FBF zWzdjDfc{58Xj8bm8V&iDi^-JYPT~$_i@5l98V<(9Og$x13vD@JzzbujUo!Z4GSXQO zRJt8~wJzL71KPX|l}0nxqM&kks7MlfCJJ6eV{jlKGRTa8GGh_p^hd_(CkCW=u}cj_WZ6YmE5oEx*rh3nl6`SSb>Zezq#edx0ZnApyd;f5I+NH#2yUA9 zkhrH2k}8anXA?|P*uj{1^>yMYLpL^hqGJrMCcyWpCnGalL)csy+i7v{pgyN@j5_C$ zXNl|=$p~%=C~+K_9nS7Mj?AF~gT_cxir6(u$Vaab4!fW(F}N%dNJAm)r3es4245=D zo=m8uF|xiw=3~IzuJFW&lvI1q^autxp7HYR>FhPet2i)x7aEBJgE7#iap`kIz5pIB z=;hin9<F5jqr?;R-tJij-VqXt0k0lvDE~Qj2-9Sm|g<-E-X(#9>!0_$qD$vI<%2^ss?~edcvEC zOpT;OD9hExO!`Yan(8Zz$iY!anj0=U z1Y|cM^;5T9+A8}bw2(P5_6j)us)V!RPG|#Fi6ya+yRxwKexYm|V+V<;i;6G=tm3qU z6M7ty5U2Bs*>fF=iz17QOp8l;iU+2P%Ms3GzLHx9N~%+xY9mWJ^Gg~LPR(;A&7Vpr zsgCVhSE_BUbRitOi?1a5T)yQ{eDmv-Lax$&Er$Vz(#v6`Lp}DvpU|UUOUL=jCbi0@ z9Li?q(7zKWfPsFWLca?p07w80kcIS}=^w*+0MI|@LPpH+pZCpldFS6=!LRG|A6~(8 z)bT&Og7b*Ozr6zTch3r~WB+fj`1_xn?a;r!;_tr)iWV7r?a=$X99 zudlz_+}zmSez&u;{owtRnPiaY{bczwBY%4zS~nJ_DAoV{zcR_rjYnkmWbLxQ-iOxkyT&tCpls{&hp|Ur#*2m)vlM<{za=(!49v2q^jtqiA6H-Oa z{X(wGT#lmb$2-kMg}ww1ZohZGkn1)VeO^s! ztN}Qd%Qi(5qx@lq&ODvF%ZJfN@+jc;O?Y`@qCQPMqccxq7saMhW}eZJ>q|erCGEU^ z{c`NC{5>esbDnGf%!SfrfGE0R;*dX{+FqhFPZc>W>T2m~)#7KwaxalZBDe3kh`Q z344MY>}?5R#VO-r74Fo#relTkUZ&ESCovn~<2pT)gvqRs41OZlCXx;R#XPO%S=;)o z=G&ibSS@f4-|#GukC9m`@+kO|dFt9&yJD7vcvTuY-xvj z-_n_x8+taoUT@uuZ)bXU>*LJpwX9u9&v$oh*Iox&euv4g z-i3S--|jok-Ap(RKPdRBAN6Eqd%(!ZFUW$^YvJ{M!K&(=2S>u?gH18zxveg1AfQ}ynP zt<4zKmv8qDtI{@+imHnn{4Z3N)@4UkmS1UKR9RUu3s6~Ib`(}wTk>U9d9@gIM|u54 zT8r|=^O94_n+uJ{m0v&W-B)@uJF=zp_UU}N($>someRZFy*pytQ^;1aoeBP{V(-Ug zFN=K`)4qiLIBFJ%{q)%JH1_j|?+NUe;i#jcyF+P8qF*1Bh=}gJY1yEzM%M8b57u$!9) zJyoUWsq`?_$LHMd05vf1rzbchB#h41{xSqdMMhruQxE*FtiVbC&inhf2`fEb{S~bK zC0qU7O?&xrHl2gb&d$xv&CAOx$j>hH8m@B>$Iu^4}h4|4p#U&-_2^5Y1U){xW3is2jVg;-4lC{5fQ^Tz|lz615OP z{pBm~;0_x>Z?ZB2>~Fe*fCjXEs&-{WO_MF>H6yu=$<+rnDJP4?#Xp@yw7dkIJ)@zx zBIP6@m6SUk?8rppqam+mRwFd78FMzg$Z|gP)V7L6?4WqbfgA58)Ivh8mAJQ0dgQoj z1nou~d}lV=t8)P}JR4+u=h9;E+qQdpZv9}_*@`_?GufAdu~h@j4lDC&AX2BVS`MiJ zV?MT@vZP#*EsK_Lxx#t$!rCW5*T^MUc~a{mFd2VlHHx8+MF4>c?_*W4fKDPSS5ym~ zK$s$Pg=ie{SzO@i(`@I^7y}n$=z@y5Vht~vdI8iu7%QD?I*t(KPY8yah*$?dRJaNy zR0?FR^p9}p-im`6i#PYXWT?$VoXI*w=j(D>HD?x+MWd>PjiJqpX^IpQ8pnCvpw>Kh zH-82#x*TMkEF+?Ws7w)W0o9_*SKJ?pvbS^b#b5Nu8B%EXZV);79WBYtqOssgAIC@@hUnRvZ_2C$)$hV>HPMiyHu31kHh;i;Ch93?1x^SlF`vg_Q zbHBlwoOqw6=aw37ttU5IHe}phMWmuJIUd^Efu~+P%&rsk=cV2ZBy{S@9;YQSiIJfd zBTf;IcyJ)c>Kt=PH3()vMvu^GB+XySi-4GACEu6%Cj5SBOn2G5bHbz}xkRzsq+Mx5 zwj+A+Tw!d<#~XKp_5y^CFgy;6m%nRlAj@+}y{7iAL);tA#)Ue{z9loqj2;t8?P)fo zMBVqkoQ?EGjbiPDkWgK4{Nc)3si=*a3o;3lCLkz&=>eozd}{(-)@%hXJe766a!4>n z8Oe)D$R`_~fDkSic-PbZpnk0RJoremO z&px(BCw4F0`eNz4N?3RqdkLZ|(Yd1fP-rO5GHp=AKd>S%=Ss8tNa37ZXz`hh_fX0C zw3l*~!~|=DYhq0d2w}lk=rQjmBIAk)_;7zYA|eh2>b`2CW5Z~NaSJZ7cpvZXPIfKw*j3(hR2vA?e3cqyZ`FfPy%K!a`Xydkes3T&eY#`T9ge$5*1vCfLG_ zUF`x(&Z<0rAM$ZtTEwxa@*+(ur~`AQ*y?_%`E3Ft$lwv2L%MQO;h9y%*2d%9!;45` z*mj%{Dvgm3Wy+;9gjT+_6u&2$@EA2Rk>zVxArwhu4OJc>k#xDOrEv|9cuT=46n*%`mGQj>>eW5>rOP*X6g^XGaV(oBaI#aC{GaM|L}jfCN2Y3P4H}MTHd+zDzHi7<=jH6KT7XEaxWG>_`JiexUl-W}|?lq>#Gb^n{#Q z(#g_g0HgIVHuZa`DgFN4_|p91ewN9ts>jvV<2pC^5yJ-9{vyK6!Mpwn?uO^s_jeg_ zU@U$>dx^ZH1=kDP6Z)cUcx*g)NdMgY(rpV)JXiaWp>hs76Bi;bv@>LkW1r-?e@@(z zHgxOEhc}NY0yV8cLuU4q>~Gh=l0(V?7KlS9OV;HoX;EFbOl9W?spjlSERFbN$E{)B zRcRB`G^>LCJgVQ&e70qq_Ch5OBfdt~w#QfFZHdjb;AhQAq4@ zL1&W9wH;?Ur}vvPJ311I!Np82J1#tRR^MA95@(%Y`)^x4EEJ&01$``CGR6S}!Q}ig zF^;ApX~m?(4FBdc0>Xa#9`-k!alQS&(a+G-;V3ynXHT#{{({=WO2HEbQghesN863Nop~`>-)27f z(kfJ)rV?LhLX1xH*%ed#BcH60a)z>XZ9VNLZ_gJA?ym8i=D9f+))3y;B?dLDIP~Hx zYg9HXaXW?asrWr@VB}2s2ZNnM$E$F&wya@6*9C!{e11Yg_V(Aw8+Sh6W)p8Jt8CV) z?EC)w9Lx6)BEaHd9Q-0=fys}?z_J_YdA>T1$9j=><7qbQMZ)BRWpTu@6ura;@6LE( z3>m2`dO?LA$%j7*^zJ)h1}<~%q`2Bvx@c9JDvT=4FRsDGol?payA(5+#$5TviUtp0 z$7w>5X9TrWjjx)c)^{FPakbUo_x?kL6feIb%7N%H)Oeq5Aypx}Nd>Ni2u=wzrs^4* zj@&I%&2REsjXTBn&|xN++%rWgtKTs1UgMbUR*_WQAwSBQYbYLDkv!1%7Vw*~-F{)P zmos2G>be;+5HdE7oEkoU9tw)hQW1GCBOQuP`+1zq#g{3jO#jyCk#rJe~JIoZ8Cb>hM0i^m&f ze{3$7@6C+e>2BL^{jrr^@a+ZLUAybD`|rUC?~~(pfzMn9V2ICGNYOSyC#I4 zpM>xP*uOo!B(-?DaS?j#3tDJ5mXRmO&d_l%NU%Q9R}LNcq|KFimOrKqluxn5(7j#+ zMw495DNA|@E|W8!UBI3&^O60cvM>tA*g-jm**W&dL7Tx(BJ?zn!3v`~>rD9Mj8+vT zdPZaT-1XbgXM_`y5cOVj0X=ZvzBA)5`wi#d&Q2p68DcDz}O*-j}o1n5x zey~iLc#zN{Lz6ONen0a3w#+n$u>l8xj>DE?&y4ArwzxuNsK}#n$;I~(JZHd7+hXaV z@^VyU9Zji1Rq2HlwjG7wBrz`cd2F)-_;I8y6}Y*~VCK%$pbR-dLr&!KLB1l*020`g z)Z|JU;{^iAFcco#5fM6La8Qzrl;H%D3Gxq9+(u^;Iz-dG??Iw5U=(H_?x~m9J1*@< zP*jl4aQUpfk>u+JQ}8%@7DQFy;gdYa4TRAUppP&Ej*w4-!uT`Z{5VtoT4CNwrW(4y zR2M$w4z9y!3zOj~>o8`r&v87qf@1VJNaKQ0juJZ{q>OY7hIQbe(+L7~u7v@^=paOl zsk9JnO*5GW2La$JB9xcms6j&xYMil=0_J(eYh542V&&DR>AM^sFu0CRIHTEYd;X97 zv*Y$YSBVTn(2!UXp^#eExPE%NoXw&Y)>k<))i?~%v6qwnA{&Yor4H92ZtZ$I#T@Ab2s70 z<_EJahNI={ho7vX!^RO7AImK~RHZ82f|Z5$yUL^Dv+}=kbBrSbPzW)ZgZcPC<}YZ8 zP(85?cwmp8M~7$xQ4&NYV|5SeV&NtXMJ7@uAfk~g0lve2;oWCm`E__TCb~o~(Gy&L z3>tg{VjwPc2}B4ssSXyI2(ID-%(jspk&tG9adsA+g-38=U@ZW0Wg=wHlnlbl*3d%y zsmOA2xpfX?raLqm5@OwNlGT}JPm2A_Q+-=S83p zC8z_Yod)7*4*3)W2MTfpkPJ+-c=ZaG29gav}Nt7@rHRg!yKnjpdYl{NVYRv7<%7pog z!#ASXxGY6gEO{j^=y5@sF^O-^Uog9WL3I;-x6X=4wZg4}3Q&lS;+D;lXqmQX6x>=> zHElO4n#MzcFj+G=QQ*%k8Lv>7za6Giv?u|=6`nTEZ&K@0wp>@*`YSH@aB-iRXfe^f zBx;%H9G0etjyoc1H^4=#{j_ff9b#wX6-uZfiam_m$mahc%g$~R%QVEn1Fz*8DHqL- z!wg-L3&Q5ccII7gUojF?vgcK)cYO0T_=;P+_)T4{?)Wd8=p*n1GnE9xhy;G=&Xe60 ziWQyXRt}7fc6KSwj`hwiMVQgmG&kPJpf708h$NqNN5A!?ZXo#$lz&t=xyLnGA}4vd zEjea4IX1!t|K8%~IHo)0EVkmxY^te#YC%eBU_~lPI&}ykM3zpgHnEpX_iwbXrGb)A z#4a;A;gSofj%K#3gHiXT-G=PlvV(IT*So3nW_F1oH zd9dUx5H)MdTOPWPy8HoNd%{*SpLF@#?B%v#)jxPi3=Sm5ViMCF$#R>7Hoe2?lPy@1 zEijiooltuRog?Ou1Et>NTk8dd!Qc%fX04mcb-66}a<9m!a5ChjMxHf9G|y(|na}0< z?&d9{^R3kK+wu8fLHQ0n`7U4c1Pf7_?jSf;dCx74Ob6T7TAGF&R`b5{sdHfdUB zN*u#B4~)n>O>35aEAUYJ%H^nW4kEmZ%ukjp&Au^K_!a$PK8j}6L@{pB^=w&h zZ2R8a3Zk~$Nw(USZB;jH{X8<|`H3Rb*NUCB{KGfY{@O#g!!wsErafY2wB2UBZ_lVz z%=rD7QB-Lo9DeG5t<|mOVrbdZ6ZhNR)!B+Xvn{B2x-rtu0Jl${;AKU!C)uzUO3Y5Y zimTsjHaT@Kxeo39d?`+MDoFnP7@*6y&|zG;qrlSM+|*UhDGr2F=K zlfL;dgU>c^Zvm|uZ*o3#t|`+qh5LtPV7SNiqFFTwR8z@Rns$m~)$MhP9ry$3Dp4L-5#sj4qSYwWd7V_8Oqu*7} zL-%8z&*NY2MJ-2J@?D&Ht!;&Qh6w6QQ? zleMA>np3V9RIO*v5<8wkTR88xiYuFy=nFbE%3f0D>>Pm!{+1Xj;4;1pX(JZxTUDmV zG%>ZHV7d0G?T31A0F)E%W%2NyPh!X*fT@Ro5ZOK_Csg7m&*$BQJ@s)-#EUw>>qa@| z)*j<_`$D!LfNUXWhYe_-qau${@4S+u28!Mlbl|bsM%pTaa<+$_2~2)kMj{Z9YbfPE zyhLAyx`RmWr7t$jEqMFNs2?mA&YWHF?mEmtV(h>(#h`Hh)mYIzMIQaaa2Y>p-{p=V zSPNBN>XC*_bg48M*=z|EDD&NC)p{werhYH}p(6X_wc@EC16Na@9})zZNw8Lm)~73L z>IObx@{-OdrXEGaGf5YJNsW_M@0t`-dbqW}jlL1GeDNgHcRh2c^ZWj&DCpYr7YOev zJ?|$@JS7z|vCuZklS-htoOx*GKojpDA>B`o{=rcvq!fNrFnBHIr6kXzz1zW{@t4Bc zT5yqip&-U)0%TVQ(zA!${Em!4BQi)ys)b>WzN#Vmw5PC7LLeh|&X1LAXm~(4@=m?) zQ*OUkH27QYJL*iSv;~#t5%(My-@l!#B4#<@{Mq2en3#<)(a*U-i)CxRLTz7!`p%=P zKR4W1q??=!yg}kwM$62PaK}$Qb(HAGMB!K^j_AQ%5D`lI# z+CSLkg#`~?Xh6Gjjd^{$!S+2QbGiR=>%i&nZ_a-o3cMJ6`#XC7+wz&M?~p?)7a!Z& zSk8&Z{*Y*!b()m;`idiwc*zNX9bxKSWt6o-w(q%coc;Lp)UIJ z5jh z!+ptSHv$gGBJ7nH!605*aemi^d3^q)-oB9ZNwP?C+4=Zt`$th+?4gd} zd{rz8Ugz9Hc)Q?4h@&~zmB5?D>n|%fcz~@_@1Mtb&4_%H_1e;@6EN6|+6S zavmVD#uNLlv=o~el!Z!o0T^3UVTE=bU)-+UU1GTTZOa?kY?NLO3k#1hEv&`_Pl9qc}wSHkJMK; z_8Z4}ccP6Y%Mz^2fP=?OaIx@CX&8^ZFNw_VI42`@1Wa}3L_xp?>{u^$?>L>a4KJQS0W2-EO*WFG~zv~N7O0i z0MWj9W!!*KHBz6X=6@wdgXn!AMXw?J<$NQg^hE1Di#J3L8D4~EirG*~Wx*ugalREi;*nv+x}`T3lU~^C`dU;RX(tNeit!@aF`VsKq{+OFj0l9f)W^D0ODGA9HX>l`Wm_#$b=grT0D^GqPO~0@Hk77 zRq{y^8PO79_@$sSH;&lHW$#Mtd#@p2X6i;}j3h$apr7vZ|ba=Sf`PJ(}i zpRPw4X2~!HWfO>^ZlNPVksf|R9p8wNzyqRc0aXv>*)4N`dN<|Np@d_Vl(ZKa*n^<@ zdyWavW)9aS?yGJrQXz|`0UKDJQb2F^W3ZBSPLiSn=MdeJQt$+0m3_!Z5VE_E7s(s1 zaPvBG8Rx;l>;maje=6S5Fh0UaNvG>l(2z9}7?IlO-29YY*J_IUd&g zq+ubi?kLn3AGUe2f_u8&`0e^J1kt)yW4b{>U<2en9&dGcy3y{wtlpN)w9wCqM=!{* zBMn8%oNiXXUB%ohs|L8bGOF0i+7xZ~%`d)J3a?3Xo^;=5`hgiJ*!4}#9@U;Ld0~m= z%;qyvx1#%8`&zPUs;pMpb49w`L?KMg+qaDENAIZ9k36K^EigbySv||(W9~8=%ssuH zPqHMwnIj+)I@T*)mqbq-I^hnpTL7bjA`MUaf<@fW{Q6xur^%qHT8o``s}R{o$pcCX`E-( zybngp^<24K(w=0P>Bf1QI>7h!S&no)IKD=VPyqEG8yjuCcq%}5|MSzkapTHF26-Cj z7&%Q_nPxO70cW?j6jh4L#+Vj_0E!AEwiGbe(6bKQ$5j{-rNb!na2XxHfKU(4P z_YVvRAkeqC2!!C^;LzV30=?twA5erp_qXW&+kf8Q`X83J=zu{^&Tqyb|G&iye%EdN zJ!$YSF@wg&rr!$Oe_9a@F#CT-`@IXy)Au(YkGQSX67lqQ|oV(w$ER@ zc=5|)ySTKpL^s&dtF-TlJxzr3~dBCWSu^vAn*?|xNj?d<$)()wAV_36{k z60Ogl|D~&z{;K+a{y~Qg0_pVJ_Tc}##icI{;M%MI)fSh-d)!c*Smb{dmfqj^@QO+{ zuQK~h&&glWKJXVGZ_@N8XX7lru(Z+TN>~%F!|!Kd=|P#ftAy8ddhYz&1QC!)SJC+U zmZ3W-9CCwu@kknX7Sqku#&*U6=mms52&=BgFQRmCpzkE(ps8GtRmydW5p(=~9|rY+ zB~60UjY!kM2o8Pu0!gLwUrufCsSm?Fy{qMgL-YlnNLn_XodZ9HRg6|#@p#+o`RO&K zW*0L+`#jcK_OR^h92Mfg*K=gw|l*)8?dchB5;7TqR)T$R!}k* z0vG!r>rm7pUo($2*`CAIop3rKcv z!$M69Iw8dXg$84i)Ky6W$=yyCWZ@2=#S2J^qoBp*k1OsmI49`7F$?q6!kEGmM4=!l zQSV7+e=?E1G`KzQT3KZ9YlUvfR44YHHGbKt)3L*YOJ*DByuwRdy()s7t`Oj!pf2g- zDb@fF96Jk3Acj%~wG)*A*z5cBn?pXXoo*4&rX~59+n>u}JBO#Rb2>Cr2RS9`EBbj2 zNh`VniV=f+fL3QvZ0}g&O$Fe2fo|M5VpKU9jhO0G1n`~ap}a<7hqoq=J-c9Y_e6!b zV+Bl4xAvCm`^L90x7>~U#R-GHU8WcYK!%ewLnVG%*6>)uKF8@9)S={(M_r!6 z9G9P)v7iCfDFV>xb~EM`dGs-KN>buZ#IpEd;jb(C=Z$%Ixw_{3%wUV5q4GOJ&@b|nwT7Q8b4 zbzI65Fyd=HP`GDWbTu^Q^D`;?oG-NWBU~GK#gL7+fQhp{phm5TS>&a6r-7>*^?*0Y zM}O=vv#JyA=CDL|>G}$=WHF9^mUhcE((SnPPM_ciDptHR31sFSk6c?R#-eBpz63o} zk9Ho&7kghRTGkmEW-U-8=UL+^90t`H#+P%7LHJXN-ZPeSKGTyUjRoD(a> zYO6lknc~A@Vv$2amXkRa8EG7axiBu<^+eT3$K$oGs=2(P=Nlf?C?0Dc$%DVjpW-;J zOc(45-CBf7eO*;7?0Iym76P}&6v(@%9xL&aVHHqAfG8y8vr%e=qu*;a9}E`~az2!< zTc>N$3roY@JU7?!7B4*dP`$+x12#izhOprcMd{s5jx^n9MHeEd8X@VD{W@GQT%eK_RBW z;y(M6;`vUw1GgKF8y8NN?d2(G{h+UGu}@X-=SvwLZnO%_nX1vYkvtaDXf0q<(BQ}= zV|lyL))YL3!nkvFkf1X5Qh7&l?viJr0J}wO!j*MV4oZ3|pJrcdu6iX}e-V&+M47H= zKFL^#Pw>%1B_C=wV2$ukManOZbdC(PB_ESN!Q94}^XPhu>4eYO>wiwUZDa^iT~57YtP;@-^`wM5C7=QO*DFw>$-l&c^-5Il_`Q} z0(*m2cc~4yAsqFjZf|!KjeR>m9Ij;GaOOJ{JPfYAWrhjLs5!fQ@}*$zxG(QXzGMqf zg2iA=A`ax767Z9t{j(=c09jN0KA8V7H&QaYf zGx>zACb11ov1~N@R7h858jqHU1-ei4I<~z!w3ib;jI%;?LUNE;N}qKa#f0tJ#q$u+ z7!X(vNRx><1trfL*>(gJQ*i`Bg&2)9Cmp`BN2#l`+hkOU&Ho8WhY~lRW~%5@I+c2t|yc<@ws+<&`}31;C`z`zZ`eiHT$7J8T~`Xi_q`6JOi} zvAKtLi4ZB`oAW)|E6;a{kw~tBj*;{UrUp^UMO2w*!E91Wi8){wsxwyg;H;I{3NUC* z`&JJe2O=SV1SYZ;&NEZOJ`?HcURqFqhMR`Aj0Yputa5v5zo~wdbbE2$NPx^xBdWt; z+T{J%XM%p@^hcq}l-14PA~r7GI``W*KKLKcqqwWnAnHL}^N4M2Z8DK~a3`8Y#v4MN zh0qkbNd&rLzPFC0DS{vwe&_$V^xS<ye(ejN(zXLu{WGm!&pg3L-+yd00x_NJWM)8UBc)xT$+t=;1u=JA zdG^+4^R;QKKv*F?QbKdvJe$diJZRU9W|!1S2SaI!5#YDYTb|Bb;pr5iEdu9cJJJQA z(R7-!xVp`WY^o}c{WCUV69q?(lM?8=5Wn;LIUb2beD z8VD=9#hL~_=aRNyx#(U=2UnUMbs|$Op;v;VNR9`q$>FZFC27&P~{$ZXC5MTH)uIu1t+Oy(@Y3esCZ+$p$sQ{Oxqjmhaxh%;S~C)F5xL zSZ4I@*zKE;NFi;p9>K|(h6v3 zr3Do*Ud=(%idxW5qItid;g#lq|Cz%Y!y#&pAYd5pV7M#Icy};V>@obJW3*{>pPA+DVo!v}DiLB&w#KSVGG}eXsw1$PN?2_>tZq0~pMW)N#Trjy zO?R+nh&T(SI4iq2oA5Y0LYzZu+}X)Er=2(#1kOzf=V6EQ499sBa6YX#ze!xc4lWQ8 zAFLD~VizA49v@DKk8F*PnvB1?6OTqD#3&_T?GkX|33~~IgrwGll*xp&odi50F;giq z+b;2Xcp`z2nBSUMIGK2JCy|IqDppD=wMznorj>-G>YaGn;iUSVq#8tW>P}KiC8#Mp zInge;AtAY4KcQ_UsRNOM*-5SwP3fDA@9|E4+?oPzk;xyCDrlcM8lEZoEk*o(=F`bc=?k1GD9)@-r7Zb@CirW#lDLisG^-#pP9j zf@(QQWt4)dyzHsNj%uKtn(ENipyU7)`ux?^AfluvqO31`%2-T6T~tX2@HQx%HarC6 z_tgy=7bx{EQ5uBpOB+n0c};_O%`WknhjLp*38`BNXgl)jI16gp@#(qq8F&IE)<`2C z3CjS$>L7c@4G=p3ZPtgBJrD%a*4FteVh>bV|CO=_v=4`%y|eK}VDAf5Sp$(FH#ZM} zviI|K|4rEg38LSKeMCf1cz9$~)YV@lM0V*=`z)wa9;baAr(@y~=L`;)Z1(d76dvV3 zEfIxZ^S(YJ=mAsk9fr_$R>6N+KLkNet14chX1p&_(srUL8Di zN&q$q%aBAsB>^D(VGgOT*Rrm<Q&dSIkG&&sU!v^;2u?I~n2OTR(Nl8Enc6xd`K07NF!2bdEKleHw*p~zBe?dVG zfsj{FP*_-a^VTgQpsKjh&`u=g1Nc7x{+E|m9&WyYop)1H9T2htI#U3&|B&l{@Swe; z;~{|aSKaRc>QNd=1N;3b-90_sq{Fgm0Omgc`3HdQb?4Cdi{WPgz&|!NGB&o~RQ+Q5 z_0-f9P*eTt)$GBpnmk2b0t%{u)Yd^q_2CYCe|rtsFo1IE_wV2BZ>Bf44ia1&2X)kk zUDVrO_IJ_i-@bhV>==KIasfLGY8r0+24ZI{g@Cqtf5SX6>Kpf8j%EI_F>2MUNI&7f zr?U)m`CNCo4S&PzErMov&4OS1OS@*4RAjyLBVWqOzs;J3!Bkh z*X)oUs{GJ{bQYSTBu--6js0@(XI-`fAqHL`Sx}`R_u&J$(4{`%Z4Tv-9xSsh!x* zNJjtpC7>}1CX5&r`;zc=9Qjg-Fy-pBT9SG>c98$Mcbgc{7=^@z4_h7!72R*74(Cne z&8vI^G*X9;YsQK~vs2|AdffD+pX{fz9>y);wblskiJvL=(^(_f67@3^t?pWu0b}l2 zKsrn43@BuEDc1!j=9%kw{o4z|VI%c^IxDbG?9X&oq`k#W=Ur6iU(#6#!qvaiS>mgu z**3ePj6gc8iYY4W&vaJl$o-0ns}4XqtM=~NuXI*v{zMW*McIBjtNrVm@6C#^{59ec zZzP)P$YAyRrosJm*3fj@>g|Jc*5oFqX~Z+h_wIV1 z#HaR00R>+7UOO}yFhUoJ1FcICA245zRTGQ`b8|g`i>>zEdN*8;+xG$Cq0y&agjcO> z;nz2LJ`cZBA)EU36C6oIierWA0+*};y(zufVj6n(g&5szuiG}BschE^EW{LQOPIc#>AGvNg0X#OPsje7^+3Gw25we@`TE|g@H2r1r=0GTJRS#x7_CLhz8}5-LW}@$gIh^;VIo&VRU<}Y z40$SNg8rD&5pwa^`#g3cJBxuKzM{TuKJnDP5Tk-7>_!!%+_w)#?!1-D=wRt>sI!%( z9-xsf z>Cf`9ZCmS)nWR3CzPzaReEhlnsTY%8#jzlS%gmTC`GLAMk}?qwrR~zAuB2R}Y9~FG zUexQPeUfx_`zeuXAxBNQOq_xO2L7XeSq;QN8%;UO!^7(uqgJNDau&%Z{6c=zkASQpG1icaXkxtW`bNt3 z{Ccv>*=)uu`h9XSYw>|=6Sve?K&-fp)WFgy#1J6FI2|CEA(0Jz)BpH%y>D6=eJOGr z+oxFdB0a&l^gc+rU-u*nC@y4_kKJIv(5>!V(v?zSO3r~3CL@_ddZ9AJ{Q-;2#w@>0 zOkPX~ngUsxQ_l^Rq}+^knBKgeCtoHVk?=&JDmX{YM@Y+4`KiY2CPA$q*k49Ga;jd< zrNWe-c<>9+=2YozXVP{lp>_ zU&JCW)XzE#w!)xVs*qAJyI6;3uB{6+oEqY1yVnXk_!p~KbE;jgQ7Kz>X8L(*6)XkY z!eQJcys<0Pns-3yjx8*;aD;BG;1!V1Z1$}?Xg#&h7zRES4W5}{J;k=7bXWSA+L0f7 zAOTt&HD${P$URWIb0QFZM4LY`Wd|+!(lFINfUjwnfP+)brvh+mNHGIzx=tI z=ke0pGjTLv_d$2~7kD2yAczw~4uL=BsSAEBZrstNc+2TSsxuR0vU57u*NP#FiLvJH zlezke#{@lTB;4qU!&i`tHKj_(#>9s%7WOSW9Fv1AlUMCHPdCzEp=)D0=j#@zsm@=* z)+`a#YLV|{4VF!$dn(dU!o_+sUbY3r6et>dYyd&6ITys_!p+A$hen4b&lpPDQLvxZ z?^9{=eOXM~(~Bx^kTw1B%FZSte`9GVioxRb_s9sZZ-ET7)EH>m9=%CS5@ec>)=CrnjcYPOTr;t&9~oi+svu_J(2o&Np_lY%2fyrMG9ow`Gwe+Hj?k;m~p_ zm3no`iz^hbV_S7p5t8^`#aA8cIiXR_?FEjk*fpDU)!Pk>pt|nh1o*{^XW~!{(Xi6i zSQPH`P9sAITkh+hXgw*HD$20%-q*1^y(+swOf(DUKE-@of910GaYnG-749W9$!7;Q zqG)%G>g17jq;d0qK~J&ia_1v5QvzCY@hg#fGA7jeR5}G+5!XYJi?PF3o#q`fCPI!a zhk&<_)+@prM6ST~#ap&b;&e?FJ0rC*mzO=58T5^q+3lDSK>rZtwg;Ll(YpvwSCYaPwTByCPh-;+?)%oOKAiw&i6%f^clM zrr{3*wU#;NkR0dkDR$nmY^#hsa~a*Nn_T%gd7=m3!r{wckm0J6L9>$JFPp|?r(Va6x6cn00Ca*7T7O${yhhA5jd{ z!#)Hm#wKWLgJ3GtKvv>i@6;0}>KD#y(BxQs%|2qE%`uf4Rdyz9Alo1@H*Pr_otHa5 znQU*Kb3Hr^U&vz+g3bqW?vm-D zcSH1huU8O~u0Dp{p){4+4wl&?8y>H4gFen+Vh^0RH-+uw&m%6dT~0Qf!oQs=SX#Z= z)XzrZEOdh9#lmTc_)KtM;dgsloIbn}#nePHD_0Eu)L!*ZlsT`6t1X+wzKDzFmSqHo?w3?vrMS=bw|c^5=yRZDW_j6S5aK!w zQ6H|2Vsw(d_DmVQ86Q#)1rLx4e-O0qgyJ7iUcGYs>eA(_IryvE_8IpR3fiYqZI`n} z9Wvh77dS)`kV%B3MD_*y)Z6#45>K#a!a+>y)TM!$r43M~96IfakS}D~*~vDm zGh><|*Xr`oGgzc^bb@h#)U;;Fx26k3<}0ri)y?b@q}F8Pgf)*U$no5%rI4*(ItefJ zPJ-$&T>f%}rY6j$N)}dD+sO|0Ah@{U-jSQKgucS*knze zmk@eFqUMCE4&?%wb932{s@I;qBK_87kOiFa2MRW4W+gXcC|6)#pyL52)921J)&<&6 z3}4ws(g)-;cWEeD*kN!g2`1G5 zYPRY?ujWbvX%d?I9u5TE?;T=K;@c{bcAG4Qee-CN7iNTG`s$@n)zn61cvn^R8InL~_{` zW+e7bH3{YtFL=|{_(}$BpO0|7L7Rj9QS3n*$PEXHUW|T$M_gu*hIu~y3MJ35a z4sA1RAGACLAJL0ux!a!n9g_4wQE%5VhYa%{9->B4J+jW9eb>sHlr8+BKy-Q!p;z$i zt3n+Ifo&tHj!vj=WpHG$wAN7Lwp<5`g#V}od#!HK=&3pu)eocW3YgRQF~g2AcTpUmien2DgBWfPk1Hkn=XoApST9jKc%>z|!joVDMB%}oo=JvW(q z>M^IEFgH~!>e+^SK|7wmi%8WV_%h(zIr$GDu40S81(gq)ax4U*XbUw zw^LuImcI7vdj0dmYext*Riti0`9N&UF81YUI!Ud=LH#hzbXz^vxgufFJ97a*Bx~a(thw!i-!v4uu$Yyonv`&^Z%24H%WntI92z*WTH6_2k*Oa*#feis}9{zO$IleC?Q{HB$3ZhNo*aG>ptV8X-q5vxzubcQ)5d zYX4T0gEwe#xQ}wvBKKn}DBF>W#z;%4n_E{jaI_tyZhmppRfXqEo0a2-+cuQXLj&W3 zI$6u z{DNVEQJn+XUAS#D4wOX)ZmN{4bw+rl`N$p*wl`#0O3M;lI+T83U{nu zGt)YT3Q)Mlm6=B`L@h{`0O=B0i}ym}zZ%MTVWM1+QrPWTS^U3RR;QuRav)6 z`K@bVH{|;pv=mEeCc@<6a*KLk-C1R*_(T?{D4Ec23^d!v8hvVfo_CT4&G>3PCL5JS znCvMBq|hd>f$6uM%Y?M6oM}Od7^9#Tz*r$0{n{)EGmFA<9p9csPLE1&>}6L#RwABX z-OZBxmMx<8hrw`+jSz9Jy;$SO6SIz$OE+o#E44+QuwuoJ7qbmj>YjHqPYC#2f=o5g zOIIGo=VuoS%!%lpc`}k%-cl-+x~k^fW|22Nc1s?hsW|gTZ|sJYGYMTVxn^i3{s`l3i>yoB%~ak*srfwW*53V+;z9vaVJ4w4KEC97E4cmBU}Q zKGvx-xQR@9Td585w~Le1c7=k}-8j}2D4@`JT6=vr&xKIRs8ip6L0A-*{)Djf6FrLd zAuPVeTV|bC7EZn1eRV{rlgIx6!eVy< z1z{2BUKN-rv`XwlSa<-!dXv?<4`IReq;6t6M*#?ncuY#%gK-B17w>$N*|!Gm_-K+^PZe9g~8EA>#8iqfNea=MfLs;F7V7#Aw#or(-Dl>(ebE*zr6v*)zhES7ATK@~T7906+ zpCsMPSDZx-AuJ0`-&vNI^t-rELOa~=sV)_K zhPSd?W&|g3{zLw!eL(tr?C70E7y@jsSXfNcN2PvDyz!V)I3HM7l#+E*At{oxutiA* zOJz3_DAfaoNW*#&P~Z#;!Ml<4_~eDh4>q!u_?c;tp_k{rhxT7Ry^*YDP^~w=C6NMQ zIlgs@tTC8qI)(&?D(RejD?QRiLH-3{C4T=C!je6H0AYoHmb6%w^~nB3VevXU@CDlK zQ&@RmlFKg&E2b_z`okr)^+Dvn7(by<}!vKW(UwzQK2!S3AW;`F(KnI(lA>TBGy+S zR^HnN0IJ+_Vo$DEt4zi^#DOSfu>q9Wi$Yi}eYlP{)+Y{YAPP6?$DUupo&lIDgpCye z3oupZa^kf6UKKNR1{fVxQ+EK<0RSB!r2;@X0Eh#?Hv#_VLqq(4vR=wFn0>s)DrsNy zbwJoqI2Qtt4b&L`Yf2B5UlhJgzwsJK$fI8{&6PoL6!7&7z-R!51^{UOOAU{k1Ds&L zaG9iKfSB3$gQdL#a2ZhA2T=M)P{zhDU0BxFLt~gje4~AQ({EzNw|e;yngI%f3Aq`7 z?Y*MBsJXcwz*!!4+#ehqc=qh+$jI||@76xNfA{g@hu;msU%!3alXG8&$SA;w14Fnl6WMnBszZ^&u@EX z5u<_jU%*1MRjr*-ZhBM2Z!vh>sCC~+)+sJ3K%F|;nRsA4-+bB|GTk!ibHDj5U_9Tp z3Z!~=L(vnOe9-=T@ASFWye+qn2kpPYYnHuS%6Iq<+kZ1@$A0v(zD-$M0F39iKBDUV z*MRoli~i?!zJ4Ni?}>bre%StPD|Dd5`T0Xtn=c<1p59VpL;w8#b@d%};y*W@E4k2q zx3+^*AUqTJXt={xT!GpHz)1GXc#hzUYz9BRA;8Z7iG_*izDRpQ$R8gQrLKYsHA0W0kdgglVqHB#Rp_a z$au-k7>I~RLnp~pQnC?oA}WAI!Ot8EV=_ze)8N%-)?r}vi?V$zcU5I`So?^-^8ob5 z;D;k(B2mZN==EHsyUKhzbvRCq(bwOg??+J{k-J2`^w7CUyyb;AU>m#RBv{mP+qn>> zPT47JE~_CekTSnVA)pkb12a%JgWl?t8wSJAuAsg;sy`0DJB`{r!DX{eb#XZdQ@!9?LvRl)U@t{M691+kfoA%gMlFOHTw*>ysc{>u8uU5ptxTM0Ii<15Y7BMf5vi zEFS0|0AHfm-^m!c9*d+eg6ZjZvE;8~MU9JSZTq`8+ScPxDMj>N`rTXrU!pWv#0c;u zyu0i1Kh^1r;qm%C{9GFe`o_g9x&1xDCpQw!Qi|Cs^n1k}Hj?ZHi#hJ~_ew`@B)ia; zARYsJN&ZHPw{Z#A%l^I-Z2(`AQo{Wf;7g`A(!vKzc((z*WOpMS4e%w@2K|~`n|OdP z;Xg6}@FklW_>@vX5r8jo*vuphmI4Bp0n^CMEFyiGsGh+Si~P;(O5-wd+kq!GZJRj_ zDP@vg22c0-l2#zUaCzXV)9z+2iM|{aZ!qY_wY9(EmdhO&^gOwhH=I%~UtuuhV>)s* zwy8X+VM@rqJfT2$Q#gq<#UFGtp^!taB5~NBFZ6oCjqgDsiR7wd5ow}_X-4S&`CIuv zIe!z@cmB`$TUl9od3goEQT{GX{?GZ_KYWb-Kk^&@Ie+`l`5O>y{LlH@f6m|j`}rG` zZ86E~I86cyDKwUC0E>&@23e5GP8w@O;F2g1--Mwu`7{^Vj1#HsprWn=MNqK|BUuhz zv8TL!j4>8yR*qg_Dmx5gI3bp`=X%Y_v0>^I$Dvy|4`HnVFwG7vnt26VsWw+cFQ7!z zj&Bu{>AE0)kv3aM_5zDZutlcUu7_xqKD&fd2GSl%^7fc4IywUNJ(bt>)XWV#|6-HS zubn7$Q4D`}PgtqrGw=7(sw~h7nL$Q>ASiQSC_p+Z?->*XZy-s!boCUT;I!n{$FV?M zG~DJ=-pW3TcpCU*Kh4-er`kvW&fhF=4RR-_?w`Lc%Mgd;D>aWypTzFsir}d$reE#x z7{kxS2$K{osr(mdk!&Eg-X<XK_BDrBw8%w>~9Mz2Fv7MiW|P%t<>M|Lk-5X04cB z&=X7XwU!F?j<;uG4aYnf?^NpdzBbQ#G6s{nQ)L!9Xjx)t{mA$Z;G}zM-TcJ*uHT&+ z7oMlK-G&pjDR*kU>y`{gpV&0l->D0|b?P79M%JvNWte-mELbx#r0|L9ek*Vh~cSX*`kpw|}puEAmLUQn4R^#AVg1oKJ z^Klx%J^akm6J_@vQGRqZ*@T*_=m|l*yTv{DXaztZmS+9ioJb_hwqKLw_(HwJ$dxHI z{WtK2KuX@Eqp=44Z`d(tbwfnKtOyLv82b}TJ6z-Rwe845(HH(+Tf;Q=*~LmCtA3H^ zHlW8t-Z0gpd-KosMaWvqDbHdzZ&-H@a%8#Z7f-JJzS#h5~$(AAhhL6Kw{ zF8cfZdb}E52C`5v8q99BzL9>94voB}*nx+W1U6kVS-suv8=j_&wqjY5*DG!n0reto zt|zXMG#W+u8iMlzX;Gh*hFW=Jt8MvRO)EPEZ9zJ87Qt^#ue`a$K+ryhW&pX~J}HDl zK|xgW@kWJY`g9bj(kOcE8Shb0ae00Dh0QS!neT#$jo-<~z5VE8|EZ2I z*Ofq!qK~0Z7pnwzw7G*B61}^@ZLKE^4x*35p4Y7(Kd=MI$LomM!{np-hphMQZ{fM& zmnG6NJ>3i2nxosEpZQzzv7n-7RyZ-t=iFYfDTO+aeAE`zBf)z2laD(JK=Lv1Cy;!k z*-t*=Z<~+3dwnxCf=`~r66?#y2oKZ{?Q7V6NJ0LDITkgh`t)2Va?Qz{Sz{tY zF$iy4fGrIg$M800j5{@{Yg124L;}mvR`)e?r{MY)jzrR=X%Kutl_V?GIQT*3;NCW?3z?S!yfbQQB zo>2n$i=@}DBimx&cHmqVNG1XYoInb1d3gyafdNh#fz1O26rtWg#!&qKbCgK^Uq^}D zJ6Sca9-%4dcwJ9uI3N<6l3IR7)Jo<}wIZS+RItK0 z()6)zncU^Vz1`1Zja8i$I=~p`0e<{LVuRTiTeGh+Rf;Tai$$@U+M>M z`@VjrDvI7eN=)95s(ss9*%ygD?D)RD4jd((d9)u@+u9e2JpvALs6@$VD2MlcRIQR6 zLw{n091DoV=CN!x-t%z?&&qimaFn>wb+aj@p zsM#QqXhTdu0eoBKjDauZ$s_AQZ{(x);4E1dMAC?%<4rG!mETnvP_p?y)B>QF7F zMUZP-t)L8z6s;sC`G{8C%I6ZTE~uszt=Y?OYZIxx{-j!@E@wJlq#nPPByu}_H&Ub_ ziPl%7F_FtrqzNl-B+?vv@{~x+RYM7pJJAkDMefdBpcA<_8}VJZbtdth@crq$8R52< zRZoQd22_p@wN4cZJyU)c zCG@=U+mGYJw`u%^Ug#V(5gMr%>yRHURaO-mGdJQ98ZWVrR+uQdz##PU+?5Z4lf*>T z6H|qGbAriTMj;_K{h(Sl?Qos-~v7N?Vk`me?; z8j$2g;PIgav4YBgX9pNQ*xHx?f#1Wi0}$-J5_##+6bPhW0W}~HfIYMWW@V)Sk63rob;>_M@iT+d$BCKWF;m$9KRN_&?4BfCv14als-$V30oWm?yDA=(x0%%v@t) zX?QgKDg%qV7zq)Up6njQ5vQysm8#oeR$S<`#bHpK zRs9?=X`<8#qSOtdG`I|8#d*&J^O%PnwZ6)283|;?1vDM@v*JKL+|U~cho5u~Ka7PR z7Nh;vuKi2r+F=g-(6IJbz1seW$~qZpn+CPd+z){}=0MNo?hmS*3%G1Coc1_Qhxq+8 zxJUl}#LByt!nYCPe+P2$9z$3Mn-`JQr;Nq70uliv!1pIs^buXZLg3eiXs$g6UwxtH zgE0?IIC~}iuO7GGQSfUnc|flk7&8IJyadNgf@7y>&@c9L;7MY2s^ug^%!4Dm9hY4^X{+*Xs0C?Zx3Tx6!n}7uP&BhKO{tcX20+u(x?^ah=UtfQ_ zxaAR${cdPz0^;BQ&Flt*z#l$*beIFLZSMo3;BAkG+Xr4ed^!qP-VUPRe=S=Zdj5PE z$bpZJjsX|Yu{J$DJu@>4(l1vgQm6pnzh4-_(9Ve@Zf#}GU9;hb9eXq z&!0aJpI+cYH}p??=l>)-EB)V)oy9DgAt4wh^(^soPg<%*D>P39gIwIwMNL&{k1M@r9ODRd} zY?XXb+b4&$YN-cpKha9;+dJ(Za5D#XbzZPc>(Ex8>ZG-xL>gqdohdR)EL6MN@3&aB zeS2H&>TMOeW2W4~?xt5pJ*RuBv_TWRiqOqw4cZb3w#5WX-36BRv)r27^%|KpF|vd6hRVJ%+vA(&E(279`l21fGXmp+DT z3t-?w7bm-H-~r{@$)E~C@h9@T+NorEb0K9?L-*8NlA@#S@J}!;8nvOL&C*iU%loqCzlSjV2=r2w?|{G_{U_t`{_Y zi(8I?Mt4_*P0fR#T@>Nyrn92D^eC{t6;QZL$(~XKqr1rAvvMXVSlf!}R+JkA#(!Sc zAf2z)Ri8HAvs%m~F>Db7hWN2tPi5uE=-f*vW9eh_hkJhB+Rez4qBaUWv7|x_-SMA`O~#MwZ12wA<4$1Hpb>OmbN!PHW|CfTW8c<{&@d& zMuqxD;f+D%c>3vw{-+>-MNYBNK;QZ0;_zsE4eCJBq9G2`LU^o?Y0R+pNjXWg7}A& zn22YvAM2?y=VMRrZVnbW?mC?~9sjvLBDWDlpVos$_c43AKX~)C|3{DVxIP*r4VGa| zecU60NgJGzc>UwYXZNQcaTFY_FX!6j$bI}7>ODso(IAIPt*{P5?vLbqbPWowJa<;8 z#3x_MZu2mLJMqpxPxNbeHX7;5H)Zeyj@57?W9Vqe?5vY7QjUG5rQKSI`{u%N8j70d zem80@Kbyh8YZhp`H_@*P_t8iXMAyaw^Sj1%N+B`kO2d3K`ZgO0CoS7W50%$B+5eeo z#{h)b-JzbS{mK-rO#yqL8Fw0QdpA`_l6G}P?_|h)s(?5!zguK~2nS(K&JDmw!C=9= zXf(7C%zQlIy2K)1y9zMBdpuoIk65a733X)=Wz#rHs{!%;!pAd+E)~_Iku=~+274>@ z2y%m|IaQuU|G?vz^bL$0@|%G9-O~MlDqwym=h*%OAm*lOU08tm9WPlADaF94)sQp>zSx7W8#BM zlI-VsrqK77O1PwM1g{CJwy&mMyh>3>hIzGqlEMe2#TG7#dflJz7_4rq7FEYoOPpHQxbq)eea{H)_n|r#GCQf+%|lpmYQQU6E?e^1#=ZB+l2+B3IrSkNlIN=y_+^X) z^i7*Ga|aiNPkkFOo6uJW9k|aAwzr+Yd{%v5ZOhAxJa&u^iR2d$5Ec{^mXMH^l0wPJ zDagw!0!zA+CskEc)YQ~8|5`x{9L@qYJVr*Q`>QUKGp45IfbZ7a-12t|t-ZYiunu!@ zI13cfo;~aQe^NvX2yy?Ri#7u2>;aZ;k&#iq<+z8Xv>41`BW>(omA5!t!a?CpVnRaF zU){GUDQT&x=?4`!fc_Tv+Be|-?-$hmGZpUsI_dvbu!2PD1lCMF`&aLOTRHu6E$#?V zR{MK7_1DVU{gu_h((0gJ=b*Qi{BIT49;~Z=b=Lyzwf}E)*8;0Oo`1gDli-^C$2OpW z!z5F^3R%7Ux7zj$LH^pGZL@k*CI_oMvpnh4Lsz?o>CnMy@8rE09;3UGlME(szxKvX zv|V%H!V{C}Qse2PVRIAoRe~m1f*@$s>^}Ak6@}1BEg|_h#Vw&@^O6p1eYNiW#zHc3 z?dH_QItf$-+Ga14&)vDGxC!rxJLy@d&zk`CHu1u$4*C{jj)fDL^^V$>OM_>ZU zv&zO6y2o@9pIM1UDhn1qk-(@>t11n8=wK4|D#vRp(^E1Ozv7Yhs>j39o6PxlEXq@2 zQmc-yOpRA zN)a4S6bdTxUo7M=I8P=VdHyBC%`i<m4(IYHBjZ{AeJtVM-gA>&Ptp{dAHrNFFO@HW0<9fH0#($|6y{ zh0#e+EQ!0WvG3N=c~rlP3#bvqz#?+00&JxO)`0c#qv9XlU@q1w2T3#c)6sJ++wr-n z4^wf&0z2~x`BRz4ex$v0jes6aq;u;^^@N(AORb^8>nJ6fSP&#=(DWG9ow4^ZpQR1Ft8^S3 zFEViAczD(|C|`T7pY>y*%hlIJ)~#~)Rh{)4L-RNn8paI{N(~;zUBZE>pO7Jw<{6wZ zi>KOD{}{~kYknO-qCw-I#2@V+pU_0c25p4uMV(si(bWM#Ad^iq^D3W}Btd=1y3o>2 z&O|mjo!i-(AYAMTmiQVyoz1xyMG>?rYHV2WfPkaBCFjy$EUaD(3baCrgHUvDwVmacU-GGx@!} zDNUqjU_(5o3qxQ7i5c1$KRcf(z~J4@t7(Rj+nJ}h7NgEizk%&wFNU$`K#$TiR;aN+ z=;%cue1WADS}v|&MVAb)s5}MRXFdl0&pXN6(P?|1uUZ++7T;HdVzJx1|+GkF-j$&=-VlZ;ZPuW$OyK3}?b__;dy_!(~ z{_OHD5_(6SOph<7IM&cj)K&?ye7H){1Set@`1k`U++z7@i=KC`_rbFU_NMI=o2p&P zB;maVu_WO@oR(x4yfT!7Aigy{x%>KYT%Gx2-L=x34`cDj3@(_pqoh7>(X;IogT}CgJ(3GDM&2AYrgbD-dqe4Qh4LG-NU1By zL=3~@5$B2z$H6OoWo?|e7v?$d9lw=$-3TtZ!25mYvCzCMKHk{l7~TDTeN(x!X|=#= zPg%nFYEyQ_Y__Q7w2?N5rdl(nq;M1MiCbcgQ@B&*nq`1J$}f@m*<&>LxuG_fyi3kK$xa4@ zcZ#BF@#)Fd53Rt@2xK4{&4C4v@rXGozI~c0lQ1gKQz~K2rA}R5wn)Q%ia?a9EIy^A zBn(&s74A8)u0zV7@46dudzVR$m(ST+)l=cC|;_KT>7^SnQ$dt9%fvNt^h zbKD4yg`Fi+&zy8+I)XUM=L}DfU*8)Qu>a^m-Wv?i;nY3FBT78#)9QmADUv$@>bY^9 z_@z_1C}JeV>CwHPkj*O>7=7&LV&0upQhT{fjWeT+>5h6v9i29R4FaKFYLpUrp&K_( zQFPfvCRn|*PgT9kR~7_Y*Eua@ZJwh=#eRv8EONCo9(l@2Do)eqlGHlLSoYpg`yyLhlf&^xm5R>4JtTAWCSVE4?TnAV}9xM4B3^ zh=NfO5D-HVQA7b7w)o|Fo_Fv4p1t>X);aTU)~rlsWz9@-U%%^$lxMNMrF@JKjStY& zl5jfeLs8^+k~IM<^svOPOrDn7bSxb4^%p(=jRTj1he=a3w}X9P5ZF`(#}&<^UIdsb zI%#`7gDX4fm@?3|(OsM#oJsRZ%#2*@H47xb$pGlH`gEFGns91{iIhraAw0TRR1E-6 zTChE-9>+)`#4Sp3EWx5g9Ko4*?OTrE^XLndEtj2OfG8byoB%7H;uoaDED6@aj%R1K zz^bapuVjJNdabmg9WHCZtTeMT;h7qkTxYCqEFOvt0r5W1eZ`d}@yU7-9DU5uw#kK` z`%qFB3y)%UD&btC&0>$Br?Ly7u&2;4tQM4h=2YF$W2s6*x6MAEjZ-dSIY)z?1wzOe zUFwEP#cN0!?sWnFTtDe#yPE zg>kE1o%{A`XP_Uk^B5NCJUehtzY}1uZ&0qN4)| zk+e&qBS-7wghrd-=`_f*wep5I{368r3DAE)0;i(wTNUIx;+Bz(hXMo3E^f!j8=VTJ z$#D~5dY=PxCouXg99(0h6G6ezT|q*QCn5^NPE?fJO1iWLr%R87T;R!X8@N20#j#|h zkV%WN+~ULCFKBAzIEsVumR}wUlp>ja0te&$xr&1Te9RSiGVT+D^x}xCusim{*Y{%gvm&anZ>b=KF*6@ zG4CGK;v9=fG^lhihoXl=ai%PYaR-&)n8Q@xyq2? zdi)&}Q>aT7J7)Nu^WhXH{W^#84~%+vw$OgP)R(xU7NRB?uw^C37gaH73!XD9u#*7j zzVpfHai&esJ!BwarksNt553EB>_#-qrBFeN5WUnS?Jx~I-^8?j@T{pQzD|>5KU>y? zN!2F6J@Fje{V`ixO*+A4p6Lqa-?hK$*1g@xx5p;IE)3!Sd}iAuNxNU&jjbbGnWXGj3@15NQ5cMUQgdkw z7F!iQI87uJn$f{<3ks&b5?nHVp|mgG$%O$<1tdUmDx!5MC>MT!H0dB4o=t&>4|cj| zi;0B-0&o}ZBk&v#VVuEuG0~&`q!h%Q!G%~`inBrdv)=F#5YXgQ0>$INqtIAD>|2Sb zH38lk#jl9$QTeG|uc{M#sEADfiF)LSw^Y4*j#=VqwEq!mdoorgO1whZCV#X$*h4Ql z#t)9obXhbxhc1x8L?l$2G*{T3iM3wc)ZI$T&(J!K<-KL5VqB(toUy?YSqRU-8UDeX zmSlDl15+}P;sU*3r_YM6R$1}Vp1Lhram$$TiuM=dS+XoZ5`X{&44ct}Tma|q3k=Z3 z3?3qZZzeFJ*t~r>=LobwgAq>V@3r-DAJ7VbE~`Qun{ts-sd49I3j^WH^?^ouSx^r)cn=(5*nMz>&9 z;-t=`Z}&tBJl~s37Q9dn~K}OnQG-n&UB_+T$-a&-C=d@X7 zO5dGiTNLlqm$lGKD~qPnbd=6jKPKKw}Dlz6~>lK(l_opKl$T4^Ws5^qpT# zo)7Ju3BNNxzcPPXe}*Wy@K|plHnNr}1X{0JNZPGQez~ypeSs8NlcvA8@ckKC|5;|u z;>^He{+*ia-;2V#i^VlTrM@p-@xQ3NL#w#+!s#nd3k}uq5|voUtxrV~06Y>7$R>S` z_LoZ%Mu9DDx7k)4{8tJT zl3N&c%X%!(VfwP12x|)kGd9CLe}1K-W@YWpN?#;kiH-Y};7eiUmx4y{0UStU6lqil zAUU8OQIUWOLVF$R5gs8(T^%i0O2jQK&wx6ps0Vn~C$}MkRMb=!^!qecsCU_0f39tuD3+4F3+rR*ewYb!3OY%M}@G@k*^N#tO@#| zDv2*&+AhDISrcMkXDrf>tZvLuUKZY2y{3TDC$h9t;PNfwIidr2NM0H|8DtKf)0iacf%-#d|(vd+l z3~wQ#6$cWogX@sCw<1xY==Eak4l8B5fU%uQSe*dCjsV~zSP13p%j}UQVKg$Gf+@fv zMDAkRcactofEX;QnSJNY%!&g8lURr<9*b}J)ui4&wRiD zRxvdWDDGQJ*IuF?0$})i7>ND*2*o7|jm4OXcmP1aQ2X|FTM}5-6R%KLXEBR*dvl%Z zr)zh6eZPfWdOOATX{`o4Oo9aAkb>UtDYmS7_rBR**fo6hE#btfpW%}KkxAqX13jtirw^+rbqmcskUrQ+%t370n-TP1i5(nT=r6R5p z-=3cRo=!z@p*cb+$Tc$J))~NU#?>YKrME+`ehP{luGt>FxO zAmmdql{$*Ot5)Ffq$NYt&%z*C2RezbIN^$-P`?oCSp-Xi_4ZuCH;pZB81QY6SYJ!k zyJaQLTE0CxT_h3Qba~&Xh&`7_IlMF@Jn(5I)=begyw0>!@2IX-(;y{Z!P_#y?v}(R zO9R0#&+kzvDQt!V>UG!WLHycChFB-usDt;L^FA&!q+ z?gmfFFIIB2RLXcyq`xWhee{03TIOuQP5MCj-e#9oRkx!=_^a6hwYH^wYUU-8d&F?p z)w>JrH{-0yQykcBIZeewdBv-HJ1k^xes(-Ae|xjrTIt@nqc!fnc#nI;EAw%pHRRqeFjlrlK&bUu!&K33SB>#;Zd(_3VZhe`B0OoP?c9L-qGVjYhQH}yDL zNHxvbSjtVPIm;_O?{&8C@i}mIJg#5t>|}4oZR+eCbj$U=Tb#O^n}6A@dB?Cj-2fmM zUgv%y1)|~MpC#eu!2*}2x;aKQMUw)qW@;=2z#P6h2iJh{UXi!Y^N_;zU#q4n`&nM+ z4?695(m-|~==RGK?#INdRLs*p=(|N7zBU7`@IkApXsEa*q#n!lOY=GwCo3%GBA@zZ z4Y=;7y9JMA_*K1EP|fwoZ+t3c#P^kB7A$HM1lA?{lewK^Nsv4_VY0RtcULxq-6z(g zWwql`?}63!G$TWm)q`kptwO`qz2P3~n0_&^ZDt9h=lU zFYHf~WaBN@Q%!TjCM(T_ERM9dnNN?YHU(nM_Z(4fMCG{@IDQY6pUg-8q5rri_P+FOMuFKa`2k}@;@sE=Xp zMrW`vIQgi4Weuu5(hwLB54xj%l7|C@2MTuKS?(gua4i$V0`&+4bu+*-)dcsbkR-m? z5KuYwiYmxxsU{&=|Od%}-Pil^0$FvpzwiboB_x)P%>61x9i?0!DbWj#}~v zCm^RRrRV4MX&n$EoKaGjKFDKzCsN)-Z0i*mD~O>jQJ0GsLnDNSnT zg&%m7BvqUbsY;d9sK56ck`Cppo{%dbWI(4!+`;k_;G1ZG_Ay*KFN!3k!;sGMVc+8$ zXdF@0PY~5vG!FOJ&g8IxlfQ`5xeEAlW(XvxcZ4%n2OfkDWyu(hTvJX>FoPunVS@#x znAlj+NuB@++{*j26!g0BAUa?BQHbn$k8E{`C$sO?0^rSs;Pc%FJ>u_2#fqnd1FI{C zNebeHDmEZ?uMq?qzRn%V{2Q~m9AUQY&tuRl@&#&iykpT{By<}xtATM;-UGs+he-T! zjt-~AZue-41dIO0GXP-?OugN3x)ED%%D;2WQs;P?IN8R0_?okea*bGGJ83pJ zqCl-<)weL_AY#`*V8{Fu9jQ{5bO_Hh4$1Q?LL?&R&1{PmLu^1ET-+2hrj)y{lkaHE zp^qlJ=J~_!80?2|Ihcug;PMhXO(ErU$m_{5vDckWmx^(&^M4IpjbDJyGuhHq?Y*qulMIo==C=^UQAK~q_h3;$!6nb0eRQ?ODwyFy1uu{av!hfCNgzi22mLtHl;n> z>Ua*Kqt($wR6R+sFd?h_P^Q7H0xakD+BsJs?OK(~%;6d1o`f|&`ik%P%j8Y&KJ==EF&=43Ddf5GvZ&?<4rOXF131#cvZdjK3RFAEmqtB? zbQwYU?5*9iqGhV_2Ea~=r(;^`Y75!WX`XY7#VBOCl;Rry2}v}mBb0osINbHB&R*-C zrLQ1u^oLpWb9X7A=vmca=6z8LfF5f(CLSLEz=BP(>j4g8k-rCTj)xd9XS{KT=UMEe zLHxxOvpU_)qoBa!v3=P<`|iEGgcEfJ-yS?RHV5QJ3!da~K6h3%Z4GMo$C_iLU&j8u z1L`Rm_}m}3Z`?@C1@eq>DSB84M=awAkZ+N=G3UJP9r46AX_m{<0TrQ5RuNx@(+$s7 z+};L@i}uIXZw6gz6v;I5E`WMHVp3Edan9K(gXl2qKxjE@f)PtEfDkxy~js5~V zm~Erd(lTb)h>5jczFf_8y;N8KMYh(~*8Mx&%4{1oHPtmXGNPo`~$tddPV;`d&Hz(|BHD22X|$% zuFQ>zWBd=q^{<-n-<0cLNns|S{&!HA3A+BnsQ+S8u1x4b#sOejh&857|0{vS zn}PHh{Iaun!uj#+ouO~j%lH18EAcyhc)*-1xt(<3uep+RV5Pk<{ugtulrCSS1Xxd;U2)IUa;`wZXFi=VkmT0eO|u{#3R~2V)@c=w^0pfw=6z>c!t|{Ul){ z0YX!m6=6LK%*A|Ne0y&#Fw$mSD>%_XMLI~*)h~<+Ixqgoqyi{8KCK&2`@*X#DgHL~ zD)_f2SQ-hWZ2(Ib@vIV>gq$29TyAz6w!md6o0nIsc-HW@nh8kwYxSgz-1*$ekk-ZD zt?#bZbvSL;Ge~>@cu}#uP8v%QM@uR;HI$Yq>hD9muRS{jVAZ>Wjt!}8A;-71vBq&c zWalyRF60~y+(-AeB>^9yNm{AaTydV(G&D6K;IzUcTzanK7BKCGlv3v)!00{jK*7ip zJBCtOQVjff6^%}+FD*p}HVL2e;g`XJYVj5VycRZHa&PG(b*gL9c_BU5bG7`3z2Mi( zOi4Vwc=_w%8Ie@BsGrTZ>Bc04l9{*~4y#k0hEk#WbdjWXRh;u>*d~W873uFHl&~&< zPcd=^rCc9}NsT@Z3LlMoRe7EMT$up2;6QMd(Fh<5_B{iM;+8$dHpM-xwf6ZA#DBM# zu@Z5OP0k`?R{i);Rk6LJ<_lE=1j=M@fq%-7n$U6_ksPaAPa0Nxb>|)DXq^)a_h_%()8}vR z-ax+vg4)`+`t?|{xkgJH#@bqv+d(oD5z0vlB)B6|Q5!6KmS=QPP;>Q-#y!nnhd&fF z>8Ragvo)Y1;lWytw@-!&0cNIVj0vX#uzvOI1-?`};pLSOo#tB*B9QO61Gj9bNIszu z$lpCR0@QIt;o%!c0_D7h!n*(wRmOZ%RI^!GfI%=koinAuUwLI-4Ig2|$8d5GUS*i- zDXt)W8>ls$3~|J=0tqp6_(C?8ro_Fa=+k@XsgR5BJM*$V$D+;?X$wnr;sS-bP_IY_ zrfSI7J-nd5I3b%5oU4ieA(08!=F*IAEJU$61nF%ofB4d@z%`aY!q#)azc+tK&4W|- zycImO)Rc@Jb9IQL?0Ha%q1nudSH=}GjZoB%c3_Czl&DJIqA@;uJzphx&R} zEAb{@4FHPM!6<%>tOwd=7|%0WWNm@Uh*1Y#>5M2yEYBDM$p~HN!eSB6A0YV}&<)f5 zG=t#>#ynQPIC;qR3NFNYpj)<-(RhojBgq(MlPwPRfVIe%I^-)I6+nmaS`m&p!FpRD zU~}yZh)-&*qW%OXwlKGtC!G^t2l??U)Ue1ZwNV{prmVN7v(B;GE*qEK>un*B#oRC zz{biwJeTOw_cO`!l$p`E?z=o5Mm_)6-XO$h)(>slK(l!b5tR6^`3nyt6?tSyx&EO> zMAJ}+O0GyQA~p)%+atJ~t6?Ex{IQKydqT18>)`vX^5puHjV_+qou3-wS)Tds*hMB9 z`jBUD01xosucRC={{U7P1z!XG3^C7U35amWKuB9(k61m^vhuT8>c&!E!;@3J@5Y1| zaNdUJpCAYHk13KOtsOdm#yss7+d#d`5O_vF$%o4=yslqhd^d0xySKM-6#UKdyC`2- zFkf)}gssaVmUsP>h~lpxH7P>m6y!H2L$~YE1NlPD1MYPhWnGvp}26{<^jd=?J*@O`s2xrkNQrQhvePj$aDU#p-iN zx9?^y3wJEtRXz6*{z_tMpwpESKmr1vg671t+6P}b6(@LB#-H=IIrHtc2JhRQo4>dO z`$^2!(b)OezPXiK$i6H0P}hYS454HZN7Wt>>mJ%DqBIq)8GuQ(7-{p2T#p2<_Y{=I z4c?g6ZEWi}cRWF`@s9_>-+`rO!J8x>&H}cBr}OI1EmL_4L0)@ltOgzsXjx0Tds@KM z#G>g!k&)LW0nEY#){Ird5yg{K%Eg@!Cc{J3mBkx{FUjg~`!ol{q^k2ha*a_;#%B6# zvgdI3|CE`7>EpKr>0YXh`+Tga*-MEh+AlY{q&k8x<@$03O2PL{N50+u`NrP+x)yfs z+{VBStCH}Lp~?G`d-v{t?PQz$+~=qM>A(Sf;rp+cju3_XXF>zM?h*c6GaBYgB{xE* zXuP{u@m3DFPOKr*%T>>i$nY=~=Cu~T{DQO(BkkbtD68!NL*Lw7DccXB()7R%G;4rS&0wIAi=S1#419cKm%*BAO&6c~x-aBUzzhR2cob_cgk}XV# z3J`@z5PSd)*~z-lPjGAgVioX7s$deukRn1bi2B>-+YT<^O$=u4M?o+o))70=Hw5Xi zb9Xd_?w$3dQWDfKN;)K@uPSGn2^vpDW@ljbyAenYUh4#}6m+}*CexWOy22b279ee? zC6uhH678%aCC2F*s;YI8b2Jlz%Rr52V8XUz2k-NYW-5;EopA_NMB}p_pA1_VO|No` z_CFLnnSf>N0});5DBQlL@HV&Wq7t3!SW&-U4JG4ygN`d0=XWSO`bCO^s+nn#^Lb1) zSS5FKKhsA-U*SO15S!cLaWsoBk6B&+3KTas7B>gxSxIXSQP|m!=b0+!jaKH?w#d4O zik~i)@Ltd4Hbv|Z{ZbkrhXjG&6t)klaYIjzL8P>diK%27TJh~e0QK! z^m;xu3-JM4fW#LJ(XBo?B!B!=@RCo7?s3t?=psWp@BX1wPPcSH0KgL{b2C&)6bCPV z3YVh*d?@VIKsG$SpyBC}7W}1eK=vmTz!N}zN*d||UJ*$_{ydb@A)$Wk9bo|SS+p4! zn-;~D-wM2eOLrT~^`*%nTV>gC@Hjd^wGOU8Gv30o>7Wq@0F)=_(t*V#P7>k(?b_&Y z1ro3PT#AO0uK1P(4~e9$+RB ziX>Ge@gv{kASt43tF+Sn7FoWx#Y+@Z19}k>&-}DrQdPy~1okmJ`nN^7wtu-0MhQK5#%I`fK56ToRFiAQL0>Vh*-kXqj4e@civubSewh>-D4Aix zeyG$-C+-_-j%dY=4;Rl1LgrkpfpD7MC+X>>1xn4=G3_1+qh7 z;{?Fzhp4$a15#lziNgLRwDx39m1b0S^ag7$7VIgl`L-Or`CJ9EX2Gi*=X&-y8VhNm zsZlq~j4Lyqi)uDgBEQI-i3nBHCLv|xtU#*vUp`xT)>(dbN;swE&T142d~RnFr%$D1 zDvMP70oAYsO5g&N*zhoDDLk|e5b=zi^!?)gq0X@Z02NT;NoViJv)g>p@nMzTp_U@~ zt{!-Der{Da!=>G4)eUx0+jM5Gd#H9{!Ph`nEVn_&8qW`yMKoC&l1`l9`GIl>>?6Gn2QUdiM#nCL_5JH++U zK{oV^eb6b$zRN(N5$lsyZj4eh0A)&0GNB+R)AdFq&1Ic^BGolA2EA%2R;G9d;|*4^ z=dI3Onvf9#;G)G;Ap%b`wWV9}mN6VJq$^65IidGVbbm6r%9{b{eX;KtYM|qPJK6fK- z?VWCWN;z^RYa&VHYlC}o6YPRIYkjB^3JWW5fS)E^tEaMG_peP+VVR&EsqQX%KIJio zj`cJ_{Klc$x(k}SF8NT|8B}&Ep8fsOjRLKb<`Bg*edQ`WZa>$%Y_Sk3mQ+VY;%R3g zsrp@nKFE%}^Vtx^tt3lzuj3!j|wHWeOK`93UDA5Vv*ieAT$ z55;66`Y9Tat)!Ps-gaq06K8xCwsoql9Nq9U8}ZoL`>j}&@+J{dn1VVK%|?-tg$ECbvNYP%Q`3pmSA zNY!b;kL&l>fsC=ppOAB>7UE^ihh(b|hgL0c+XvAxU@8saCD!|S5YSABRRfy}Ay||k zzG^e^0F9(VO`UZ2gn^h_ZR?3AD<*fv8rq5-Z8D#Ws*?tC;MC}jQ+m&P<&WAzD#uK!ly|3&#m;S# zJqmuriw0qFi-Ad>g~I8#0V;ecBfnCdzL16hX=?}VmOK+IN9Rmzk&P5M4>Yu`JaZX4VN3NNL_RBfe^hdFw6syzD*3C$7L^LV6t6R{h< zB0-U>?e$gMw`B>>E{qHCoRwC0R(qAStKMSb-jBw8uMv(;42^mCs(9@cmjNH9R=tz& zT2sQ>S^qU5#@g%vpkGVejJZZsIq>-;+aVe;Y>DP;EfT54U=226MJgU_OTt?ve4?+* zf(ll{3Kp}UDVP>DaImG0t}EK1ZSd>^4&)EiIt{&I-@x zFJ{;jOI9@wJU_A{Q_vK(nc4txWS8coqSBVxt=Zp1ecGx#%^S@x*W=```(Vw0wbaOpCsbLERcq@#9OL0B1*ROS^{_46=Bf5|(XJ-Kkzd5F$l3jjZ*Hxm6ezS|gf zI-rQ0PZF+K20bdDT3MN{MtiLw)D$(Z6A{S^d7o7gQ+1ccMBc={z~O%6#!OV4tE!Ou zRFSG(*)Fu1-CCLdwvs1*`$X-|TKCSoXFJj6>}4;eKG3gTIsKk;enobC3+q@NC#Lz} z?)yguTDCLUo2QgSeTdN~t3w$#?LS#cT~TYAVm>48F4!6FaWS&^!j;aFHNtzcUNUwc zZd9{wG>naxd^N6_XJU{tvMb*|__NopTh{Kq1#h=rT21&EQu&dd82Za^lRszA*>7XH z012P==8U^4icu05;Mgy|9x(CreeM_EJC_CS$?k#APMKuPmZaO=!|WS79MRuj+0du= zlph~y0H}RxU`VNYUDLdLO*eN(>e;&#j>dS^Mzf^GKEKZ|tF|ofeRQ-#mwf+tz#z^*?P&j3<1}7E^V%z zeiN5ncie9H-njrlhHh93LNP)euK7G@XDS)cMhf?sy`L}^CIPl+6_sclew|ka`_`xJY&sNN=()U zRhs@zOtkxf@o3Cbh{gjuDaG!GI4L;MF+4B8R9R6guuuk~V$70<;t%%gG!y^HZk$<8 zS4@6e>KmFiD@q3#HIi?QVk}w|r!O=g;fR9{qunPR}AuA6LX& z)hzd56XD^LG3n6c$f=h*!{TXHYqz@DfAW$cR6Fp}#@s_}OaD=8d+_h!Q}S>d{@6C- zk2A*2{#oalLt8AF?6-j|j1}#%%e-`UG+f>k$}1hP@%rw)_0Qj*xHsQ;J+FGALBcI0 z>-TM*H{;z?2ynXoR8D{m2>g2bE+;Z1I5GFuQ`Jj#kSa&H7Rzm-r`c=Q!1#LOw&N_N zx*j)nxd%ms-DIxwxiBH^a1RhqB_ZQN|0YLn9q3WMxf;-)-NcmsZryM()jaUId&r%* zg=O)?*^-fm)-;6!;O67uukTKMymjQIsT4iR56|UCTC8%qs%&N~we>}EaH!%kHK;UelP50%c3Wsp#w zE%rX>Y=}~&10Ug-h;jpo%9Y`W$kQ|8^YFRyxDtzU*(RyBALf(ahj0ltjwbQ&n<=-@ z@4T3kJeQEj%9)m$E0jNx=aD}X5epDpFS6BWXrXf7O;8Yo8}SVNaOXCe8gqiDBVK!Y z{)uKJ22TEW z8Ff;MoZ5MZB}UY}D>^JikKOBcW&1(F4w*VG4fD8)^kh!tDOS{DzC8&axo|ei>y|~n z5zf|m;g?C5U2nucZ$HsF?O5`z6MoAWZa*gIGSW8K>eks7B|39+-fM7R{`rlwUz$A5 zB{Gz6t(TpQ3_4Y9lZtTt{zZvw77@U`EO|$9zU$J&j+=RtiG&PrR&=uTQRs$)8TgqV(7z#HVHW=tEiXbd$2?sd~J5~ z#6+8m^#5LP0I>tS4zI0mg(OJZ2f75yK-=a0EOMU zcdI0?^dMg2%)}+0;cy~Z;krNj*)JohxwKuP6u}U_TxK19p z{Mh*LMDVlnk^I+kmtH&~p+8k!@(ET?(VmH%RKBuS{aW*J;zV@cr{bOJH|!f# zkM&kRT~&P_V)ROjmT8Bp$JDHwD0$t>jaSP$D){!Z@#Uv%wV#_FoC>oQ(|T5OU9s8d z{44uQNdpZU>Pgl$;p+{TpD+4;X;0Y+_q5lXzcsJW8C9_2{V}O;@aq@iIl+hp;FZN; z!LMD;ry_!0X}p+`ROqQ!SPrrO+4Hn7{#1KS*;*Sq+$!OMbY#z(KLmezc`Bi4S#DW&k$?OWgODeRm{eW*c)9FMq*VLVS)I(8Fr zR?GdU(EcEEvB9k7aAM>KVVQKz>d)c?n-ZDpH@HCQ z_B{ud<7)Gjd2G2B(Wa*kl6-OOz2(RLdXYGqieO+4LdtvWaG^P$ag|-TJY31EN*Nbe# zGE9U>1Q>5DM{Yd2l}}PTkF!r%JjSB_eaS~VwP}`%qHk-znTx`TH0}Yqy~l)q6DFPI zFCFAZb|ie*1{?ciqhfxU2upZUcXMjF*3(U-f;sf39(NXl@ZMZrcGaU*}M!TA=5Aex*6hum`3Y1U-`W8ejl1ac%v zKgbi7NJRKhVZ~4H_Vi?b2Z6hFb)?_IxWolu8F<$B@IU~toSJi(&Y@amg2!bZ3ryp* z8Q~ruq9gGB%Y&B}@fm!Ovovz#UTR$ew5AdmgNLYz;}!XhWjAx;Tln#OP~xEprm^(H zn0^f(IgXI#i-SpclESwKQ$+M;F{6iKx{K`-w(rO7-y4)vrp0G-9|42=ImyxeM`DQW z-JUQ4EvJrNtYVXU8E>K#+li{A%L1n)hjXjbuaE$XMAtAX>w_tG%UOlAdH1cwdVMHn0c0x)3?Ei+w8-6 zka&jzt$JWI4ay^L)_N2gU6+%W135bdygrxH{764%{C;aSu=BHV*&e?93Mn#y9G-0& z5+xD@246EJzdvP#$2}zLafbOks8Y3R%wTT<=k!*=h^4j8V78k+W03yojycP?%?B?J z9=snjtpSV#(1B;OGm~0j&fCU{g)rneo*{*Yo~37q(V)1uCP_JI@f2$A6fmp~C^!W@ zQ)wQGg*6mfN^NH()tN*^0b}-zu#eK7N6akfS*L%#_oA11UM-69XZjxT#uKu(^X|kE zvJ6Soq-=w&*cnkvlaD4QuQtuM@g`5iZOz8Bj=l%Sc-V;HvLdyBc0`ye|Lpo)&hn#~ zmzySub-c#IHZKLJ>F|3g+0alNY#2?BuLCOc7pH=A1ThA}*}zNBvYx+Z$1alZdjn9v zvyvDm$T+Rga;{P@8J?g_tiYad+BdgWO|++&hTw zahVGVw{jC9A{jUmkiiEKK#)=(ivT_n5C;ftcR$yJNap4RE;f-M9^}ZrbTf}uhebYC z5;<-$MSU^HS56p|&uWnw&Bwt!M;-~ew*$ESrWf-jYNhze7ic59CYeo~omC^iM7lF` znKv2tn7`QLVrLGR8;PAFC+_iz!L38JJb&dD`FXu4pFyJ4VBszB;%lC*xh0LrjxMd) z;zHTF-)-PK=Ai(X)D$H75Ei{hmdC-C;QUchOZk~g4Oqy@P172RSHJaK>Ga&wTu#-` zo|M;0yrBH=<7pIm2ztdjbkVvI0FB?viry|^1waLUGp}pTF*tHOj#FWejHrR?MP>P5 zVT_bLqp&?#a-AR`d5Qb-k#Gt=h6)+BMdVkLd|F_lU1TxJ%Sggg2#FjC0EY8%5c@Bs zhu%$}Dz+ox$9J+qaPZ{uDa5<`-PeF;h{MwQd8|0-cnR%o``w*$2m}OtRW})I+Os48%mI*@$j6*|1fHog${;R2R)^+o!)^z0;P)`3 zOmYf|JQuc>r+)81P)Du>#>cpyQnMx?$>BuwSa@A6;{hv9B3Bi9=65hMlRPq6w=)nv zfbhBzIU?+AKk(X0rVjxoOHA{nUrO&cmU?2W7>IDF1UR?ABuo8@Hjf?AON+)@VDZp> zJ)|>{c_?WlJ#y%Wtgx+M zl6pV}6~gKRa3b9mG1ZPR&4ar=^jL&OXG0YEf>sy`kSKn0F|AoIt=Sj1;DLM7nr=B- ziq0^^LAbRBb; zT=$>?qp?{gGO%H+_vK0e@$hI2Aq|4F0)4PL5>@X*%b|2sg=?+VVuOCVm~&moN%;Z| zWh_(&70W*GwA3JzAmE?9*@1T#S2~LW``}I<{&wymzi&y zMt#j8%ySo-P4=o}m7aqo7G{pVwz!m^cBnhXBf1^7dB2JiSyv74DunIFohsHVmVJ^X zKNDIcKltZ76f!rGOG*nP!p1+{E0PRj(3S>!@X!Er@&#g+vqx6%OitBB>AXUilYZ1HmF zX-fGrX8&SA?`w#|v>4&kwfymPhlh|w<`j39okwQWB6(px&lh0wC;Q<*{4hGU-A%jD#SQgx7<4>qosaX6LH3T`td5`Ua}&6yG+F1E zFYk}py%oOj#o7J52=iLhw7~;uX;^&Wi3~zo93Cp;m-U!IMDw<>s38d1j}1Hlf)rp6 z4sxE4BbjI-90?Cs|QbuXh&~Iu%sZ0#jWjS2@2`f-ZM!&+oCJtn zo`Q^vWku3sZ+`k*f3VmC&pdeJCzJC` z6+q6dB}X{;R#HdjhKG4>bsq(zcY;wUyQ|VXATe zt}aIYW5gA5i1|l{%M{@<6}U`_mx=BEC9P#f6`9gn@3Oz}+@R{e6@#_YZf=j3>e}|8mSi@-~^}#Q!wP{-;QGY;2rKtTJ7!Oa&{` zxymF>nMcmtnB=JA|6P~G61wt#(`8u)R9p7^OPBQ>cm46zf03h(WqSW1M?L;8abq0f0Lu}bua#wqdx5F{3~ua+xYPI%~$vEU;ZLTadf5}Rc_Uz zziZ>ZAFp+-O_h8uTrPr!_6Kw`GlHUq_CRpP~UFJ(TG@l zDj_o$)SZs}mORuSwShe??anyIBuA4jSzk369Ajgr^_;qG%_K+1&;g8&1j?P^#2c@k zwO`6J1|pA68&zMmKk??r&%>_ySC5557=Hi)&tH+iZd^20%roa{Thy7Hv?mX@W!cWsVM@80<_Iwp1I+It10cRCH9x1BQW z|B85agQ~H)!+b(H6=6IsDYw@3h@f~R2pyz_CLKbPA|-_0G$7KX z7b&8GfFM$mKnPX3R5esV0RaIiiu58Kks_j01uIGs74a^0?|t?;-`Qt;zwWqem% z`@L#yt~lTRv$-^=T|;d#H1AtBK zWti)<=H>hf*La=(rlmFK#?`|snZ-}lH8;!K!!T0F*T@2UyGIXltAZhgYugJv7u;bc^&kC>pq|S%6Jfbct>}&5Y ze?5LkSuc#~NZkDTKZ=`z_Y(Rp*|eXvL;bHK3S(I4)Z59->?tOKey)J`{w_V{LpmzAI!%;ZrrMRlIn*4nl=9y`2okD`#o;{ zPx#|sck}<>adVkd7yj;>YkNfeojCtb2*|(q4?8;tXQxyDD*}S^iZt|%`A;ASV4V2B zKoHwhsAD#r>puq2|MDOIIAi_}p#K{J0=_!@cl4YgatIkc`j`JWaghQnp1VY40Hf#s z3j#s}4y0$l=g$9m8vR{72hOD5qvt7`{}?^b`10>K$c4nh;A^+8WR*tdl>ckr{Qo9G z{%;%vU_oLF?*KRm^=1t~gd`W=%d2kvGk0D9ES>{U2p|Uj2Nd#q`ux9ekmCD~{|5{5 z7X$fW#|Cl}p7SA_6eENfcY<<}J0<4^W z{``4sdl!H|fPM4buls-Y&3|JbKLB6xA3MR{BV57)ZBR$kiTeKw{^0&!{KS#1*8d&; zAivN5cle|5-{FrMDKt-;tmD9M_@idw!6_J9F!eg^R7?2w+r{ArpA4LS93c0?6sDaY z|B;I9S2?x$@c~D8YlC-2+B*i#$UlB!KA&s+ZH=2xD|@arx2yh^ig?m-CcJwoKh*$8 zMMf7&J4NTdvmSfV_TaZv#K-%_Z>dOa?nQZKKq~SssXVwJfIq_OpI05m{FaKWl63kC ze@jIyL0hXmVJBaSru`pM5lLF)m(S^)|EE+$gz~lKzoa7eqH&*1eOIsjLn>nGk&Nf^ zTgxyx$}4(pYcs(eYGx$0o^4}WC7fY1>_75=5L|iS)TPp zQbK24cpxHVi<0*Y$T+tFvGm@0BKKF+>v5ghwL2f0g`Pk8@L=g$%Q;9PeZw1x{D6i; zdML*kce;eRNf1+dOL`}QI<2T6=5Mj@U%d=R)jmL`XPUD}nFU#Ji(I!xu_8^D%{jsB zRu)p+dk`j-`8$4!{f7Ri(H{28^(d8LY|3)Wx)FnkP)3`%Fntm&12lTo;Ni%jVu@Bdaqx_1{W4d#Ntf^o;qk?tZr+9f`Lf6@ z*5JDw-9P#v3I-5;h_B?kZ{vDBW;+`#&&>?5ce5qGuAGeF*^SfMha|t^J>iVv)M~jj z!ci_J3WJqkRvA9`HMOMQwd#Yx^9kf<^fxXLxw1RCkQ=jtgQnQQeZSNl}RtA!=Wqj7kq3ry#c``wDw zG~qRpJDyA5@Y5VMX;B=+c-S>@L^j;5f#tir$HNInh3bCo#$5vPUe+r*+l-V>aSXL> zj*nY*rd|k2$Aw%1%iIY^$vf5ayf_oDC}||TFYcCfS~h-3a);t2dCzT#=1wWdBZ|Al{viXMG9Lr%-VKI9eFl$flSacd8h zzMoEDe@8A_+d?Gio;HD}smD^Z>gbP$bmHA;1Bzm_PM1q>PqA#kB!A|htT{?~%It4_ z@4%4+t=;h#QtXw^+EYB;MmPrM@XA@m6zI+t0}Bo|6qskqL~@Vfn4-vERu`7X&|P$+ z5xJ^)2ZK(~b5uQ zOPutU{gQE=$=gjq7hp}@dgW)6yt2uc2W5=3giVxm??BQ`>5XI#ILEJ$Eu!8R5>(GD zizSoB>0?l=I=kPDr7*DtJCPd(;z!I-8DH?LW}R#};nu-KH}--$8wv*&;f;i_1)|+h z=w*#UaU5ib9@QKwB&v2F#-@~b%APotRz|E?;i#yJZtG6AFz;lJo@7ucDY=O~ zSimZHJ*4fDZ?JkgQI=-y8Cy%ZZ^$~}5YH)Hg2%vv3m%H$!o`*DY3|iqzLY?J#Zwk0 z90P>IZexkHc_igJq!wp1_1z@dTa1pAgWmCLh>vzgYQVLu4?HJirjy)SQt>srv-@Tt z^MiZOxaT#`L#&fKD*8`05LC-4A`^&^^DFF@g9Jt~_jqLAGOLpB-AM0UDSfg?bZY6ISj&Msb7IL5;I6cbT?s4vv+s8torzJ&0ujo5qU(Y3f{<*|C5ZE0nTJ02K zolwk$cM8?FS>~V;8tyVy$e09pd~)2V;!2XHke84rTiEn{^5SH5aR{S0w+`Qr(}%YY z!d(Ymhea{;r^gf@%(^{P0lU~3hOnnITs=zUE-6FrS)gnL1cTb^pLJi1HFvlwz07kj zoe=82`~tbli@ev~-Hj?-1D~lRm2C9|hgzW|#E4BdFY~aSO@8AR*^FnAtddGloB#Z| zex}(uuY~k&k?Y4h?nQBl!RrUb#>UUu_ zkaT0X-+A#me#^Ga7-7Bfc;2y3v2J37_b8705xQIQnlOqdz6F1LiGnQxLG9H8sag|d z=fKiQL*5!R9q`2hqHaEx}CVVaBLuLnVA~i;9LYs zfBgJOb)nF;!Tt|-sKd6}Dyq>6@lS;>zMrtB7g4!c{XW3m@?0Nbo&)2TbboSk@yHRp zeB*1zo+u{oKKi&<;S&pNaMdu0?M05sm#b=L(cW52o?KbQ7vkMNq|>W@kL#}-fCet} zxDWSzkH#^2$H{$;KcLN@0e|bY9*@vqUYLt3oO`_aftzFX_+spl6Wn*IheSomnM{#M zu@`-}&d6xBNf~@w)8u^4Io0qi_p#hl&e(OcBxhQ(7}R~x$*_(f*CKd`PVh=tF`Q>P zHa(si$uKzt`K=^Yfk;vl!>+8g3)V_i@SiGPkHxK9Ffgd*zXpT^jVbjB4s zF>BD(8y-r1y^(w?y*;nm{+9idTp9F#8BtVR6I z?Pwkh$kY0$o*M`k9%ma)vCpG8n#7s5#JQM=Tl#`ehD+S;mWcKM@zha1gfV^~BK-H` z19_+pa}-y5swrDMazZkVO)AS;>VS%Tzl7L)gV@9H94s*%61JirxXJBd65bBPUy!6` zswZY|kV1T^d6Ht`zS39or0=pp4{1lYT zg=YGcj@Xz@DpRMMB(ji6sg3s6UYjI0CnUc$5pDC8kIa;>@;dr13{1h%bEDzuI5<}) zI37#SPfQCSMwgf*RoFxBV_53lzNVd%+u0Xo=CDdYNh5&DlwO^c&p9p@5QMppM_^V!~nutf49)F|oSvvhN z#4+koQAR^YD)=9dHr7btO(#*|i>9X`Q)J1?K0kyGYSfZ3NS?_|{9 z68U-^)WT_hcqv#s^P=|-$aaaL#!fgV)aa?PNKIe>*9z8=Tm1MEBNL~{T$yFTG;A+a z`>`95cjX-KRFN$K^*l_J=m$m~y6I-%^FDawZG?ftmUPoQ;oNRuFD%ltGdj~DX%Ah{ z14_P%W@*MT=S}2a+t2SIMvr3)D!$zUJ&C#H0qr25u3QlggdSVH=5O-&X zK-p}rRNoc}jU%1At*Uj};Ovl2erPe0#?a>mEni}ExR0t{QVaAlq@6}Rsw?1tKx>#$)82?l0fs`h1uwHihToS95dfbqgoZ!d?{?2G-_BJNCmb~cN;iK1CgYfXPsqq>}k z)o{p&;WI9x`!lSwA*l>FIa(OH%(d<20_iiturZHL?g!Hb9 zmYATA3xvEgMs6>m4)Zo|-mXNv@66x0&GZGstV+DK`G#p_3))Ozd52B}7gQBQ#58x) z6##Q3*2b>5;;t>aZ~ct8CFqPq02kY-$6L7d>xCT-sAvb_+of6~rNnmL64pMf$`h4x zH&esF6VB|`qMVr>1odCgu)_w_Jq%Qv1NJiet{h2-npd)U=BQ+JOqj#lipLP@eB_MYsZ%>p!r&H+ zAr?3e7{@94UBtVrd9mnjy3N~Q820q(K7?I45}gj-mPEL4BF_ijl)|Mv5y|2rK*rNq zX4z78^AA~`6X`ao$Zt#8i-QSd6Xb^_mJe9eIF+X9m(*!HRjR-Gb z#uMr@eReL3k#X3%fk96Q%H%v0VYq`0z3;Er!^jSI))Tj%YXYDgZ1lWi6U@sF_XE0@H~jtg7JK*P>Bu1?c3I7B*LV?#q+xmvuYS{>g#Eh>OV z-G>nvS~Tm@Eqk9f9YUHf9;tu#0E{NMY?57I1n_$`cqT24YYCoCgZ#2%{6fsuJKo-y z9P@I8pnP3;QSFXE6P;k2qS&g=TdZ)0Y*D*>@|-YBx`p_T#pA-OHS>%ozY2fQEO|_XeR*!Wc-(nPN>S?d@iS7# z*j7UMvgA@1E)KaFUnqU}a-o&AJ6dbd+oc)&(GH$fcbgxBNCjRsQ}0g^+PNaTKF?O& zY7=%N7@qOe-u5c4b`)0RMAex!@nuHnB?+_4s=91rO9&{JcMog0OOR+-AC}?qQujS& zyRuF{ZanSD+XvCjUUNOL{-b`E-}!N~!8!c-vZ0tH=6H>xbK5#8&L5FmUzN^Kt2rpI`G2I+>0UyAFx` zgBxF^);(!mL%EGTjIHRSDt8!d>CR~%YeM3RN2E(6@z8DpO2vzTWfj`LWyb{}Fi12{ z1-Cw$aAq!rw%#v8E?u&uD^*Im$Mu$>qmxdb#yI9}!t^!Jyr_x$^qtg$K~anDJ{%)g zNiE+L!@GFAvX?6h+N!L4*i_9~$5>0$?vjKA%(L17MrG)jZQ*D*sjxmos}^>b9NZ`x zvrj0vGi0BjCG3{&Ar$E$bxdtLL@8;zpuSDS5F0i@Lp5&E>3XTzGaf%$RWW)-*zv$W z>`h4;(52We429TKc{WL&3cJ|I5H~w~y!`N6f{{~`4Gou6B1QbFM0=Z`Vek^J z+Or(2Gd5p^L)pVG9FgW?uW`oNfLxSt7zCzBYuNqXhjbkmWWG&1H?Az9d)cHt$ZFFA!LeR+5iMknXUT zVpn`#sa=vUGJiKp?o}YIbd7mth2db}AM+@{~^wVr8GLfMzZtj&cx+{>yB^|x#j9w-%jnzD#)LxuU750c){64DG z0g>(glB~47=(VmfvLZVnA_x7PQj2{FWqpMtFn#RQ83wTg7`#mQ!0-b@|NgwhGs25y z3_Uw!aUPFm12K`7nV+10b<%3_`{GKxz`We{wYS~!V0rp25e4WngiKWUj#v1}rdcoY z@+%s}w#2YA!9b=l?4-+?rZZ2|UVf#aaW~P-*ItUq&v$`X$}!Lx^s;ov%6`=BB|PI0 z(Mi(7WWP|XCQ;<$xe=LehMKKQCYcPw6MB*>>M|M{82R}_by0ftERbf8vJRcf^3R1s z^b0Zw%3RB^g<}Aikze@rFOL?Gf$LcT%!4?z8;GTPYu&7C1%rR&_OwumoXd@WefFBj zcqDMPlLfxC_usvzUoHH#*cW}V39n5FP_d04MD&)mwUDflh_7gz`G zx=(&Vy@lwKmME{Z$ZoXoH~D5GJ4^NftxP^`txG3F-V0l?Tqp+8vF z1;v5LI2=6g<&@4#c59T&OW4*D<7*njOTqPz0*9Mh+M8EhHh1OdP12e3Yd;deZy#+j z9L+DiD0Z%w&cV`-*1ahFGybx(KJ+o>giuPks|Dz zd9%t#WV(5~m&B2A*vE+v&Z8sBt%7BZK&09(JQkS2eGs%x`%cGW#E=N zt^3kZFS>^FvPevbHfbXVZDiUPApF1qNl5QsywZsW}E70 zTLlbLA~e_#Jgj(%83q9z;gDpZn&UnTcT&?5EDJ>TZLmI5VtVOI7&HgnMJ!MSX>aMy#8Scc-+@iHX9+m0yogiQv1BMvb@aKgb!>m_y z{<(PR+F#_beRk}IcFf_-`%@T}66UK&CX<*`NQ!XC!$$z94BRZmO@1w zzSY0!^Dwk|u+v)5TkagZm0CgTFh zQ48R#evMmxcQr~@6@8Z5uA|$WyOR4|BpZPyM0Pi%2}JvelMcJLOUeZzk^Kylw)Y8I zw=g1Pgx$6Ll@hP_-Dw(cCeDrJxDxf(A3^uBHuL1Vub$9GENq)f7B{!ckacv1ys}Of z@R-z+9MgMItt;UM@Uf&wU|PTgTw^A4uUE-j4p_V#9=ar&+6hMZTb9n94c-9TW_DTj z#JgF5bPu4Jhoj=$e3$AmiPy3qV-O)XKVH|$r4xCvFD#ff$WJ_Xv~>7Z`mDYu4UKoX zaPgmFOmzyfN%E4GBUq4Lqb4*@Eb7Z6U9JyZBddAhftJ8^B%D!G)ZgT!P5BasF88tp zh~7Tav_Gf7%zavGG6a=008K%4v!67Y_>f{cX0k-5$%AxPmRx1%74Uy3_P{B4+)!;% zwkzZqiEyrcv{;jGML5^9F(DN5(ty?1)!K^HD8X3ZQig^&RB3Kpkv)OjE%fEthcT!@ zJ?lxs*&p7BLdhltuzlpDI@l&jab+4>tsb9jZOFKbOJ)n4pK?a5kg~n@1rkcp!mkM~ zPKZM#o@LMMjhrc0cYy>lyA#R=o55 zaiinsoAf;GY;Fl+#d%Npa%40^<2TBA+F=Y{yv}&84rmiP?En=@Z6FvXPKcN(*|V0R9_j99jf`x=r21{0HP zd=_`Ti(QUK?aul;57z4rs~=)_?K9=veb)>cvLub_`tcbB@=wvI0-R-4_+O z3e;AMv&Ww1j~^USD%Y^|FEG!a=skO^snrs1`?T;C^o>vLkmaT8Pm9)3KAO*3#|Bci zi$2BqXvGYjh?vPL_?YdZv)ek3fBy8=uNUIFFiiq^kClcntQ1+y*Ee#nr?F5rj z2;P4B6$I2Bk7w0B&zS64?say!YddfcL$aHbZvsw}Fb1u)44yQacf=YEb4V7FX z?7B-%-=m&>YpC2drNY)#N}*IcupW0PoKbDaeI(~L3F~FDo`TY3x>y;&j zA9PL6InKnZG~FwIgM;~xk7wYI}y!iNu2M^p=xvHbS{B$cVD|P_#nI4 z`mu^K58Wr6)O*-o2PSB*F0&}u_e#v563w05I3CO?NxRg?IBR(x4t1*ce=QHXw(7CU zH()oQ`8p_V_QxteAdy@XS4vHd^!jx4O27HpFQGTydq^7dww<~Vlv7jWBwO0zygZSt zOD?ul==5}7KrQ6;Wxckii1H)pjXafJth2|o9-(O&YTncw@Or2L_SV@}-VWeCc5hz5 zf6T7PdCKQ3^Y_LO=K33Yn+)Oac|D$ODbpU9`r(Bf^(iMBixfvKPUs$);=8_s%LKEW z*@XzYrFSZhL0D||z4=Lcc=eA5KFaJzu`$O6?5lZ`HW6m}NGeE1W%nQypVvZb&G6;f>tWi5K`7co^v^JCRPj_mlOSig_Xj zL=UF*@h4lDEFJc%I=uPO=(%?(j+0Ngg7wK@7A!(^=6-D`r1C@BRj<;ig82JI5*=yh z*vIF73{oEcY}>AVi>Qp2A3$9gUW|;7b8%+T3jP z-zOG>QUYIwNEV5%pPw{n3^Dzp!n2_ZiTNdvi8++AB-lQeQ=Uo&r;Sd z^IG}dm1nuaQ2CZpc^s4kOwL*<}S-NR}ayGulC_+F*LJ3{Tjn&AZ!I; z9X@e(Mz&&*z`U9%z4jh59MevZ_^n zc3W|b1eGdVO>>v&w|i)LH8we7jV@8S*!IEG{O!v%*vP{kDoV5pkMG2_Z85+)^Rc#UB@a+@q}9mvx^Gr3~ZOIB{23 zY6JHuPm`e$mn*6I>xV8vMq4w88axO`f*`>Yhwbq=_J)H{HVorn-)O9))*hHl~6I1(z@^_9FdbW`r`c(7!L@J{y- zd+J9<#J#H+r)fi%kxmq^_?o>RnMvNS>~k3H9Qk3E7HB?GHC11*Lgt^{detpHlAot>37G`yU3t2p6BM6qMEF*fZo6s> z2qCp^lGJ0UJz|t)Fog2DpHcJx&sZ3kZWuir?~~jM-55$2)9Vc$awW8&dk5?H<74wo zef8tpwhett;TrFr6v)DIb`aMsK&2K&>Ha3rr%H`&8meTx9fcPige&!|G|Rd(XTq2QOJ}lSk*os^?Mno;O;ycUx{vjV@~4eK}qG z3VLFKZERCyY^Cwes=M$HC%+pF1b% z=m-m!#E{{XV0)DB@#F5&6Ug$3ZCNp|IYf=9Iw4tBo`z_%0J*eK!ve~okK{Ctm_`s- z#M1Ts?Vq^Znc$Hl;2GLN2IEN}6=up&W5$Y)j4h5zR--xXBeht{d1$%AR0~-+BpM%-(Dq|c>m*wxpblj%``%N zID`Pdlnqh9Jg9GhKO<6I>6`r;;YnwBBsp#N#L5B&TSGvT{JSd1eTvV0U~SvLtM^z3 zJ2}8UB{1;F)gHB|!q)q92!9L%XLVQWM6y~B#QW+KQaZ?6oiQ237gO3IG-{^Jo>1%c zP({X~k%)+JYlq^`DJEg#Y2h zY!q!4gt4_?0?E-#k+LVF)@SSY?Y5z1Z<9+zYLJFet<_l|>&{l_V0_gAB%DZ?3wzkxZaO12SkoK4urzS2G($aFVm1`vMhnt!%yb>;V(-BA+ke(1oW*XM}PQO9k3) zzUOQPJ|jP@>U?^qgZ3U!i*&HNbKEY$F{Zco(oe!;I^EBA+!c=(!yWH~e}j+nB*hmi@Ls zf{q38VjVmf1GDF$Ui9sqq=&bjqsOcC&fL{mFm!zzi^!{aw8xoX&Oc>~US4+Y^{&+M z5@ip@8LU6+glQi3j-2T*I`J>W8BW_^0dyY7?^Y* zbvNp?kV*X2xdmG%O}%T671z#$tImPy;BvIpLGig`9jn_~p3v)_#Ph4$FV611FO^-> zRfvNnQk0XL?Dx)il}2fYRjPW0AGhhWr5^GOTzYXl$J-$yy!EOHrmt>|^1h>>&(M#L z-q{5HWZ`L@o^j~I2LTg4JHzhf=aDCSTBOyfO~LdM8xN8~5e_=ijuo;R((#c+dV&+= zDK`ph>gkdV(=f?+jS#)y14`7s&5Wn3@3FRB=Y?{M@r#4-M}baJ0?BUM6wplap(^s@?~$Vt9XrE< zsps&SnQ*;w;-A%VFccIGlD!DjLNota3&rY4$f=0S9}|~X`}ZkuS$QR34O~W1{a?L( zU=|#x?*IEH_&)~0)$HKFCb)(pT;t5Y75o3O56-4+idHuNJrNF6{k1**)%KTC#Q}x? zzr9g7&gUPsf1oaE6a+I4{!gV*ahwgn0*D%%0dN3}xxcBll8cUNG)}|7zz7ht14?$l z!43%6ot=LR*xlW|JUzYr{LTTB-(g`_0DU^(PLGO;0SE$MPX!9Be*fhN95SH}SpZD{ zbQ_T|t{!r}3E&R?Zi2J>mjeL8 zpIPt`aOBwEW+-Fi;J+gY$e3}UCHjvw@ZVL?KWqYN_IDvPcK$zfLO1AA--1%#fv$b{ zM>#Zo3zYHsU;F|X*bUFx`#l|=vk%Jo2Fm^M!~j2GfS)nIzcMgb(#NkF=qJTS0z=_| z3LbF31LNR-XTkIH3vb-G`DgFDrlzL0w)S3KU430$LsL^Tu<;G_I@{XXfnH}v$78_6 z{>Q;S2$VO0=H|Z?>wjmv|LD}0S65d7XaZ7PFBudMtJ zefr+s*ZuwdA3uKlIyeAIn*Z-#0NL^L580td;*jKb8muesp`u02icIQD`jUB+{r?Bq z!OULb^bZmGlfPt#%ITFCL-%k0gY3vIA1g8WC)uH&hQIMwgnl2Aa|a+h?z#0OjXnvj zp1=Q_?D$bHyYPqXNPZ^wo9uYIxG*LE>M@ayQBZ3_Vfi=Nap)fokR9owzXD`i8`g*O z^sUE6e%C^wYOw&>q2e)Faq6y%W#}|6VBF~ktH=}f^!Q2#pceYWM*@S#!C1c>-``qc z4F2je_E5uI_!b_yD`M6`Rey3_$MFmVZ!fP=(lZP6a05A6xak`9lmw)amUXe z`@qH-k(?MT0pjO~|PO;PEWT!|6lSVDkB>NZx>0B~w8-Js2W6{v+)tz{~_ z9DIcUbJ*b03~c?@bL`LZmStF6-dxXhBVQ5CMsaSDuK%fp2H5iau7ygyiC4ozg_$UM zldo?^?rloMIE_hd(Billa5q@=Okff|2b&wE=?uJ;#o4z0eyItXP&ZU8+JFL6gf=9K z2Lwz&M2gCu42h{2ynTE3u&z0Lv$}ESiF*ap&o^LUQaHv?+`--rFC3G;b?(T4+;iXR zkz?3#E=cTJ3&W+wbc zoJdG6t|F_6mUX3qkB>n=T5H>(+**dDnsF(Rl=0u*zQOX8<laOH9YEQAJ}!I+PY@%xs2nD-G#u} zH)~m%%qnE!nSIiwLu9Zzgl>U|I8nS1#C>5uBKRve84MxtF2Ms#RuD(YBbfd5JY|#p z<)X0YjhTF^e8j@d+zV~dw6xmqZ`m(Ie}AWbF|PA{&G9TIuQy7 zTfN*D4?Yh{+|&9{+w<{ZSA&AW&z)(zi$8bgeD3}HJgWOiyM1!?oW0uXZGT+pZi&_4u2e+Iid?e4Y_`F%19BX-y+gm(7x}@K8CYNN1`=c zSWv9+=h~zrkk)vGY}#=&&24zOm zAo?%B5Rz=qdk^)V`OqN99iYIB4ovbZgMf{(aCzU`M$acpc+2yMvMVM7)&)v2mwQX4 z{mKSFGar+bp<=Xkp+*v`#@juY_g8JHe0nnf@14*77xo#|}F=UCwsveE$!WserUW5ZPKwoZN4EwULypH9@f6c%d{ z0tO?)d$@8QB>D0L*X51MZdC)|W&@)BJzv7djwM8X1&uk`P4CNhT8fkJUQ^xZp@5IJ z`NB4FFAPPb6FUe@)ZPMJ*?kCCNJ1~HaD_EDS=NSMIQQbUCU?}}=?mxcf4q3>I2$pU zQ|r^!vzYJuwS9lHHry5R^2?OZ!wu)eE3=HZcG@0@y-)q*^UB;ceei3?hSU>ik6We~ zs+4nAl`$G&akB(FCAPccM(o2c!CBUm9A1v+Q&Ekuy5WC1IrrIg$nIVD8Z(Em+?l3k z)$qVnW9Lr=v&K8SVMKud_b}?5k;-0u#v{CUFm-k@o}o*hwIkOut%RLpSHH!T|mF&yWUW?8kG)~S!1O7RwBVvPNv=5K`3jVA>4|1c9*hCzVy@4O;=+6@y$KCMs3Hy zTUQqu^S)mE5*1DqI=cMp=DvxFPHe`l$o0~^Z<@h6@x`yiH+64)H*YxHsb{%m{9!fk zhv}EYuU#=^#J@r!1dzXEC%_j$ir{?EZNL>IBqaP_t{@qCfZ+wmFu(=`sDJ>*3m~+B z3kco@2FCwQ)nj=QQ1t*&{NGV62M0$WnFF>SAe;lj9w3|pcovY)Jv@F#^gp&9A3)f1 z_N=duj~}4z0YbZ3M_OHEA!;_)AS&ToTGMn+a9pwao`(aFxfo|BXNH#XQvkYv^uwMS5^MLs;a8)+_`fXpsDWOtpVgZeB$xi>k?`v>y z5Fo6cJb5zwN4Ep~MmRb;IyN>o{x9#&U+E6OT0MIK zBo6>sOH0cuD=R>r|DEXnMEcjS|68gD$mnc3p--Hjq zSl_>Y2Xa0DWc~d46Oi%z0)plLw=dw)fcHN`-%Lm5{uA`=dD#o|Pv{$XG%zX9`Kt@E zD#oQrJ3ncxI(5tRmKpG9ApNY;_C5f8pFTQM3p^Td{`m7~kb8YGp?4%UPvrX&Azy49 zMkl&bW>UC~zbPiPfWfb=SG#ta!$cd}S&fAaD6h#*cURMxub*uWNs;08UTjCSEe+wO zz`1zp-ZVDd3tNJ7;7{SlzmYfPiD2dxQ6_CHr=MrIx0Av5#YrKSdx{-d5A4K zG6RQ-saE5-O!70fX1tW`*2xg#Cd_B-GPh3TjNMss;)B~S8Sw+t{`x{Qsc}nIt5Lm+ zUKrkDV+2R-GD66fBNKT24<#oE;dqEe=Mn;W?6{)&AqYRDy)eB<3`f0Hrn%syn_%tz zda{Yw{&J5|w4s%2BH=)f493nCFYqv{!^@)Jah?6b)D}HQO0(fI6>%682I7jvN!)NH z(29$gqLJKs0`9b23MTdl!S_Ps`#!5A`1^UE5P@Bc2{)1_Fb*RdZg3mSvteb=$KTLz zpYNlIAzwSJHEd?RK$X2ray_$FL5-DaMcyugC!|w3Nc?QZaLR=c-;yz@!O0|{s8AfA zXUJL~#`t~l0BdwYV2_Zt7>FVy5n$j~M>?1!GsJ+J;}%c{84X@RSwmsxi0W0B1!4|ho(L6T6Snu@7UOOXCEK^0d`Bkq1e^lm z>bg;USdT=$swD(ByQX6>3vb>XwqCYmIS3}IdN}Y&Sj~$|_c40!Cha656bIl}41#tK zdAM^U;#wJ!mIk~zuLk*Y@yTHk+*G5 z@e%Msaj)y+18&tU4LCV$yqJ6zEmuE@jtZ{}UJ1^QZev1*fCs$=+;Q>3WZb0G4dxKV zTfw#NZ>^H-!g?tJkAiG|7`c``v0m?Dc9G(zv@*B9f1^`)rBNDAo-?}nE40a@R`~MA zq%PsSU#@8j@W)?;K6B4ZVB?3EF`kG(NHzV_$Jao`Cb51!#S@*Z{Ge_)Uk}NZ$dS)} zT3VewFE;);2i5VB5p=FfaH%C5!MuVAC-W2=A0@!>ac_*Nb+I7@Twv@zu$zx`q6ONt zWT=8B&Lx0d%>;e%!rm9Oo_@j8d*^ke4xT?MB=zh^z)*(72bR36fQ>)aKP8PRxru}~6qf`rb7QU@?M2hoi_5@E7#I$D@EG;DSUnqIuxdg& z_zOE7`-Yya{f0hje|r$MMq;4_&w|=E9gRc6iI-mPtNln08Z3+I8%HrN`=j|uv*JQ6( z1|63+ewal~H51ovaW;c(VK9YpdY2>XhrmQ^g7AyA)<&n|q_}q0C-z3t#k`iM0|;gM zb05VKa}~Cw=hz?blaS>G_abgn7i5_2#LTh!m-Cg#8YW4PM5GNchw!Bu^|PI%c>uhyto&3$>~ZJ~fbB4;>wOkkOxF>9Dj zeq&0s(CMuW7n+(?9*5jwo6UBG;lKQ`dd(DDz9{2{KR4s%ZDCTK(krJ0DEZ_QD{8)Ki5UCiu<2ndV## zj4jYmB59g*IeKDbnvbpARnLGE$OZSoZ0WEb5r|^3U{gW;qNMG@(V|H7j`99(m!idY zy^rUf7FPdU?HcqSOqtqi&+R;F%L}p0Lv;(L7#v~lEVi}EQLXv;q$5q8hKf$_GKoMZ z2wJ!?g(l$CXR#!fB|UY)wN8P<*Gm?`!*PrelEKCuqBNfU<%bb_1($udfJRX#bAoxd z@N*(q=%CnJn=b#nQYzxaY9~ty<&|(Jjub@d1bG$OyBioOWeLy_U*8V)A3qGbms;%= z|1tsgIvu`8JF_V0-t|uX5=CFYY$|w< zFJcP3$i(ENagLpv+C4#Iu`|OzdDIM@s$vTru?=PPV9`}qF%Il4MN<#Htc~xup{+L@ zU#q>k^7{Tqt@A|YdnXR@y=_~dkN0e%Jx!o~Y?j|)QP__kzt5j&p@3Zu{C>U@`ox-j z+DS|=VYyGQ?C31^wL$T1co{;sW>5RgXWkmHSQT4vTZJ@tI&5S`h|-`}KgG-Pz`y;h zT$Pev1E<(6ZnGC2^g(VA+u(CWyO!rcW|GC1Zv?@D(R6{ivI2S18C(!?=xLEHPcqi= zWT^k;Sypy5JQhpuJi&+D_uCm@<(3TkDMBBQ5oZ>mi%Jh^YCI(ph7h-l5DAKS#l@zN zp})Azt4~AJB7BcNBVy_hhQPq)AP-+1!g#_&NH3(Q8Jb>d;O|4%DrPe5V=e@S1f~aB zn^1%#*)P(=4jZ}QpN!}}HBuk!#PV60=7y+#>SwL#HKwro@n?WEwh$J|@!tFFLd(L1 za9@fW{CzR}?1U#tppp4HUO@;}+^fIHSRWLAYHT^RX|{ z=W*t3Z@{Bj7vK|CrxJu1P@;|32gr1!$4G?F9u6z$B91`F!Z#e&R` z;fDbmU9gw^JkkA0aP+ZdJ>el%gpaq~M24MUoPH?Lk!LeDE&JNL%25BM6p^6Q1Mv=2 z5WV9BA1=|ZYQ-^LTxM<8eo-mCYS*@!(~rZR-aTDgVxGeEfGy4zenSZyg3}X++w!vp zOMjMM2s@=-!6~=LI-CRde|_mVug8%bA6NpL<(^ksOK#*QTd$Q2xie7cHTSRnq{~%o zydcE8^4t^$&Zzl(8gBu7E5F2{K;EdJBELX9zTnP$0e^o%EpH(wys*Kc5N1?(Mh{L3 z^u&Iat|MiceYV7c=}2|iN*ORGwCf2)7-d2f3ohi?I1*XG?z+d?HdKhUN{c$A!GCzO z{q&J>u@#tIrt<5_P_}%8GkAsciu!rkrM6sNW$_Oe7s+)MjeTP)o2Lt0(#`ET;>X2N zFvxzdf(^{T29v&eeD~(BwM%bvG5F7PO$lrxvBH@WoUW5_w+Yzb)BlIMHw}lv@B98| zH)bFE-q?2ysfcRqYc&;;sG&%asH|;cUm~OIMPuI?OG0SuyM_=_A<5oYk~ZB_UFYxq zo#(aO*Z)55|AWgD<~Tf=In2DjzMt3U^*)%yXJ8vASw->!<@C3+5b7 zdv`+WmL$wRJnH=YJxd@V+s`0^7vSs09@u0SX&=wIEde%S*56~IK0VO z%MaJajn0Uzs;z>c>(kO zQ+2FD^=FWFTA*Kc$CZ+R(=D3&r@eO zLT<6I!6tVSs5dxPfaiiYTWsN>ROm)QprWzd4Wc6`|Btx$onZ7=y^OR*I5Z9Lo?_Mlw&FPYC{6fw-gL z-E$TR$BPoRLd`ckBp*n9JuYN!wO@=53&X*?4I~&YJX+ZXtskgKA_vXr`YfIh1KSYS z9IBbntp`G7{Ns-zMZA=zfHx@)FhZ>XRs1N+Kk;jDO_mNgL8qY&Pfhkx$6ugt;p3{a z;a?BJh_DJUR53JsJhvt-#JKyIajdB!hH)a^f_Rg0Q$En#n_D~Wx=t;)Cg{4=!?{TC z?kdbYy>$qZ)w<({S(NnlMF5GEm*xU{$G77HM1hNXOJ^*0HAjC3j=&SlwTQ1%j272x6ND-)6g?rpAnS#;{*lvc5&Lh2&ZUpL}uy zXiRc7EI#SmmrIbWzj?h;=AGAKVEul#Gl&a-&)@3nMR>k;Jt83Db~LGlEBc(XKAUe8 zn7CWgj{8*J%geQni46m%497~3ou-RPoi&?KZU(BME9 zUXo}nF`Aj`EmBaPqUm~zAqBUg7sE)dy;qu^e~oKFo}cwk?2tJq1F9Au(x*+eo4D)i zg*{MISI^JqX%>F4{qO?cK^E70C&vJ>`d|_aAIWojDK*fN@8j65FqA;dL~5)qpB-a| zh)-wrSeHvvSGriYxLkL3eOJznZXQ^7etoxPMtAY|ZlkGghHeizXG;{zz`dh z>g$$q7|g%1J*eQ&t<*4>IWefV+(i_BQLX(#Q}w}~n=eXJUhFGL)qVXU_sa{CYO4PJ zA^OQ7j4|Qmt>YtZohy5{K=6q&hUP&Nw``7azI5gwLU) zTuV?sbbcKg(g7zRO+dQs=kePAQkK&aNCEf|kkVAvj-!ZJChy@%gbtG@i1rf38o6A; zAM7ydeTXMEjpzF7m*GG7Vj6~Ti;wCIKx@gP%B%1k20p5R|KalJ74%r1>e!7-0QwKU zoR~3}k}**{yc!4Yk3$}$0&WseT{Pr#7r;I$qJxIYU;wGA;~tl|E{KosqanH|sJKag z+ig!|7a8Tk!k>>B@nMaGGm%|H5on4gM> zpo18mT>VW*TNWUkh*ZLXD?Pa-sh}t#NI>mv*=t}Cg`&+O^Xq{@)}qQfQx3pA5q_k;$J>xL*M|eXOpwO{o@(?$I`bs&je*BF=KFg zMu|EbMh5P|A+<}V@5jtu?4NaF15Y--ci0}^vB}Tw!yorVK4f$APyyL=xabs!!GiiS z#>EEa%U(kt6NIT`#0~(`3d3`z5nRVMh5_K^Y@ja-L39En6JAA~j)t-3V=0LE#+5nr z@}R`{iU&ZAj1*fNk6q^C)EP?ykO01y0StVr#OT8Ie9@2hVuz73rc3RIKXys1ygLo6 zXMh>~FLBTC?@uqkRNH;0aWUlwK9zu4=6iXeWE<7Vg2A8R`y1zW-5n`m07*1NJB$0} zgXOh`X=yfGwhZOckEg#`O1(L?TVf%gVSVS%_pvb_^KL?F$xtT%a=ClXN1Pk7V`D95 zitFIUfoJ0~k4EHU**OlY+RxTqB-VG`wyE=YH+pFWMu$`|5lK`44-LU-Q>0EJ-j%M! zF$L<`BV`V-%1ICb4=!MGpFaK02@5YE040yYY5;CIjL$GCfzRI0@l^KO>>!fw*|I1Qexqb7`X@f* z^45iCj`bcZ$0XnfJ&`Y$xf&VJ_-zlsK?c&ie@5C9UQ2<9Gr2b}0qmH_=eeNA^cj9W zZgm#m0RbT~051YaJKcsfG8PB}AFmw#phfvHTK1#(IsOc0d%EoB?(I=PMraZrSo++U zI)&6*eA0yHfTkAGG+4h;OrqO48!YeSfYMcRX5KBv!$e9J*u<&Md$8|Y<3W>C@n=Fs z#Jq;xI>Kzy_6gM*twroK;Fp+=;LB=~I|}1$-3$MtF6d%H;Lx$|dZp__bJkr4s+_D` zH7!%hkBFkQQWA=jWw@0rWqDl`k~$EN?(GhJppvqaC~YpQ6J~BQBvo^-Z>-!`)(q_G z!(E*qQ;4zZxEr`F;wV^lI+m~_^{|j1F*#NeDWS>wT&5RNX4YkOfyGmH?rEb8U)qU^ z3yCn1gEl91ea0VOQeH2GD!jyiPx(DRW;muXD-(uSzPtS7g3a3dzDG%Q{67LVmtQ}7 zCK7BTw)H8;C&lB;&s#qhzWn4Ol8QvA=Tv~NTVm_Lg6D{mKSe@_`s;NHxdPHM4~Yj+ zdqY0zVGcsn4e=&Rv4*@y4AqTz_FI)337!e@+AnZ6yWCjR;o+*WxbKX5v)FZrhN=?3UuKL8CXrcBDQrSxEkXb*tv&V4Pp~8Nv z%0s)yuI=hqpNikE>{p%t&H#WQaQcyhKOvfAJuX=*G7zr7>{nWy@uceS2-O_a=OSM- zH{J^&9Xqu5CLh&2rxy<-+Nv9;oORU(@5N|4KOR1~2yPWpmxwAxj?YvD3Vub|f|H$e z7p@c{_RJe$pX?!m1x>8lSk~vYL(9O{l!B0v29HYv+$Z>EJR!E*6dyeSyk~YiZWbVy zVrATvk)U+o#-2B(Q$@`QdoDIfX4sWmtLx$15@>DGcI8bd2gV#F!NAf^t3uaBFN~ib zMIvF}1RQTZrGHV@0_t_z+%;<2miK0-lJF6H=yxhd+jm<9=9f7g|XbBq@@j)U1*M4X9hL~w_Z@6Ipp zO~4Um7VMmebF>re1p~RNdmhf^-D=9Y*l~po^CXAwrmkQc!0mk4(au|K9-FqhGk9+E zj3YrubqBNXU>Y0BPoU79r;Pm~xzTp^qZvuo)GcO&gA#WJ$Qt0)opd~zk|}7#dF%5X zIlX#aI*Ee5$(*atpi4@2!W%Ta1ty%Au#kSAn{DL|MVH}=g^Zb z)-!zED5h}6o*Tm8KxvnP>U*!<7_vzsGN$kEYP$qVhU`1wXUb&{)B!6@^TJ7nYTLN2TcyYd(gQj~}j z9R)1GYY3gvJ|_G_Duc_>VF$@4CmWcVBa2rNPgaG1Ulqr5Kn~wpgm@GZ~zWi|Zva(=< zf^(P|BKo4n&F|)9wCg~q#l#do);0Kx2%F214MHE))>kVez-;pYl>5R>DsIOIzAJP9 zj_JM<@so_MSykqV9c|jFrWCEss&MOzkA@u1fb%n*Y?SnxWUhGaerdZ)Dctp)WON`> zty4<4&=v*iPN69-Wg_xZK*UsqDAPF$4GZ@+*hb1W&L{mjR}Cvj^oOD8L8uk%>e34- zL;zXeT9RLc*0cd>vyG~j&vnoQ5bOs6H>QrH;IJ#Pm@4bFIxhgVcTOY3}pi zLZpZpW`aXbCsGoe1<;d0rmLU_G zm}1XhrDJQjf-9MD?d8Jg>?f39=muB2Zloz}ZaXHsIhnNwk@%v{onSG^7O0oYV&kIilX~b0$Fj zjpWsm??@kzK?mkj(Vptz5ti9fm;%V%9aq9B^~A7U@zFplX4FaCisX(6BgqNDJM7H! z#sL6hNjVoPte4_;7;mf|4~#zOeh8XpYOK0Ou$P$#y2qW;LX3`!IA(j73EKjwtlTkj zfnMZ|-$D?QNiuzRZ&^Vwd{6aE)XAR4T2o|Sax0#j;5cbaq@{Sz@3E}}MDOqLZe>Kzo6NS7>_YZ>npZaOF z?0)gby8lKV<93<<8+QB;!}#BsV_uTO|Hh6v%<+F>sr?H$ma;tmhdKVct5#p113(y= znORs^9Om%H9PZ)o(8FJHS`O~;FHTwx=Y-dIrkTu%;A#%VX6Hal-&0(L^Aj7{(mvnI^MqjH#GS_wbTNm=Ybp+nf4Jt zV*~H~LsXlvb(R|M5}bHGBoCS@;Sp23d68~@& ze~60zL`(b2N&81g%fTo(2n7e8;E)p>8*OIQGY&uThn)DsOmJuk4l40mMcY=|(7^#F zDwy5Xt$lxNv>bARBc$bU6MvYA-?YTP3TX!h27V*X!yHIrY;5cg9`Q#`J2v@#V*0~x zKkeIpC~DvTxBax6fBdw6m(y}M12ngoDTA&T$glK26hY=6BHf;|7d3Yk+P(Oy=IW8$ zzNb$dUeyI7cX-6_(SKKYTUn<6mzkE8WwGNSonxjQt#tc?AWybz-}xJY{O0S%XMtD$ zK#x~SIQ8Nr6V0b$lQG(2MCrtyKmBD2U>>d z@-RR5qAbT#mKAz^J8DERBuCWQ1do3jtxs0&QMuYn4AmNPD=WdpGc&-FQx?j2l#+7f zu;e1WM5N75!3bY9jpPxCbH2b56n_!`9CRX^g^#ajvzVf(9c zxEafv+v=Y3Omp=U6iej+W9j{CdT;J(^r}|^n!L+=MfJPYN^2U-D@pZNdKqmt9c@Ib z$uz1!TQ*O@bt%F4PZ}lesKG9oQ@$-St_q&7;jtT%AfMfa_g4CuTxTfHON}o%DErE@ z!SBgCH=@)G$lrDewt1=>sx=UmRx@6z+PA9Ke#ZgT>I`XAg%;g7pT+WOP%96={NyDb zUg;ysX)7&nKfevUgZZ>p>MAjP%P3T(X2CB`x=sCxI^;M?8WE-ior|AD+8f3kajg`Z zx0J8Ln5$iY4HzwULUjx*2|CEm7Lt-4SMnGYpNxa|4-&Zz49)zsdL?g0G>J-v1mBu?H$GeuTp#F% zk6wM692TV2dJiAM*ESj?*{om;^(={eIGi0U%E3HMk!P=eqEvaDNATON=R4nHYQh|Uzs*zUCQR$*{z zA2S~e&+DsC2t+H?^xLA>-#y{+4B0Ai63k&5@$j~{>Bv#In*lDuG1ex569YJ*xl}hf;(bM3-x+f_a#Ab;j$OcSXfiSPh#|7yDPTX5Px=wwoz3y`}y4$&e6d+m2YG0$fT zUN?D)I(r_MTgP8|r3CYnp3u9f#a|e4%2hjM!Qv)9L$JUvceN(3X)x}Zu{M9_VY7&# zZo57APXy`@2Y91Y+4HWnT_G;lt@pJFgdBNz)2OvL099;Ph)7P*5N{A~7+q?~d!Dr_oU_@l&=mt*DAP%H%K3AVl7h!x?Nf3-xn2gWf63bq;5MDUUGWa0U;AnqzA*cqLni zfvuxZVR8fy)G5$SpDoqxByddXQ-~u21jplC4^%!bA;iSzfgoLxNxd_2I=}uZD&q4j zoqDcG3O!J6kNue?3<yMN*p8JSO7d@lIYHgs^SJR4DW`yxZPG_jPoFt|LZ>iG#8^ zdrZ9_4t)FaoQly*RFiSU!07}S>ndHaZ_Zff>%c7u&0vDNYnTufOv`1cz5zy9UjO=n z?o*X57&09mJqnHT1BY-2VgonMiXLV#RN+BbeN(>|X_&{QzI|@;vEv7Gd^#hVC~)h8 z$-~u5s{BGA3iT>;Bz5*$S>B=Li&wW^{^0g8kn0UH#6eI(6rdE*kz3o90F5EQW$md; zT(R$jh5W**)S1zKkm;u9)t`dBn5diZb8kj4HO!KjOIxlFBzfMDq;TcHomNgLA$Xt^ z1_%monQCg?vj;|t0i9%l1%$R*_isA|0(UU7gmL|QL+-1`9;O3?0Xu4X&u`Dq#xrEE zCiXT7N;zID8vh^!50)g90u2eA|4ll#3QwuxF0}@5y{CSbpKXeaHxLEy*D}#yi(gH> z{$r7v4lljNzhm!#&NISsr-O6cHt){Z%U#}EvG?+=^~UN3d7Xo%&AeXWsE8&WCni%A zab+W0@cMjLe~3cI7Mb}!cXri5mO#-68%3ljJ%r9bLD zV%J|g@_a@E^FNt+5SM)K`39TXf^SoVWhZGsRCE*0F_|r>ma*z{79cbz(W-mV@$M1R z?S%xc_TYp#|Nc`yze=C(9IV*>UN!91zap+O6aB$5AcCT#+iq~vcWK4oZrxqGi$l2Y z@5_9sZ(iq`^Zoq#?$P$vdH3zgaQx#FIG$NhJO##64T__}l?aY11n!%@?prkXph-;J zXUa;){k>WtQ=e@=auGrdZ(GjY0{Y=`vp^^l$HM~hBm(`2xI`RR z!%I*k&LI3V#f`)r#_%YX!+rBa9{@N$Vqi~&U#@a-OJ!ri*qCRY7`f!2fSPc=SzVKO zL(o*vRo~+hkdSil`R$U=!KQvbqQSvmO(MdQ&uMj1^n?^QPed+vILBz+GD*C3^s%K3 zn5;7wStL)zZvuHJIH)I(R{)kof??&j7G0dGNLcHR@S5a1JF0Ox*USs}A}U*bu4n{& zI1!Z-9;X*YwbPk ztpD*(N5TNxHY!?iSwfL}x?-_o(U7hPp#oZKO*H9?xEA*@V5VK@&Zh6RL`j=mvL8f*p$4r~+pPHYNTIipmp^$v<0#8XnRPGhA;Ez%yjJZv#5%y^%3TgENr&NuqZ5#V;n(6zGxx z$9?@X1TJRoR!VdD5*_dF!>g#pXLf66RCabvJl;HJ&%zFH?fswHY2$NAN4v9(x-&+F z<3&@GkzLVe5HT?A#C_urwq3K2MP&)vciL zY`YX@A27QgS&*lznAWV2r7L^uN^j} zJnLk4z9x6l>_A#vcgkc+VPM9i*0I8>?tn4HBKN?ee&Mvo<3)S$d9~d|@3M;;?TXt2 ziaS$_yX%U3$BX;F7PCZ32KSW=*_Dh0l#Hg9XvRGlA1{&CFJLT`Oz#t)8OwVYQ2IQM zqewu1VPk(%u(gwU&33p30G3QcYy$4%%b|;G>@WXgLFlXMpdNYDgj0Ceiz|Pj=w0w*fF-b_}#5Vv%41onn~dwt1wLaYD*G z(CZ9tQT)yxJl`uG?M0k=QMtN4WS)fPqh(=ERcwyqN(fjb3*0ojOfs%=KaywH9R^Q% zrHJEWg}}#1{Nn?6B+Z`Mlo|kASfy*8h%f?XH2jg5IQ**^r)mtRudlVXi4-3rOf2Pf-RsIM>C}1{|Y< zg8d1=IuLNV0PKxZs3aZSDH3J{fiVL>I#{LO0ca8he*6K$kbw4ZdlE*gD=NZVcv=f` zgO?(oR#-e`6VV<$XsRI&V^RBa5=~|!Hc38t1njwOuF84llObIFA!KEfMY+?fQZ^eM zc&D!EDRis{JBq7!P?mz~oUNl^3n%N?94iI6DvXI_Q=nuP(vpNL0HDbfX!!LfWNNu3 z1JeS46fq6Wo>nD)E8{|9DY=jZK!YKxwl1i?j1HM(Vh3@M7Gi_4Xw4}KcAf%-ETO|_ z&^1pK${qVd76)-hlL=5zJ!*}N;A0>*iKuorhSgIPn+!LnqNj8l9{HIaUNc#aK$?I%pUJ@>4RQ2c(W* zGoTC=+~EmCt!_^kx#tNAdyE1%pkX9?>I1)bZ|*}a&>*$;mMQh8vM5-$r7HDrUBSAz zA!5g+2}B9K3p4;->ghmT;aG>E{kk<5zF|+?uOtzxH)(AkN$4;EyR6iISFApihWQR? zxMkP{b?mYdZN*!5pH09V+11^I>LU};tKaMUEpbOrF&;mKE~i$1@kH2)_6lDrWAwJ$ z**BKZw=rZQ;@&sJBHM-MP{;Y6J`c-E`J3e+WKULcKU*EjCZqc6dmZhYAwWn21)EF6 z84xh92?*Zh=Y9|RZD^R+WEM^Z2Y0Aksz0UQ$GSDcdS^N0DBjvi=|SqDiga<-0Bo!M zNQX*m17qX~8Rx3n#kB7(VT@e5)FZpx?R&G6Xr9{0*UBsHcBLOfS0c1!@}cg@g{$fX=wVrRx!SZty`GM%4mU2Y}q^GXyn3*q$gp zW)Jx$6I28pqCiPZG|ys-%XJpdLso5cQwIT?@u0E3 zo{n5j#kP2k*Rx8`-*_d2gr-r;A@jgO_UVP(TMPN= z3x$mf#cvi$w-*=^ixmeJt4=Rg-&(9mU#x3ftbeoExV^}fSZXfqhY$fk&!x`vrS8Th zAQ1o|F0s^j+WVJ=4ljdo%cCp!;l}0O^yPxXz*0KK<6G?$%B*VB^4#4eA>i{1NNkqH zJN&@=`!|M>aqX8+edKm{j76ds84zA|&O6s6l-;y~z`D3SmV4C@mtMVwLshw0 z+k%En^d{RVj0Ra!LC-ew%%?9stjCzqmqZ7)EBEZjuXzmPj+WVwaSF;N@?{?}f#?BG z+zS8(|ALb=<0s9~>$)A>Kg+NWSDPrYDu8Y^Kk1kteW20D(oGVg)*ZEVlL>i_9cFE=F_B}+7#mV8 z{s3+VcFq3Cnuy~X8-UJs!!lwT$1PslE>~HnV@EtcYxdSer`2B6!QEagJ@V*l(c7={ z7HGzE7SsaRPG~G5)!oox)NKQ>){}3X#9#P4s|r#Q*DwrHHCq*#rBnSZ5|3ES*3?ycl9QF3# z1vuEg{PV{%L^m@rm_qW=ckMB!W*$qyq3OCu&6y<@nKEg+WkV;?FBCjdGgQ{@;=3}{ z-X=zy5BUe{&m=b-4a?=6XU!#!#K|1WwCWm^?Tt1;e+uGqPN+X-=yy_G=4zH^wsDU~ zl^sqwp0O)oO(l6VSqmYy{HE=}iI1lnZ~oYZirKO$&7tTW#t&`F9o-wMn2Yxa!*xo0 z_469p$~!u%vu0|yrL>atm9H1T?T{v|H~~LG@Ox=z*E{o;u0LJi;VbeISy>A+G=He58Z>fIxgn=eWUjDS;4DLrYX^rj;Fiy3G{Z$(^wUpO zn98Tgh8+!8tq3EAfV}Q@-Q_k2Gc^%0h%gH`jPuhj)1GITkqgvXPo1v~x~3EHBqU02 z4&kYCC7*61a4+9XUe=B%J)<9#Cm8f*;k{XWDUwmVg0WKW29@l4Ubi!)aIHJ1;@Xv- z8k*gmAp51R?eocXYeiA1^AoLQJ<$P?<)n{rr`2R&TNUxBk)U(cQIj2^d}U`nb;5S| z+XjAkbj?p=fNVfkR8_TFYeI?bcmAdT>>-Qvn+TaL>8D#?0UzrOJ|?+Fkd8B)tBK98 z8f2<^1m+=jJ%6{>rpn7|lc*U*#u}}48g5$t9OO$JRa&{X(;Q)2&Xok(Ep>-}bg5SM zjnc;h;^gn>4|bjB zQ=FU%)rq^yotb>Y^Nx4IdVNN2yVHf?KAS0K?KGze=4!rW5?ASna|$nMMK>-$`(ugX zWH~Gorx^}FU2Rr{zWGYOHV zDOg0Zcc4ks`e|i3Zi6b@^SP-3upq(fwt+97D&FntFfpi~dVZ#fOTON-1$t!i@!4E0 zY=m;}VM@Xcfx;I{imhL`(kTzui}Tn0rowl)w#LZ;FY!{G#6@^G*N1hPaTR*RLmwS&qEfr1qC*w+6_*qmg zLkfr^n~*pj5^VMbz{Ni@o15Y+;|6J&ggRH_3Er$=uJUyMQWIiH0y#u@#(`TIz8fv! z7NXdj3cdbBho_VkrcoKTSAnG81~CwSb(De&)iRXgQ43S4>c4bLKp$Vo@U_sL=F0Jl z!&NgP4~0ktkmp*HB;!JDqf>bI2I;>ZV1(8wzu+73LP|*GBH4gL`1e{S`$p9DO@{11 zc5SVD?7Z%r)tv_9YPA8aj^1$$8T)i@OJB%m;EogDSr{-yUs$5zj`C3Fj^!G&!#;!e zBlkH$#Z%gn;_#7|WT*tLu_lf+Azsb$oP=PP5%0di=yUPea{SueClb|TcYQy{G1F>W zI&DLvo=s48I)CZ5d_(iSWGE+6B4}{bP9s&*eHK=xZE$LYk=pfrR{hdR%U@>N4~BV; z^vE*r+!txT%(U-QlDsN-W7k`MnQ4z49(j>&Idh)$%Shsr;!}bhiU_)IE zWjSVAo1sj>Hdo`_bw}^|3}x-$m}!mn9lMt}lr3*`!Q8s;SYq{1&dyMdnRegtRF0W; zZ`*~#-gU>*H;3{LLNAg-_gQ5M4Ck8|DTPoOR=InI3yy?dw946MU1-BG)3#l-d0b~* z>N8w)2I^+}Ak``h#4J8<#rv=l}5^AL*38G>Wp=2_~gg8xx4Isda_@Cq(Xn$-PK6PZZL7A z_z}ABg7s6ok?QKQd}yJYv(BmU5rOK1C6_$BpPuS}Gm=*i_3#XJvwgdHt)|1s(W1k{sF(F0(p3B}N1fiR{>=DO znhFs6Z;r5`04hKw91sjJ18Xv|C&=j>v{2a>(*bDshuyz{5y z`}yDF>HqEI;tZ+Z%`eT&FTY<{{;>4%FGUxpP4(}VuK8v5;>Xpc-_5FjU+Mo-vAX{0 z^Tx&oXRH5r%j(t_&QkyP@0<}f_8*W3uhM^I(bDF@b=_AuUdXX%IqQ>%j&*-5S}JE3 z;v?57s@Kc|5aQ$JBdO_%4#ehT;Cp<|)I)U=(CtV9ZwQ9dG^`xqjBr8#Ro#J%JzL-m)Z^OjW zv)|qVt8*aD6ho;nAlDEFo`J1Wxjndua%8ouyPGhouOI49T^F2{BJcdbYv9B%;c2TKK(#)_46>)ljw(xKhPET`5CKy47xg)y_ zCND0PcghtlRrK!BQG~LLl{pqI8@J`hqb^0u)e}C8oO0DL<&~P5M7NdNx!j_ax~1yH zm8b0XZz~*DNQ26?He2+u@%!fD$7e4*0L;UXNhG@oE?>-U{(^WY2oxo%Ebsb44Jz5O z1(=Ri=J8!x?f7`h)B_bt!>hIvfAFGtMi%t?54Rgyl((@YGGbJQz5=lwgwIRs&)-Dp zuJBRgy)+I~k_l0|%yT-(1GmmCUKxC`(7TIedp_+kYp{+KariiSvYC)-bq@wpB4pl| zQKwk$-lHyRa94tb$m*A19T4BWT~)dTh7Ylv!0TN)CJ&m4-vLgTKD)guThHtbihz>A zt~jXd2{J)nGLY(7E6n1#q*>*W$2Qk3Arrgzi-9FE>H`b=OO)A5hnj}81=D?XA8mQ| zPQ-HUyip%_oK`*1ahwDtq?{*45QNEuuLJ6OEmyE(t-1)^^j1twRn@Dz*N5vqGY%11 zS3p?hmM0$%)U_R1X@}a_J3*n&OOS>QqaA4*a_=IH5emdRwfJD+!F%Fc7X?dJ&3Z1f zuB05^v)PESW1nj4AxS7?aGNDJf%s}bghG<4BE{(%pQmV*2eAG?y|ln9AtTT;!4mdv znwZsf`pPXPdc%PoK1v$0S#=_Ct6GulR4AbR6m)0P>0q zgWn&NZ8hskSl{f$7yZMLgCOZ0Wnwz+| z|AXV^!Gk}Jn@5ix{Ux~hMPdC`+x)-x+WfQB=HHAq9G}e}M(gi7n-_oZtluJ=KNg$6 zD{TIc{u++FX8G6GZ*R@VkDLPW)z!7NwRKLk{npm}vQqv}bk_X;ZPeie{pEij^lG{T zE>S=*WT#X8R0t+bMReC`8Ph5A=tzFS>Ij28G*_Io^u>5BT#N8d3}x`&beYgix&bSP z6eX~C8n8=p77ad35_zad0}801U}|Y(+=Ui_3#TzdLTi#IgYflB6j!F);WWK>-Dwtk z)rKXUQw5>}XbUkC=QVJxJ>K%R<)r6`hvZzYQ+eLfoEG&lia_jPwbG5bfjJ|2=)w3} ze7M<&D6SbbR+CaW&~r^ANi|=4$rNEX+gl(tRREN`bSzp%XC0KJt$L<;79~;~v+HUN zd2&v@hlFtst4ev{9Ps`lhgI7-lx(!gtVha6``-hEitGTL$`?MG$R54(zC%>&#?dBz z72G)z%vBI5FJ|0&iO)y?r`2YQm3+*GiE3Pwb|W8XAeixY$m^-6>YnM2Jqn-f>6iyO0aMZ?e)Uf*Ucnunbk0pyXfAN9RVlQ(m@Wb3c;dU zqjP~G4FVDqQ}MiYH6)E}M^HuLlhaNxNfD+H?;Z`g5JR5_2WY6vC`X_8cXevI&X^lO zI>+`v$*QSI!mP}XuF%889H_Jl?3VLK1i^Mp^$t-}G0IuUIi_#*dWHT$8q7Wz$4J_tdS%ct&T5eK76VH@xhTSf+^1nq$vH}V<=ybf48DShkOY~NyV{r<7hnqSjocM+R)rODd+9B*7pZ|H z;xw=|`=oNL@RMVRkd(cia`K{MK=W%;Qo77mxiQU>Z(i%JU7F5%tz%|FRQ;sFZ5;IV z=mmM-#!A=K@3X2cDcxRC8IUA;(3!1*WV7u`XBe|ZIvsRa_L|e~qKy_=ADm{g>w1Ps zPoSI!JM<7j1{78Ul(MM>>L=^-XS0IUllB&*1P!@j8VcUK7ryBBuF4~!3(4W;Lzx^_ z&Gb;=v83*y+_n0c%HY0%=!XrUpQY|I7WUdJ0cLCs^No z402TUqe^^3lO*P7c50^kgMf9nQ+G%R>(Rh6Mewqoh8y{|T}@kk^T@y;<|eF| zfBd}qy;uF20G_H!UZ)NLn*-T|Vjr!N6EUJAstQS^1u>WOY@c>dnajyV(cJZ9dR`r} zvUyUnq}t*sdbr2#gj?JG(o-o&n-7_Lgb zUaL$TdlAATKn%C=^CT$PgaB~^;n1Z?#LSP<7+0nGjXftifs~IYB72AR_yUVgky`If z^~{GQzVC{5j|1j2BLy^pywtP7iq@>l6&Q)KXv`M=Qj^~ff1f9!%*+UA0EzObX=}$E0X8Z z$(J^ze&X+8BEn3ustGCG*4h91mMA0xv3nLQ;^h_?V*hfi zp}6duD$Jslj(4Y`nv{zyquogyoRBHUU$SZ(JUj>_t_jqc&zqmIo3|9ykV;JVx_5m~ z{l~B?(mSUu@e~kF6rO76_G%%}H9o6dELc91K5H@A)b~ZMe-zCePdzoqYe6FQZ8=OG z^C4h$NF*_(xIh61gx#*<vdA9x3=kt37F?Tq!H>6_q z27qvQz>zQk0Qzb{r_!#xDURIy>YZ@rlee7ByYfE%Z}yLsx7G`Ptb9E8&GxwR*30MC zH4D~icFsRP^#qJO__N&t5d5br!~^>^IFJ9MEf^Qh!t(EJnd_j3fLzWmnHcNZog_Qh zA9r!KcRhc`-R-Q$C1=me=e@7FUk|v-In`6CenEi&A;C97LvKZdQzIh+@5Y43(W2t- z$0WqxO-iJtq})$?nDi(;g-%b+%y^iS{iv+0u%e=*sC*;h;pfYjEsp2$`}c4EQzVa^m;KKd z=VUAR*U456#bfwii{P=s|0;qP{U;IpwamYX;Nkpp5qhKyFu^3umF&k?_*(=oF1oQ0 zCv9jaMsnR;8pDEej)}Vx!nlGlKJE^qwBkP^_$9WIJ@SB|mZ2j!&;YV$QlR&#pyP?= zC+JPdNF#vvTi3n1u8rT-See-Q@nwo;%Wbm|Gz=*@ye zM}S&A84#Y3z%YQ~5JP8a_CE7D{+GBjn&UwZgx0&C-ha+GwRMsaj5mqc1V)@fdj{do z$Gm)*^h*REAhtm|V{vNx!dyl~7=II*vrOljNi$ZOKSINoHv{gjXwT<9^f8~vN)J<7 z$UhQjzEF^#iC`8MSN|5lw<|4jMDUy|teV-v#nSrCg~c)^OnHgXD&Y3l71rWX<$wPQ zOZj8{#)#X;hONJ@umD^tY$im|{ok&zPSSB;X~f=l`O`~E zxa#KUxui>*W3G7|5xo18<;_=~9jc!vuD!hU`L*ANlF!?3ZhT(;{Fch4wlx{T5y4MI z$d_(SM{};Q-qB3dzRbj*@c8mR`F!b@*@wO>Up~;o)xOSUCwY9G&(GtC;ESKEd|hO8 zsD0yz;8(5|*0h|vURnFOe{iMut6IGobEnStRVn^5m9^gM**7ZsOv(nX z{$rEJ`LfFANu8OSPortqp)<_;_0UvKa30&Y_aWQ`R)!GMqf{AIkC)X+una|80#h2YJ*m{Haa(qw@xqw zsH|EYx;D*g!r!KNtUS_&&ziq-u0Zul-0{=jr?ZAAFW)3Ha zI019~nCYL4;RFmPTKs%3b8>}~D4aNniVTj94vmeC;3NtsMK}?XpPyA)Qou=$nkSW< z*l25O=43`kM{8$iTUS?mPfsT&HvZ(q?CcCD9{!#RfAZiTpRWH8zQa=hK#rb>LFeY> z)L2-W{(>z~;sPPbWT~c5pb7;?our2W1i6mkn&=tacs_k4oMaO{5-uaA0P$=B1Czk} zJjt#A!_X+We25l+5D15p@FcL5i#0GZwSa(G=_27vVZ(J^@{kw9w+2Wfft z+Y_CFdf-w5y@te!a~<~;KfqYDrWqLRs~H$d{wQmZ}Z)z0O-+FE+5 zB$B#;vAVwbUL!MgV+(bYL+Tb*>XtVDi@o;_YVv*e{GaqffP~&jLT?fRC@5;^p&Aen z5fqSKBoyf-2?0XW(7T2z_JB08g(8AASO7a9DwdCeVnfAW{Q5ng-96{*?#%A&&N;tX zCPVuplX>pzzLV#5y|4G%#BHa^Hh&XZfQiQ*vgcm1cR1N6(!@8KymKGfk8a|hXyTth z4%lz9JH=#As!4DbIXIi_=56O4;OZN=ZEw`B=!CsV=`o^(3&|yi=aVA}$&p3m=z}KF z2gtEy9XX+qyq`x-ZZ%11C8xBLQ#;6Mo#gc67Sa@9j}^&@i46LRfSa@`!c zcGk518M)yFx$y=0&@1wx*W|;0k$G>)ym#d0CDFsQpbOvS0Y&F-M8b+Sxkcl$NAvPU)s_u;tm5op*g_a^Km&-N&R1GvwyRRFiMEVS?bdY5|#TuCK_Sob#|Ge(KR- zA{1@pcft3f0iSvhoAqX%F;5A6&D`7}r|7HMQ*$TzhD1^zTTK2qVSRVqogfp0WjuaU z>U=98Zm3~X%WDayl7_QfH} zlA>KLMTyePq?lSXQxrDNcsMZ3s+a`|K@_}wq8V*DD7LxOzBynA-JU6Jjl{ArZY1uA zoha5sBbt?04p2H4>P&VmVFlf}*;eb0MKaXu=5h3J0mYrzXBTbpJk}ARXRpDAZHdX>4(Q-O}O)3CN`!lg)5L!glWfs_K$Nf-(%It_-i#NAQZfm1H7UegC zeVxw8TR)HVq$T@dnnAL|P)EASy=>*n)&+<$0%E7QCl$*AHn`%LZu(x!&2A!wWRHw6 z49i#DLm3;9A+q@toU`u3+c5Vfim`*q(aTe=aYp7K+~~geQRq5Q9}J5y^fz{d4oo^U z({*8WS9EJ+q@DFISADql$z8^Sj)nbW9v<)%m9XKc=o=Wc`@hp;5Si}(@!A))7XIX1 z{pltAtEelHgZ|(BgFl1iadH2&%>Of9{*TVVKb`Xbw#omu*7%|h_a9UIf7s!3|KW)L zyH)UKTKj)sg#X`h!SDRX*KhxO(d72u&i8-Y-uLwM_WZY=_y4Zp{jg|od*mOk_y4fH zANy^5|39_8pZq^+doN10`mcNB|I$DBPi%Yt?;fZBH~%R*x^W5$qIvkCT;#tI6F_lD zQO0WPm4kTOB9;3zjem*>%9|&uNqa?+Tpwqz)mtRKe&n>Q{X3HD#fn}}=VU9fkoeno z-?rVo)j1Zu|Jc3bTaO1nzG|-RvH9?De!C2-;Tr*VmOFq7I~Sc zvrd~cGP!U~B1q$|ZP>?qH}@mT=bq(PbQmEpBy8(M1s)5Fk3eTcuy#Jb-gG6ld0blC zU`Xr`a$(|5vou_=mVYCbQRurl@f`kx%)}T4n{5?K9^v5wwQnf4>D_!e+RU$S3~Sgv z0PAp)M%j5xh{0o+RFq`T&=oMOuH)fOxt1^nq9m}4BFw1pvtmX0`SpRG#1fw=A~k`wC-Fj)Wr_FI~L;X&D)m&EdXgayV9sqkapg92sCuO zx;osPFb;wAG8ZVjS&<8?)DEk zf4UdoRQhR(8hPW>v~`;J=b4R#ZlCX))|GyKV0iS#=Z89H#lJklPq=+~tZ=_nbSEbL z>c*F++jfe7omKnh_H_=Xc)()5$k^S&OZI%%86B<+Q;KU!CEMEJvGWtP#83q(ZujQO zZ!ctVVw11(b~jYcYFm)6G)a3k=h^sl?#FS(?DMrc(rZzk?;wQ|Qm;;Ju*-BjX55o$ zeKCBKln3UkheNWsxg_pAdVdt{H~|&*!*RT#CM!xl@-Vh*OnXoNy_2O3A;`mjzEA^G z^aUloy8M!2tvUeFv9jh=;-FNy7LbNjGYZdsEzPSFWANx4bJrG2Mg9mt5IL*NEJ6I+$0Y=!wY-@@rr#BjFeQE1u<)Lp$gnQ zr=(&`?b1*Q$pj9w8!Ct6o@%afnHYt6Pn8c?j-4G=93Q+#c5xWmH;a^05x^@JCnZ&( z1eB4WieK6O6>smdYu>Y_VZa8Q8CXbQCrEozDJX1nJ_5r+LtcSZ`B#Wyy{FfmFA=gW zTE<@R^$pW4C~AWwEJlW9mb4;myIz*6V#mW;t_8a_TpI6SZrPJX>8s@ zd2r-W8SosSR~Fq?&bqt7Evo(LPPXpWKsy9O$GR(t=+Ckgi8;NwXxu;p2Bv zphqLeg9DxOlvEiK0TGZ*I=g1#z+bWu_pP|T^^9w;K2S+)79(gf>_|Fz zxPyYJl$+o_6gYFzSP4HY?L%T2WX@r9oxpn3;*hkCt;99|v2Cvu863kH zP@jMCEK9|50&_rqZCvCVzj)G7I^0)4XFobK`(r5x*DxdYROO|q!H`X2Sa;L=qF^#K zSaND-OT!1j4dz!xp(xoae96$H$Yd%WeTPC!Zjc=c8Z<`fLe8^q4{ZA-lDYtrM>og5 z_Ib#@cj`bd$k`}*U~+IO=A8Md$&NPx6(^@J!j<{CH+qAlm7~YDtiM-&GN#ky+ioQ3xTNE#zjr|p4)B4uFu#=GQ<4B*9Qu@kvqAm!or zCr{v%^Lv`Okhgd&?wK8tC5)(@{XUE6r+QpY;K&Ua6HlDmX}|G{rT@@pf}Ha;d)_c$ zL7hkaN(8WL&R+Ct{Q1Y^@lhQCBkn**XA&k&&AJ8gv7pvI6|(cmSB376EPRJ1Ym-1> z1rO5l8kWu$j3A%{NPXsajPlp@3cfdEJ9EAxz54sF1?@`A6{qy-4qgd8oBSYpe1?t^&_vWWE%)+W0c=?Ibl!CNVCU$Ta1 zoPcssA67cgJ$(ISEd83vhYz-$1)JY}OUQhoVNW`6#cVIML^02|Ym4}OaY@}M2C`+V z%to6$!z9Abw*{AOQ;LiY@utO2)U}|Sjd5$1VTzd#1$EQeT4S*qUxl_!fP0x#4Hm;pHkkb{i8L01% zRVJZ=JdonEvAa@~5^)-)o*Em9h!37ost(b+pw|6_i3Vv+nESeKX zJa?dLI?8^?2#-fZZ~f*o)?{8 zjO`kT4YFVqlTp?Eu_wAzsy-B7+2MN|qF+VUw|MR*%kib(+fy^CmqqpA=c!Bd>pFUK zlsrL+d{j0=CA`LBq9)Iek1C}@3T9C`43+vtPZIz|#w(c6P;&{0Fq*HGuf|D^POmw* zlw|f9QG8CfIKa0!w06HT9f45Thf4LBW*J_c-!G&pO@$huhCI`14KtCf1db_@?}rW| z6ca%EmJkxcQbmG=E)TiA7JGmSHFQXD6C%}UYhVTirAGmAC`J2NFmxcShMV_>Y?eqm z7&F5?JrlcE0f3}J`)2YS>ndF)VpDjP$hv&=0QYN)@edZ(46e6+EQp+sb;F~qgv>h| zj8I{_L*bfp=DU$|WsBBuT{_^)HF?&}K`cY77_i(bK!r>Cz8`hX1C$YUPA=0Wl;B2Op+V zVL~2{TE|7`U(rpagCLw`=-7;u&X=aQeR1&S^}f2&pWufUCt_E%4{7P)z9>k)u0Heu z75CN>yO@ldu8#eXmhjAH-zUrHkDm|6B_0OXn}hs#=aYHJ)oAn}Z+MC);TMBdY93$T zEHfA*m)?A{x>-pnR%y8zuhc@^*rMszqMhENTi>ET*kbsgW!-8ENvV~r-|}Y)00aZD zzsc!tKoYimLY>JQP~i5?<9A-E=0VsVC-8Z!E7bmNyXFe3srJNIi2ewRb9g z;6c{;r&$+fvo9^=2wvn2z2qDpVhk-ZhF>v8Ub8O$^}m1Xg**rlkSe-Z0AL|eO0K#j z1`Gqn1AHEVVuRL{lkumpz{5}tO^lfW$R5Iyx56r*0x1Xyh&a2Gw=@gIA)rM3GEbY0 z&{05Ua$9sw7b+oWNT{$J(Dosxl#|BSY_w-u#ZKiszMiO2Ro8P^L=P7BU1uagp6 z9+`H{n4exWtFJkFl3l+1<~4E@0I6663Wgy}#Fh{X3qIbK1;=fyROkw_=Sb@KT~GDU za41r-b@t~6&4R?m?Dj$RJV1yFwY|xWM?X9hK;=|03ch$9@F6*EGa!&+13+wx)!d?F z^d>u8YqnG!yBjGAb8B|L;q3k9-bvpPmg@K36Vqn`?j!^lObK9Mm;zE43p(XL2$fP` z&FGZf>4B@eXpuxIN;4SOOvxT1)lFAI=w3cWCqV7|T`P5)7T2qm7R%+3urqhj_NR2h?p3 z7^ab*JA>q{3~M8BjDu?G@s&TA8gQ(#0R*$iwWcr*DY)#zkb(+wkI0)undzD^z*srH zL)FzPB0@a4b)V6Uxd0{+*=u9CDIStPAU-Yi_Smc~>^$a^cBAhneXq01HM?i)%-{R> zS?vE16VDP1L#5m!Tm>y3oRJ%;3bhtCc3^3!RGTh$wmE95(~)Pv;ITnaV@(>edDtF` zmQh=U-&7teQL&6TK~no8n%)Ii{XRbd04E9)JtF$~@9L&SOjFb**-A10Q;^nno17@- zqHX`rP*Eq_zgedv&DGg^yNlmH1=N2-PtVXu&(Nst5wTv8ao*7hKCwykIO?B?rT+yI zx;wLQPtE~R+5fS?6qS7lr!KUV7tU>qtUR`_p)ar{|5)Mj7u*>o|??z*R0WZA3iWYeac;3 z%@h11ll1=~+JAeth=69=@AqHEP%ePS{(I9=9<$7YcuCzD&eH7ofj`qx?bm9cwk)f& z{5I=)k_c#uf<(8~smDw7)CrGFwo)tfLy=x>wk+qGWl z7~}G0K3CrODpX@T#*%MmoKS8pWj0wdX*-E`YS3;kv7MV%V}|AFKWKf;tlyTSIWs?_ zdR;x^DXw-`a4fU5v5SZ4b|WiU?b~Ffb|K02IwZV`Rl1{ZrW|`!6ju)wJ70)>Eq3`t-qLp&S6#m0@=!$oM%<=a{XZ;QNp4TFR+DmGHfNt zrffFC;`x%W1XUJ0K2uQUF$FhUNZpG9H#Ws%EEmiY;gZFuQ6#m*433FxwHO&OPo}Mh zm@KeIoMBdN9e53OpCz>90y5FTw1oNb2_r(b^B!|XEA^SP2H23fpLEq)%B%>f$J8)4j&y5qzdeR}3Fw zR<8N2EDAx2WD&1c@u>(iG$(YI-|c+-kwCb{99opC5lXoktTag+qZvKUEC_i#vuxpHCIMzfkXCbG@NA%g%{B@d2A~2J%qe=!li!YJsT% z_YwVwdbZ3EaeBbx^<{4JMZ`>#Qi?uU3*Q%1? zV(~olOpz$KYOL2=f}sZ8*-#q?(Q+n^0*g%>+z?F==VQ& zJ<2i8-ZOVFZVS`-4eZIcw}%bx#ANguU)u5fR$Pwvr>Cbc1-(MZ{aOReoY%lqN)v|9 zK*mgfq{k_hhI}UW`Zz>`I&@=2(&Wu?9^9BbB%V$uJc>r6qlPvm__ksVKxj!Tb41K_ z>kYr^nB?&weZ*P;;$0UZ+^YTXt4I92HRH-#HrFAz*FgN1&3CCt=}>sog3T`NPPTUN z8LZ3FF!Ap4-LZX(Dh@Yv$RqQ}97?b~bAe<;-Z@uLPPJ!M;xsl`dEs7;Kn%2B0|7sL zovSUlXzX5DsRy^P&-M;|i!fRL(PYNPP}|{is(8Wc$>xaEh^CLiGLD?ef(jz-iv25( za?@`TAEcbbl{>MZvPHOMXMD$HQa;YTn@cjoD=Dnls4UPN^0mJ;TTEIjEdU6-jDv=j zH%jUT$A0~mQSk2B+e+!cc9SjJ#Vvd0t#Rl=gKI;am56yIEBctYE1M|WrK@SG0AH8Z z%tVH;Ql2Ql=5!;K&^wW~>T6CfHy)g<-M4zRX{j>&NFz==UKkuh2ojoKW)@4W%qe+o z5-4{t;oQ3sd6su4W8bC`?!_;$;GXi=)+S?|0JKlseusT#7<9Hm;g_a|9BNpki^&Tq&?(-a5@5e9GmCz((ggyC1Jiekr`I`Ihhs z8e;?uSr6Ut%vVk9ety|Lkq`%X_)^k+?6+oPs; z^FaIxA${0o_sXEV%VS#|^ZHqCN#+B`6*@+m$5cvK93f-818=ROd)h4%_Li5}bN8W! z!F9XVw7yC!{?Ol`t)(Ko-r!!TXL5#NmBqzZk8O3eg;&OZoJS`= zTTHljP5WczRo{G3Uh^_wz<{R*_&hPM4h#F(l~kkm?2v1m(z&MHyN-T-xX0NJ4H{Iq zY#8`G;&t}UIUPvu_<~K_x+d8G*BpL#P`C7*{+X zetjnK$s-MjPY)j4w+NGbl-yyRcpZ8*qI3IElbcE#(s$s4lDiid^!SCPF;icUqi6*2 z;oYq`_lu=>_0i?Z3+JHw9;cisxE`0Yffy%i5M^_LiG&J=PJv(Lnsy{Rrb*U4+C4uc zSliHTS0?}|&T}4_e4qp9v{sclA$bt)$dve)=K^!CNi zu=AVWK{~%bet-JQ#Y^f-xmz#INngqu+q(9x{Pd4yldU>#CKM1{C=K%cwF*|CfG}&q zOji5F>6@l1G$cdj?ViIwZscrnt(RhaXxIb%wJqnx^t250?VJKv`idzSM?GX_ulHUD z$di$38#2Bjgja>AY+bOSRx)stY0evL!7R111>0Nm5!EJ&tw*+8%vX{P6q{K>DhVJ; z0FrS#2i|6*!pwpY5Kn0m9ZOh{ub);$ii9q8Uy|Z2mgB8uC-hf)0X7xg` z1K5ilwI7+H=yIq1hWiG7${SnhAisM?qAy?CIp~ zc{PW;Ns_oC@I)>lrvZ&AKKx7qO1MNY6^e1CB~J0pyF4+^@E9Yy+e1MDK0Y73oENRj z2u0@iaPp(Zu`!)Yt!GA?KWJFDB-sUPB0u5epKp8L>@hXu@l{3&iCt%fmFVGvVB-*< zV2_^*ZnK*1Rt)E3qt5aQI$8-3W1>Vgp7mpJIFvI zxbXWV%qIHw14vCI9(juoHe-m7@gb^o_!J=K=8d`Pjku4;#0cOsBuotrbe`gKnv=C| zT)_&+6$?U7e<>?%&Ifbzr>{WPam7ozFp5iS*=C&vo{mZfq%6wFdQ}N}b>t{pc-RJ_ zU(CEVAqaVY7BTIxQ?{aD%Pa3dEuWB3Pu+IU{BGrWj_!q7G>!ph&qg?Z&T5-xj}9Zr z0?Y;hB!P=DU;qPL^K*$>AQt={A5qziI@Kb2We9bU*d@|>5+FXgf_%h>|5YY_R6smK z5+i_$tpfA=b+M6)d3{u@o?>o5gqYtrq(CS}zMp@A>Tg9enl3j2Gk5lWt{Cu9LiZiS zRBN7s?Wzny3WcW4@%8uG5$zKg-6cOTVV75>t=4U^AqE(N<2>2G2`cm>n~0SFkVyb( zLc@#-Kozu51Owfc2Mn$8P^EkfId9LZMI@>Pc{kVyK@w9rifFfEjgyR2_3BC#>v~i3 zr;d~dES8s+iT9;ueFjO04H;_$gZKGXxS!EQ-%%*LUD-dTdyEiPVjZR;9bqJX&{lrnW*4`{`)@I-!^%346|$dbrSZ?lQn!%eC0Mf_%t88SV#gOJLj*Zog1mrCWUKen1b8SpdWj&?6%8 z;*oq&o{9Jz9xdP#)FwoTQD5W4P&c6EHeFhsYq<-0Qh{xCHXYcvBxY=3e31tRFC$UtEd9l7-0{pZdB?)D(r=IS z%*f}sZaQ*2oC%$4rESXg%*cLxB-SUQ3g=fneg73k-ol{eUBmi zmv#D+l=~wy!lEM+VR&sGSf%QNW=TcmRM7CSkkZz?uQL6&FyS zg%ShLc4Q0|i$S*w&z<}}D02M+dz|ZUIEM~|(iotU@8<+!;D%Yq7jvd=Pn(J zzI3kwP$R)C1ec^H2i*A3PX+>E1z<6YLj1V+^V)!;5GwvwaIFHs3n0>sL%7KSR|XV+ zVkj^7(rU4QaDG4@3)@N?*3L93Zh&qJ1j#fCmN#7@sf--y9v}!H#^*=Gy9O3Apc}4_ zkX6o)7>r`dfE(Y>JC=+d`2bikU=lQ_-4d{C>8zIk;=>1uT`qf;nC%n*l608JIx5s3 z7Zo!Wabj$5iP=UWWHSxsCx9jT<5FF(B$r%?&olx5vyA{2DX&F{dpGd%cN^gk>k^56 zcg367IobXJS|WV)H@MoL&%b{k)khXgU><8dO$^oKCLd6OgJH%m**Km6v~H?|1j z{QQ~!>la5vTmMA&{40_n!WgRmm85o@|IZ1T-;ISLiG&5gzv!~OB57 zIE@skZu+1lCXZg{kX1)(6_ZvA;V4k?ws}Cv$JdXq@fUW&z@ev7gPvVRAY9_^0?mmU z!*G(+Qj=@i={-Hn03ot#7FNtu3npEx=Ti!hq7>dO>56 z?YVW4NWVVY?x{29Wx`oN5M1`%`js}38`HZ#z0F6f zfgndFOo5lLux~M_0Pp^_nM3d~6QCf?BuZg-sc#veM_mjqHcX%r3sWakpV_bXN#f>0 zRCyo|@^HvP8CCn}Lb>buR|^$12fgQ_oRXt>Yl`;W)&uYESv;!_%+~9u33)QpUgzJT zs9h(CToE+HZUn*`69-X8ni5|e^*EF{toVn`f&pE?@S13CrGJY}|Ho|h|D5DhRO0`^ zA*YCN{y(Joua1EK|NSXC)c(YiQDC}K|E_b@@|=jx+7|uj6!4r_$W6bUt3+&;w0E6~ zlWE9Ah0gY?e+(_QMx!qj-r>3PL~J%-&?~~N#omJ~VzbA*OWh-FO4FBqkNq7FQ$1Vt zW|ALq=UAwvPsshw@M)2u#q{1?+_>VglcL_}gLA28&~b5wKIUcGL%?-xpxC4Fhind_DJP!DfB;*%6ddSD> z@tL;^MT3*G6h&ROCkNU2mvADq$V*UHsC(?|6A;Nv>Z|;bX*RTKR}05h_)r6KR3<%-Ghp2q-tPKNO&g{HF@DC9+}7>C(49Po|)nCQ&kC zG-e?Nk=iwm!U>igAwAE(ltrei(NvHJW4Zu|3ZM&%4vbt>tQuKPj%J@}AstQi%+zFS65a}_$YTMMibqGhcUntV zKjPDlpJKs|2X?#bJZquXYR(ZN@i5Wk)8jU&`14W$Ca`0YT$Eh!dHA zjeLyFm~FE$Y#BPC(8}9qBW7XkggIej@WESi#6IZiz~z!ppjg9hn~0t~ypxnkD?V9j zLXFST@~7VCqRO~ki^>IFjK5A;(1^KfqX^d`YmGP>z4zj)j#PUov2hAYLU+;R3V8eW zCI!0o#76xoUFj{#u>Gk-v`)m`d_eKACYc%8T9dt6Z139As$p~;3MoGf5Lr_-IST^S z=0%evCW)w6q_t&XcNEMif{51kni+>^Hz1cGtLQEl3f4mXTrT(3Tr>EZAxQHy*>;$e!hgng)xvAXV65Y z0B#e;>`LH4Wd}9`YIK%@SK-=J#gN3sklcV}9?~3XonE@cRt5zD9^*E}ol7jElIG9v zhBtqoSjv}3Yqnj7M?B99mRh3<6LI_XkVTRv zE#mHbaiHfnO=q1ra*Dytg^OH*9*3=JGplNt1S0Oliz~#hcWiH`W;~d4k6a}gnchSc zqYL-iv@KqVu^ugY*37|=Y%j}Xj=6r^xnR?0d-Q}`7YiQ3fq~V8XbD|q#_5`jrOWDhNTag6wq{tkJx@vq1$hbWbcvuN z>V@;D9anFwzHHPMsaC~%5j8{rYV{Fd!sym%4ZsLkf-!zSQ`bc! zxZK9J3}F-4adV)O%2yUh*f`mptv3fb&VayV0bqE_Vdmz$Y6*vm8~iP=!#f05||ed!6_h; zmq4s66$DNQEYzp192hB+ZhCtcxJ;hv`o>ZW(Q?^&ZOf3fpDzVV4q`uO9Fq3-T@#?> zCi9UB^JsO@i0VeKCZW`unt{ofrgt)4u4Gb5+Yb+QtN=mMw1aR4Zf$=Udbg}LtWxjl zEhu(Q@0Z=%;;>S;{vPvFYzhxMuzBR91efhxIqSQL{#UdE9+jpxi|(lKu{k6wrQ6nN za{=rmALHY(``c?kpv25Ez?Q#xaYaz0Ootp2AjMR8Am-w5(%pvy8N&fIn*p2_PR%9` zVFs`c)~sR!S4fc^^59oWjzyyTbp|*V^U(K75x!jP1VQTRrs?njrKi%+VQJ%gQz*!Jn6ALcU#j;{e9pB?t!o*Od)rmyApNUmLHSGMQ}+&r-s zA0mnLV9MIHz$ui`R_7mO(vC5Eehy;Gq{{COpzp=`JD-E&p zu&LGhd+Vt5fMDPEgVX(M=DAr(aA^F1f4z-BRRADfe%OEU($7TxX_(8iF|^ed0aQsX z4`g-z2)cS46t~qg-`e#Jy>OQbRh!E(!Uw_%vE5M|6_y!25R@o{pg+iw?wWnu{ls$A z<*8C3b9c&BZwI-k2b)iBIT{@sYx@A?N-DhN!^0fJ8)>ca@X<1ljz{k~r*dVpd;HfH zV1cOf?RM>7Z`}+*pbedG9T|M&>8xeIs6cUOTmFZA{k5)MRssxqx$qW_g;3KU(YBi_ z+(a{lagp}Y<^AVh{8ezfKZ0 z)w^%`SKqt+Ml;Xf4j;{jXGXlf{)T2RDX_f7Jiw#AwwwF%1X`WD3PzxTPVg@ChryYQ1X`@$ZKVi}CrypLkp`tB8i znqS+A)29?I737SFY%%h0=4=sbHU25>d1;k+e#_@gYn?y;+ROj`qz3m}gM>Fl+xOFzG4Y;`5>Wq>gPE%5lIwJ*1R{gC7V@Y$>nU#ttQ0rF%Hav%pjkOf(k zD#HPYS_TTq#OQj_m$ArM2z%D@umEW{>tuvev?4H3U)9YgvF7=TMF2!lX-6}25c6<2 z|3w|uW#_hn&GK}{flOOX90zug#pi%T8p;y@=>i~It}cE+ifDoR?uauNpjt?9&6%9b zvd$6&dqj)N9yYkV4O^K+z6)|_9#zbep(a%3wyY?gPExvQBA4H%W|0eo2@x_vXe}1a znPkN{u^K$H+9PT;KV6=3rgvCfn{@2_nCnY^7;!_1Mq(_xtp!kjjyy6k&t z7y2wJ$u2imSJh{|%t(lr0S$>KLCfio#+IN^UeG5TtQbJEmvoVM81Tt$Mt-|olJ<$UD(0U@_k$7r7Q5t=uiUP zS4`)_#rR;yrP8h!GV!U{b8Iku2}NRD9tcT8L{wOHjo}UOsLP2!0Nr3S18mMy ziWMLv7+`fF@`EO5YfpbQQhrV7KPVT_ zX;M@A3?WaqZ2u~Agrmg#20B23;_*l(9^^v_r=;j07(liVMq?n4vx4w8(esl~6rm9T zAOfZ!Wr40QVT~IsImH&7Q*{;m`=Hf+tfL;_bQW8UZ~p#85m87&R&epnI+?b3=UkHf zv(T*S*arqN&}`9Q=WEK<|+kYA8>zznSOKI{;H z+Q>sTEFK=A9|5 zOcz#4rRD`hkUbPNHT}#SAx;zE=;jW~yJ<#2>>#0HLuxKsL^DcKT8VLS)NM>DOsciz93rfWp(kxpb7Maas)U=DT1RF07tWPv72w2t@31 z?o1oxi4lOl$mVJu^!PU(HmX6FTY0e_E2|Hr^Prl#M=mHz?cZop#sKF?TX4p*52f-?xhBzxT!F@)a!VbswXP5pU2B7WD6rW=`2|F7(LxGA)&D z0Q{0?$S%g7MdUoy8JLHhEqgN%W_`Bmd#*g^?7Ys|mm_DNzdZXEGT0n_w!vj^QD<;D zcMx)yaUx^$r81y<)485q=eosgPZw|LiavLK)48)E-!AO=;Z5g9mCygJWHtv3ABpbU zvHx==o}yVMky|7FqLu4LA(RJKTZ_eXRW z$SfD3&#;57krkaH`WaPqTqLwhJuUn#t~+xt{mfL>xu+s!UG|0foD0vgFD{7C=fQ*C zBJ|nPyt}I_RKz{6U89RA=k42xj~}IqSm*3)`hONdok;Tf?j2)kN%Z({u=AJ)Pz~<|Dwbbx4rtqqFZyK;$M__ zY}4?mp2($tDDkw#^%HeHm=;yPm3aF09fYPWrBS^ipK`~pqD(&-krFRLm74o2; z{><6`jfa89sf3TOH@kdc1T>_6L8}?|zJFTIL>cTpe}}%S6CrbT*rASmMy@f$a%G&G zXtU##LfCpC8$w3TFaRo_6)A*>%%Bcl*68ySIz_=Q-ErEZ8%E9#w8ZA&xyyF!K6gbKmT%(O2)>T@y0R2i4-qrE20|Co%E}O)y4Wq0&YS zy8L*DcJUWN<$aJob~_akjBka<>&u*Rf~s+eB}l|uMNfv2icrbKD{!f3oqQnUny!Kn ze_g#DwBb1-#rGvhP3bO|SCcupf5vU?P8!s|?hKv=L5)6Iwl+>q4ap;wfkVSvQ*R&9 zDNbKxF&*1eYiQlq)VN@i80?!Tj$G$eF?D2D{B?9F@(jz7xu|ojFvUt06m;9g+STND2yx(p zD>)9|bL`xn!Bqv|Rls*r#^*r?FzKE{*du=H^SY-X=8VmeYooAkmmY&1*GPTf1BKYO z7{HcwzA>PL21(UClXvVd)+5>T41&>0m)>_>G|78b@|fCZ5gPc zKv!)}`EYT{)(lMtvcJFh(KX~Scb-`*k03ij{53Tk#82~XGwyCLzTR+e#c$J#$YZqJ zQnMyDo1%U1Ys=7~hLknSV#Imo_0kc4YkzS+nVoJ%1ixJm@=xHOsJ5MuPrE^mVC&Cy zCR*jXnP$&KIhfv7xoP3;rP24}qfk<3>NreG?&qfx^Bn2Vk1~#CeR^DQ_`%Y9!mrC!Wko{p>jPP>0P=UKGJ>9sY$|LTuEDFK)W%h5FDfwNeYH%3cL zy+}M%XHg2}xx~V$%m$Fghf00_l0(Iitcvw&XJoZZW4aIx|RK8Wb~e(FEeWocd1P<{P<-w10u*BV$IT*!zCvI9?A=h&c?u(Jk zsa(Wu40hbKV>yHM&lGj3aTDv250J}Y}~0B#)orOe~L z9%gjFLHq&9B!=t0YuD=$V}~w`bd^AM!fs4)C8_wX$}_RU1GIcA$aSmhFR>E?a8jV~ zA1N@6YeCjV<*$mw%mmosVh1g`9nR|UhNvI zi72}n)j14_qB=}V2=anUk{l&3Y%|a6Ih32tfvHZ+f2Nc>+)53Rm_XV>BtRJ4^|wH~ z{?Jj+PdJj-FhmQPuN(H#bu}VTo)IBX7^O2}@5F5V6k@^=FN02 zuFCS`e%1t{qJ2j_bFt#Sxu&ytV|Us!NpT*CWXEI%K!-J|0`u~J*lyf(IbriY)>=g) ze#z0V#c{p)Nl<7Ali}+HCTK3&XR#|z?H@vDX6UFamG!2U%(Ddjjtzr*X-AZ$qGkEOM+00(^+!@18)*qPH@U@!+X-UvFu4&o$s(4aH!Gt1U*ss5^}>)R01=>~>$=b~nv4&t{0So7dk=+LH*Z zmw7gry%t$^(C|t6JxW;XvHLdww=+#yZ0PW!tc_iO@Q}D;IQ>y~gGbX-@98hsP)hB& zv3a{)2@KgPU3)Em)GQ~`H`GvCixX|>Y-ZLfT_R;|;JZ=~TEd)uG6`K*tz|K+cyqJT zl05-SgYCKN>s-=t3vCs|g7fBO5d+E%r;hP=&8csv##eD{1FP9zVFM$d81FQ4cTP`sLcnXo^qKKakb-}r*4RhQ zxMsJ^>TBx`yAJY-qx{#&hs|)cTbSZe{Fd77x(?#+u;=Gw-}|M^J~#4-fA>6_A~&5H zz#$5Xq6NyD70Cy;RG=_MwI`3Q$(>GNZCPQe-%sk?9uVo5QW}{F2CK|lF2_{)N_xFZ zsJj;M@TuBL^rIp7_zi>ktRKs_=|uT644i$htXO7t+xwhf^7l?ksGQp+k!aFu^wX27 zKv<`v(7QH)vNW#o*wO}Sn$628C8ytRxbhLG&nJlt!0TAIKDEVderGLG;wAD$N<6$s zi8nMS{fjxa^&+_9^bauk(l;P;%gQI6pRcA*uTB-He?GHz>Ed5Ixa;7$0$?{$B)|Lh zE%@?;u zY#qqAj>xu6HL}$tInFrmYge~TjKQ|rtQ}iWYh;rTYm@jdjRS6Do8)9t;EFMc>j!z@ zSUM(%r-$i4Vb-f>0GKJRSP2Wr#BMWT!_L_thXaX*OXzA_aUKm~z%}R(6z>THzq*UD ze3`b7T7b4LNFK+gBH03*g%G6E3WBWn6wl=nXO==M6@qJ0k&OVTTWEj<#GJ+u^>o08 zkLjgAoIVvgvVb@&MpFpB$`CW-fxH<1AA9c^)#SRc>%Qp~2)$$I9Rkveh9X@JMLHUK z5jFGWH9+HWg&||RLK9VK zNGVNC2Hzw~j*>yRE!?!A$ek@rRgijZllrM8b;i9g_piE?Ht)-Zs#AK1n@sqMk*{3& z;T)6D7(=gwR{^Gd?v|p`Df&`f0Gf{ki(wZ!w$c0~q+96-O%7&r6E;i)BN^x*VGBpN z;QwIL!VE@C0FBItS~G-*JYU$Lg8Kx;ptC8& zJ?voTYm7X{uUybSfEMyS+oT5v-m|3}EvehHQLRqE9$|n7w#gVt02!JH)B`7x1WW+~ zG0Fg;!Z0xPs3)-qxwU#j5?VC{K23Fgd<{|kxGtfvuHLcoCn}p!RH^9$xk;^iU7)Sx zMnFd?s0df#6!l>4dcEI|4Z7#X+DZNj(rc>kJTRW91%J2hjgvailC?-7{Swsvr5V+uIja@h&lNFMcxw1b4H zD4Igvfcq)F*RP$ry9jEdV8l61d&eEzJz(1t+*+%c(=mit!w$qX1`q7S+#yCRx_6tfk))F_^>$$3NR(ugirbNgM#91BN~cILBnf%O#>m&< z^yIj99>*QQ2GrO<;s#df9@aGhP#|I+;<0IzNKYcxlPj2nXp!i zQ?4#Z`rPPzo60xGK<#hZzJLd%go!8bN&*Fa*R2DAT387O#m&wHhZD}*`r$B^gSt!t z7&(rpt;jmONS$?Poies=pTbqmQV*|Xna=fs$$i(qS)C}veRJ#!3e56)+8Z?72in?o z3zE5HpSia)^8@2Lmg!Fi?rscFZ<^4|2l4KM`8PER z8wN2!5-9EI(C3)J~^6x|IfCPS9YukOXc&?JZ> z3u(u^Vo1M&)B{;FAZx?B?_*%dZ0K?NSljRziiO+7f`BgUhWN7(7sh`1L47=|I%hzXDfdB5lqAVK+tAB~4yc}zpYX4^Yk4_sWhI>#dhr;) ze{;s_FOifw@$RqOYi%uiV}rJ_k;5}ezkSQy+RFX@J&(sF{`!^A3&412;@=tA?>~Zn zAWN%^g8!@^Ph#gm3s!e?PBzrA_;5KH4!*zqM&3^-a0Nk^+@#7+!`JoRV?HAI@xsN ze1znB@vcEH&CqLb^Bo}7*7gTnb@p!fIO5@R`+9Ek( zN6A7&e=9~#_XWgwa@nZ=aoy?XcRtPz)ogd|>iKUrp41)Xt|yu+O2HwUsxJxF}b+)^sXEoq5v-G_?;;WMmo~tpOaNY5YO}=huAfbdx zX;-d)*;GhXL|Cu6`l|c-I3`8L2F2#r@t&?e;5T)Xg1%1dC~G*P^`akwjVz69cw95~ zd{)!2{bOp=K$c2pM2>*1b(t|$LgZraA)nM8ZLeMz`Ct|}8ahdV)tym+64nD84o;Nu`^6_$BDv=ioejfUTqOCP;VQJ* z*3D0HBsu>2*%`90n8!7by%yp_4^5jF7`r7-&t1~=ue^!+dhSASP6ckADE;Er97J!_ zg0M5da55pTUvURzLicTNT-mmksXV9HY(9e9`jU#-C69leT*xP7dU3qT?8bd=fU7Wc zl3&mFrn7v>2Wlmv>1PuWY5K8n0|~Z#CV$5?YAoPD#--~z=8-W^4S#-wPL}=rWcTjQ z&(A5}`G0X^Q2T$ag-V|IwNB}&s*b#6|M}$lJ+Cp5FAu^6M7};rIwbt6$O zq3^HT&kFu{H;`4mv2aCqx8sMq$No(1>rD7_`}2U3*q=ad;ka27_#nMZ_PGSqJN9yi zNxCBcMXSAhP2**bb9W7&58<7~TIH4pr?YWYGieucDg}0Nee32Jf6|v~A@o3LmYNt^ zB^&ibK38Zmx%jZyE4hczc8~2#@}-@&`V9~c8!sv2=xXvv?$>idf%K5TX{W8J^4Rw9C* zLeOF7IMeUQoKI#wA~E_vqN_xLlZtshFB0MEyWCC>Gf42@Q#ltbZWJ_|8FjAg&?&OO zi&Y2i+kiOK8{=lUshB zNY(QgFBfwNHop-0pl7w?y30`ysT{LHhtHCo2VKLw4LCEfGx9~sk?(-qt!e!5CNuFd zRl3V!o2mph?)19$4qrm{i6Sltx_I9@f7T3YZx@>L%#ANx_}xBA+E2g3EewQ^H(Vu5 zxMW_AHAtsS!*(h6KG+x0_4KV~7)?{d4JDRo5wmy4WqC-#`n=zJ+>@GAc2^ciK~@|K8#qx#+LV(??DO*4Vfue_=>^1Z&5t$DQBPc+>Cwy=AL?t%^(D zC;eW4AEuYzEdNAFb`90Kl_8V}SeQUyVUOT4U-5kTsa|^N*WAO2SP9DMq&#TM-iIo# zk`uNAy>9V@j6>7;We#}BtN5H;{qv4M+zRX_H!piFq#Z&RanE4eMF>z)gcjp8u&`RH(GKYIfLpEI6?onQTIFl>|K zGr|Fl;_U=3skG{3ncAlm=bQUo;R`gAm^fN0ac=ME>pk*&BXcc?{8#1R#m2Ys1V zXgjy_On1nW1s%hSSK>cLA2Yv}TY63&hew>33K3Y~CXP%U(K&eH0>Zpf)eb%*Xl!94 zn;JJQ3*AJ=^(xAa7b;2ij`Q1=UW#uIMk(NCvE-Rl*Bh2_bG+)%jet83*Jq!Bw&^~z zQ5USbW?mEQ#a?jH#;ZV~yUeoplF9yi)?kSK;9GFWf#Joy$I_qr3$SD2sui1@O$$u! zB2FI-$1hvYx$qg52Hq-@kbPS{>0kD#dSG;q;3x&fi749uAI^QJks~@BA!BpM$K|5> z7k6{7N0%6fbCI7SYldIwg+;D@4EPlD=kRy1Zt|MoRqhLOi>1Eyt!sW=Yh^WZD~fkV zX5Hmds>dwW^t)0xF;_Q6ep)yc2k{spjtbae^c{i(K&ZQ(+qchJmR-boNLoizrXTL$;wxF_N3{PIdp;TpCd~Zws9>o2Sjwe z45$wUZLbqG1>$UHJ(W<&JC1FJbqPwNXI+yxNEYw70-aJ>M; zl0rkwp)cYy5al#YWDeXF`Nu=tpT>3|lT2iU%}G)H^!qvDwRYX>5e^2Kf`fq$4%xyp zNpG)PoAUbI2@u{sYAjA|{tJ5kq--gP?>z^L%>#~eoq0_VXWdW_*?68&mr}{!M*$4j z>bIEuvs`eW5D z%SLtMLD%vALL}_dIb;_F2nYCYupnY5^K@tcmCUD2hP-5BEf}CsHs1it?H0*54&a|< zz@xZ)H%KU90@iv#kky_=&BndnqMZvbdsB!rN=_pH{8{pRV)UHPJvl2)s_9FrR0Xx% zAhi?u!HccIrt4n5*MxEb{&a3sj51+QaJepjhJF0&r)=u0DRh1U<_~2+_NoP5d?q6HAPTS^)v(rK*+>krpb!>6uz$e$YnZQ z5QXVz#$4e@WJwenJc4{Kq-m%X8rPWn!DIBm$0p*OWD@d67`4Kcr~P#3r27-$v=HGBLL(pp~NRZ-MZKba=jx z<|91vgHcuq@57k|xsBrwr-K*p_2^)fAQ82~tlF2uKmgbtGEQY4j55msQ%+jqPUKN= zuIEw@qT7N}bKq9kK(mItjvS5ijY>Ik0UL${r@C7Yl)pYWvhE>L!sSb4(YrYGS60Wi zbqc@?m8d}BW_2M<7!|0-nb;~;6wDgH=a5TeR9q||fP>tN2jYnQ7zKX01VEFH{XpR> z#fOE{`NG+Pg=GG>bo6yxsZS~(h36|I<4k-K{8MQ^9zxy#2wuW;kJ@nMF=R~Y!lds7 z#fpY|*Drh|o;Z?v;+U^-y?fm0;7ZTy6nk+Mx%Pzb;|auv#-w}vueoSvo71K-2@hu# zVM!EJP5E~ljrD>zg@)|ASr5C#87-myM)%88-Bc=JZ(HOy2dg=Y)8#wPs30IpEOHd)cdZ4kGH<(ChNvlAqA$ zQPPWc<$s0iTMX_)W%v2Mx7?5E^Oi=ta9}|sFkBvL%K#3gSp**L3-;>|Ka7)OK&{yQ zQycv#HpKfTN`(m{^T6CiNCN;)##o@(&>#xBgNkm&!wzEl5Fn5}8Cvku9Lt3T5WolU zXiwIFkLC~p1a%~VYKG07xhQWoL>UkF1+G})ufV$@4O~!riMav}-rxsoB*K`|!)YZr zX$I7sGCVL393rCI31}ZCz{|HR84xEHXxiMIl)^!|villu_BEG4MM$HUg~5l|gJn-g z%5RQZ5{IfWSG#Rs3@#KN2y5g*y)mP^=qOp%)$TZ0GXaKS1Ac6jGihwqZy>32?2--G zpMdTlqk}n6M#)u60(1|nANk8j=-06LE%V)6h*v`&a&BBfYeL#Z{htgK-*01%%e#3) z|2F3S-f7_d9{m6M{EnXh61C|+ooA5P*rym^Md;wS0YO%uXDjJX@9W+|`i z|1r-nE8KXy+A;?%>(slnpT37@%t>vk&9{@9+u}9)&M$Ndo#V|jBy>KwM4r_7L!` zqD=jAnM3EH-6MM?zGm+Vk4WmfT~BziJ7!nHI*mik(LZgyEh^;7PHyin)|h%8bI{|* zpiH0vx=$TLo7X_*p<5lU#fcp8Ndf|oe36%sD9N>UMm#w>`W!i-=%+=UR3eg)V5Kk<;<)s2GEs(nUAcWm zQNxyDFORb&6iN%8VmpY6pu+-F_g2|u3kzF_pDy$+FZ2t|jBg)T-0ML_3d?k}XOM=k z9c0SJKcaX>+f?G~2x=^EFTq+X#kcZ;<9Io)BA02SC{Kyc^B>6ghs;KlkB?|WNs`)6 z4u&Tv`n!?*>SyTTXScbJ2jA2I#X*R?6uow}yi&O^nb_M4PRgMN`DY18R5VORn*&wW ztNniAz?_@W#jfA_+6W?-r>_}Hm2%H$dQV)-rPkq4l6F^PkfW<^`4DBTAF0Gj34?Vw z?1X4qAk0gtn~JUCP^fZoT1?&EW`%`U^?&mBd&!U%Q zm(MC{pMu-aev4}q5i^RtT59sG)%!nvmL_l4t27p=)sp691!^g@$y=my7;h-=CU2hM z?vvm1486bS8A8AF<{A7^`?eMjNS19axv1aSTDCRi|Gr}3xbOQb1D~?*uXQ8ue1D^o z!vAAczG&Z%x2fGBJKtR>I2iYyIeaPgLvKk)>c>HMleAAGC11_R;xjRcYL$4sg%LH6 zpvh1L8KL4HrO>Ah?^QLEeAaIY)N~(xRP&_%L%D0MwNj#oFGwtaoJJH&449gmz0;ci zCNgFZNyz?#^Nyg+4c|laKR(_>KB$HeSu&U5 z@a+@GJ*xRcyz7NmMmy?h3eU(zo6&dy(^IwrT!q4`4o&bVHhDT@#m7z@(AP!g;?W_-b1^GhkxrGG<60Z1PKqb`a85tDdom5;7JTvEl}MZn9-PaBz+_l3XP0$s1Cmx{6bOft#_Klyo*dPZ zFXW5H3nrSd%2GF*@vixh5{bt!773(ly97hdWGkO1*-s53?ew>!0JDYJJCToPcx`5? zmS;IAZ7n>qjsX6{!ZiVeVmnWB9N<~;I(FUUVq*yPZHhL>buHpBprdpjxW+F5@_ z0|y4jI?>z=f9NI1zV1x8;_i@SFDY-a1PS}%c9EFPjFflw*<*4W_5fGZb1nX$k(m|V zVr}N?o&D|at>skq_vKizopZt#&bmY{RQr9$xI3QQj`SMsIPcqs4v>VE!9yF)goRe> zTjFhDp12|@03;~!Nb{J{yNBZ~Q3skL7RZy=%jtpR7mkgB$y)ARY1gRwd|MD%H^!Np zukRV3MnPpQ4v(KsbwScQs{A1G9!ILb_Rc$NpAC7ZO##{Ye|3*ktR>cM-+AEQ4q|3z zH)n&yYdc35YW#cp$+(O6nM4>ok(y`UnL9t+wiC_06;5o>IhXp-_!fc2 zavYK!KI^7y;P0Zoe#ZFf?gvG79b~P72Lr7AHjlEUEndPAvJ=90hVpjG#PMgDyi(AK znQ|LKj0HB|sP*R!8ML%z8443xhfJF3?2sYV_DreKorDn-}( z>fsj~NYD3(PXjuiFedgggaRjrKt-GcxV%Y6xU6dBl_4irZ5mg8z94o?ITy*RXsm-I zlDJddJMz}UL&4Rugfp(QjixkU=Vmr|FUwX9++BbZX@Z+||Fk-IDW4y|@+z29yA0v>t!x z<{S+Ev4-+t$*sD2zOzY3uwmhBAcLsN$3%?F!$NVM)c{hJUNB7jd4kg5cb z1_5=1=|UsAnvhXhT(B+)MP4;0pscO`uQ$Rp4IWNsC4@^(;9!%|OPt|=0s%DE7 za)C=VS*Z#VBn4%UAZ70+W&g`$x$3m5ORnE-<$r5ktYF+LzZ=)~zJTuA6YA8_;36XUvJc~S2vMHWVHbEGYvN+2C{@&x zwkJhxkEA0TDMbn&9gubjRWG7|)LPxNP8M<-5t$TF6)2q=NaML>J2*%i08GOng~+f8 z(cqo20Fr|U0APhAm1Ckge_WH=%T+6n1t_?E2sWgg>OuUHShbkRLgrtc$&;MT`)ZtD z4FpsZvJQmH@#)c|J2cOTY8}zjS{(FGT;2BXyQXnzLdK+3XQak2)mp+A5m4G%<{Sq~ zr+d}fRErxG{gR*F(`pZ~=r)?dJD<3w= zl9Eij@FsJIIv~qHM0DVK+QokqNSt}4!?h;NZw2yd!=M|U(qGj+gUiewtBPBLH(PfZ zRcNLO{`R%(Q{o*{0G_Xp=EuZuXX5g>F1`Bafi~_ zNFHTdepDT};+Q|`lNF=}q_P_li?Xt6YCZ_*E7}mUs}*zLgsiU;Bob;jr$BGJuvX6~ zn3|-o{Wa*sS}i;l&MRW`BN?C-Q;e<)x=~Sn3?=tBp!nR7dWu{v+gbf9qLGbs#T`=P zBDIK;dzq*+Tv!G!Cp`r)Y^`ra27f}N?s6{`pdu9>gOU;WQ^ZCwUrj}IK-5<=ys-4< zQ2ySphA1I)P@L3Vu_JyqM+*aw&Q>3->b9=MqZ>%@i!-OSuT)BaBP;A8iI0I}z~RgJ z277j1{=Vae{SBjX}Fhq?7=5-)f?`44f)?Le z!SuEuDavLdp!qP?bS+lTIL^*q(kQ9Dr7|{EN+X@OgtXpHeR5PI#jK-2u_HfKtgyZ# zd%3ODET-&7huA|w6dmYPJXhHxbh7x|*=7KZ`)|Ms&$?RYT@(M`0W0oq=Km2`;gv)E zM_}bYt3ChkrBNv4RG)o!>006Xv(u+o6@k=sJ*tK9hwtjL}xsou@rRrDKJIk%@-C+#<|a^Af| zx6=3djXq7M3vQ#uvd+~C-Yuc?=L7tfqaFGw%bp1s_e;-*ZQq))Q8()kjommHLF-#* zS$SS~B4uRqDEhh--uHlFG&klrZgkTkJss!?bGo9>kqxMOH8)Ugxg&a{uGvatfLBW! zkgpe7RQ+wsY&^yJl6cL|frA&!8V~0*Ym|F-4j9BN_Q%O(S=}lPeHYfW-{N4iUii)_ z>bCf?S}8N@PfP>t{ZIYZ;w)tiXB7;!eHEYx6>_mBUk#=@%y!vJR~{?Ip~5C6=EPVY z8(8_RZjVXr1&?`WQN-pe@pPHDYvNk9dzYLIuhYlNF})s&e5%5O4}8K9t5zK7S+1(H z=;)T{-o*Ds3kOqVE?vf2+6t3o3ii#P-zg{M_nnf-l!j;BY|(9jX9g2%0f@{mbaxH$yPn6yPZL~Z>OhS z3v$80QB90M-3irTWU`XtbWra3@QxF0d-B9O$0Pe!FP#}>+&c8YpmHVXbgbE{Hfy)T zdo`x`r#quw46R1>$PB%c?~5(NEO!Y&@7>a)NO-JvAC&)bFm&l_6ixTMT>SI?141sf z_+AG*H-@$n^lt2Y_09J~>2jPX>8rI&=5YNVQI8-gGLw6{w_Z0yd)|@sc-D609K2Z9 zGdh}txyFtO$2#qjDB{U%q;Pia!7HLGqN+*Xj(6TaX2Pf2~b2M)xSLnN7C49 zMDgpTt~Q!&79@SIALp^MH$8Kx#Rdx#kJO7}IzI!t?;bVx>w9_WXJd=*@Xya9N%6h`Eb(KPxi_wc{#tKVJfXkAKD02r zKIgUDlZcMlwP^E2)V#i@6OV$zxbImWr zAd^xbHM}%9oV)t;X0QseWG|&m-eD7ev~1_3n9ghe)og}C+2my@GNSNAHzP%?;#z5M zw_&Gi<F-j;O#05LijA%Gj`(SS+uX-@u=0M-EbxP$tiqk zl7#ogiFb3;G19R^n-{JAb+lD%#EI2W;O*$(G6IBk&XAN}LCv?RXk7 zlByh73q|hZVo0kiUXSL8I^Z=xKbfy|lMDXKW<#=Yt*o2_LGmbkn0xRNy;8598mg*fD&iDJ4a>&-fnengTA z-pOi;e(7*rsFZ7QEkrQ0C1cn{DlMQw1e+z!U=ekofpo_X@a|u3l_&i}`;-meI-=8V zSPnVh%Oa3s$mDBEPl9Lx-UWu^LJ+~_Es~?BJ!F_UFaCB_;W)fq0NyQniJt=UM_M}^ zHv?qp-S&I08M|J@~IV!X4T z?|q`c7|x1MiHNb~5PA_?N02~F3C6)Gyxo%J|9D06xhGnJHP`tEJ*dtI84O!2Rqg8t z?8&@#_#lOQR<$sDXm8?7_k%`|c8lvBDYA!No^^ds*y=;ql{rDZjH8sU{4!igL`(_Nz+6} zF3#7-_>Eb6AFNv-V6~>9tVi10_EYJLw}`j&Lo>TwI&;DdyNm)A3l6(4OdAbo zN7x$FP;ab}TN`xK<2ZJ{-BYP0-X;zW<0-H=@%o!hsz+l8ODRu{Vs_abR?bSSyRECX zQ1vz2GI__-skxRnUwY<0>4Cs@qHRHm1H)aP61{f3*f@A~bi*<|^Ut(zVBM_`JYZ#k z2dp&mfE8UHu%gQYR{k8Gbw8L?JvQ=Dzf0(}1OtF<6#;g;e}F_-K>f={vKa%Sr*UMp zmK}e*cmI55!Tqj=84CCi+?DpEo&RX$Ana+|T8Xj0psJax(+3!7cNuxwZ}icGRBKNy zP9^FGCYl2Y-TrzQd(_;#u&D9l&+8SaTCik>!wT#fzHRxb(s#w84BqD}-(l}1`I}=g zIk#VHDT~cLH}bIe>y0tO;HarkjO{5v(Xo@rGa96~)H@NSr2Y;$kSLUof+37uclZ9|q%QMO`?4TMz zb8YV@ngzlJS>U52Y!FfPP9Eyc9@%sb<~{`j$^$aU&fqGTArq4aV4+Lki!7siywY7Z znC^zTkH@HSoNjWCRcF&QW^fcWnjw{@Q<$}`nltt~eH5>t6r_N!QP2eM%RI36^QQcE zy8iyVQ@nC=EwDXWf}8$;srWIZaEjzSQ01Hnb)Zd2dMfDX6XoZLNJJ3d;_E%jUO2OC zWIZ0lWcmql&=2MiwG_aMj_n~runW1$G=RdzsBpnEmx1kEU zU}1et-0N7T%>=a2L>b_vpez`dZHOANMz9!q7KJA|T754Vf@z_+yjSES^5utwF^c$HsiEC!&BAg+ zV4kt3MZ)ybLD`g`&v4}o5lkTyF-#%dI#3dwFPY31^M71^jx82J#HQW%@lBD5o2gpy zuD466fBncYs~oG&OqUhJV?Y`5c&lGEI*a9wnR7aUDG^in;GU|$%s}nWdV2Aj)d%gO z=V}zQ#t#abDlh4S&*AxcNhaY2xzav9h1m!*UfjXHr*faotu?D+I z-g%LQN-iM>I}Pa^8CM+=*we z!hp$)TdmdgLyVbOa@r$R0ud9fyz^y^iYy4+Lc)ksPG^}~oKKeV1hBpxsqI48Sjy3l z1Bgi$*no_=L4hf7Fv+Zfd>km3jcq1Dtd|WDbTrooA`uM!Ih)=re$tRqmcXuZ`raa= z*6Q)&>_tK=>`57-_+78F_QX(;4_ z_oM>(L}Swp>JhIrLpEajCK(yw1`%L@XXAljHWqvtE0O>x;V}yUHk}gcg~xia1(Uhh zdw3L+RO0Llp`WZX#8r;=N<8+?Vgqmv8X>zmL+2-Z_~_6wDzv?G2)+n0XI*Kp z?6qeiFP98;_(4PnP-7}DW$!D;p~{%hw!^(+evoYQ!6|d2UOz~t^KgtoCuC03Y~HVT zOtU|vVf3DPuhV<%*f7Y7-pBFNe_As1_NSQ<8De|$Dq`*Grt{dxVYz>@CQ-jxlW)As z_us5Z@87J+|H2UFf6H(6H*4}YYw|a1@;7VpH*4}YYw|a1@;7VpH*4}YYeGW&&6@np zn*7b0{5RHQ51Sn*5{T*HS~xL5TGq8h!aYJNBTMoqQtIQyjZ9}T~|yNi{~=$emuBIb{ZI9Eoy_EDB^99Q=|?AQ~x@$Qcs z4|E(y(jb_9Paa;J9WNB#q5Z*g+&-7;;#hs5C|8Pzv(&$TiYBllzvgXhn0MoT^;>+} z1{GkBjo`fVK9#OYR(e6xAp&Ll1kD3iA0WhbeWG++w_cid|8;cBY;jq=%tY%{c_H-k z0&B-K|2AcxOR-`Gx07cJFEXD5UjuwZ`0R7KooA=%ij)3RDO*Iy@+Z)nuzF6xGTSAWG`# z6W8+aeG~`vR)c%iluF07a*;|<(-VoZs#uhO+;O6VdJ}Vw0}^XtG>a&jai{ZXz3jq} z##=S`LkW^?ehvb13_CS6bf{@`)Gthff%p?l&=gH@Oh%+tcZ`Gxq z0$lMw_5(K4(QLNF*f_87i zdm7+&%y_T8HD3%ScKp#F081_mLaQCtDq=}C6ZEq)Yr?Ol6gzlZ2P+Yn z<~H{8>^XBjJ0xznZV zY_N4dT9A3H$)n8&;m4$Ttck7!k2TpX&SOo^it)-(x?*aMpZmQ1!~6TSwV#`ccQcQE zdDA8GrTw4>h^BAP>2dSBF^G7aXWQ8X3 zTI@~a(@SyU>oz4W$BeZYwsN+t25su1y#H1yB6zh$5Vfj!zq?Ej)UBo3uaoOzTX_1Y zM1^P>lDNmiLCSn$&$-3$o%ORznG+p7uuRGUbPOQ>0H>K~x>A_DvebT6;?yba6%TWZ z<;=_;yPdSD<6_icSwY9Cy$=d3i*l>2V;@WvQ|~JN!JAsQrcpKHt$hs%2g+J(m60Y0 zXokcdb&>8X*GnVznbB?4tplO?bxVGv;R@c%Ji94*VZ|k2M|>m-54GpZBkWbCu;FMQ zq*8f#smL=(zH`-tpep%BvF~ORy_Af*vI2pMabYhrg0Prv6RH5Js*x|D!-1;>+reM1 z_+5s2=Ic~@rHo#i2)i-*R1wM8KDz*oT@=#cI`FBKyWSSrw4B})O#Ct1D9F~!JUDSz z!E07l#oro%0df?DTF*jqiy_S#CsShy`DhA9kb<0TZ+Vr2Qc}QRIN2Qj{uYR7v7kKi zj{G_agh(GZK8vTd>?()D{pE#aZwu%<(u=GqR7mLzhA0oNEu5SK24_)NY-#9 z$4bSdZ@=utb9;|P0sgPd;3BZ=jKpi52fH~fBJuJtTaK;J!DhHCIS`~qfyyY*k=le% zY$|oma50qR7WAg;D6P`=46@>6zAz5CwLK|8>&f23dSkqNZ#>i5&HWShi>0tPw9R07 z=DNuN!vn-u{WJ*3HrbPp=>!rc1cGx{nK3OFp*UOYa`|6!agT&(a;m?Sum-2D;HXEX7qLciKojy> z9Iz9Ij4W{wp*5!i^JSH@72RIjxvo!Y>5qu>O<$LXjJ> zF=-5QCXT@_auZPy;B4Pd2e}sCuiH%M#^9QSRVJxsLGb41b!AEK1<(9Ue`(17e3gu= znCI)^U z4=s=#+7>oSbi}2AFq5Mzz@No^fdI+Yx3bo*b`NnJeWov&Vfp`?GxB z_R1o-5C^s+q0GqY+j;QqS8^U4WD5l;Ndb@~*y|8$J+fv1fKt>4XW&hnIB<15xYrHY zf=9}-_cjpY&f4dP&E&_=;Es5gU_b>XCIty5!uDSkt@M@l#40(0UFDSbtqZz6wQ~E@ zAa9u#7mou;^}Fm}6*NGP$`2sBi=qTt`5vaox*Jl#o>D2_{I1ipr;gh1uY003k{T=W%?@=_nc= zqEAKTuwncxMAR3lm-acELGTx<6^GRT1yf0-?-?VtI5|41qI(-Dz{(s`6CU4EE7w;) z8LM7*S$)LLw;&=cYC`4Q7ybHiXdMyyyv>K2?6`iwGyJ3{g5yR|cCYg;E0B=N;J`7z z|D)u(Z3)KVVG1nd+gwNy6NIE9{O8RdCgXKTs3BerE1*fFqfBrz6fPnZ0;V$q4zN&q zwygZ!?jt6Ni2kMm_|=#$&WS3=p6=fXLIm?)%U7 z$vSD;>o}mrZ-O7Tbosr58S$qsO{h&PN%9BR+lf`p3rRcM=|)w% zh(|>1OK52S9xTw|QZXKg4=k(A2BqN9xnwtcm1xqO9hqKB#-l6QkmKZdl|10_tcWog zWkMntGf^%SAcF-jNGLq)sQzJAq&5*yA)w3|wV#Xf*HS7r&Yg9-(xUSiv9pe9m||xu zD$`>Fwsdch8!SMfcZ9*C330$L(T3vJJNqBwt3n!;xe@ufCntP%h!PQb07%h{Cf$P5 z0Z=)TBDc;#WggPp38-y(Dmb5k(xrgP@Mj(_!mupZX?kshq6vbIut){`&_V-)wI}Pq zx{skHvn}fEb7$Yx#+aDwrRN-wVmz#EP0TS4Fr&=B&V8q1?A}8m3d^`tQP#|=U$t#J z%8wm#3I)H8Zt83Fd8^2yf{`3p{f6Nqh`|9S>LBGLh6~CafSkrDG*CU%=^z>jO(()i zzk(~c;7%y)EFK*MAb$Qv_&6s+|vy zxDvmepw2d}w`syxbsBIHhTgiF3=q;4R<%y^&Zb_i&CTm%Ugeiw?)&NA2I^~m&2 z7wqaO#96)T*}InMY+m5`vv=mZ#ewpSrw=pYOfp}o_9oxJFVqm%lKO8|_D7{zL_h7n z*VA_&GVp1mKfXjGF>L@%rPIJO7F@D94u`BiaK|raYGg6 zL*?d!B8xorX%H4NB%D1|uSrA^K{X9SXW#E`G{4e%_)6K+p_Ya#-zSHJ7KbkVywWK> ze9?UPGh|p$et589xNmrP;OX$&=3(6UFmKgq%x`2oZDh%2L?CeF*3*&MpCdP=N1x-D zNBBXb57I{G8%7s~M<3*j@->cf4qIw4!5w~AU;MK|m-w42`JEC8{Z~_>|6)Y+cRuvu z#fz7}4~N59Tv}RM{(V+fR$je&^_q9yym_VDVEdz&*I{DSTzsnd_YRb1-}DNa*J08lbj{48xc}nQo~X)-4CjGM z3xn9lPoCfOQhSlNJLQL!-g&N?iy|A_zv{}6vjj^wlWuJ(=2)n1`GIYrB!7&5?Z=zEia!|TFRIO~-k<7DvxPYD ztL;C8=iS@Fk`;>O6}bNgXYUo))ZT9YCLsx-hhBwHq>J(=```H{W= zI!gK3*a&PB9&KNeq{(4^K@rX2|LX=emd`*~AH^%db`0U>Q->2HD5807y|bR|^TZ&K z1H&LWeHySHLftQx5P7~1gKm}AegYP&w17w-GZ8Aiqk9OI-YMo0l_`p*YtcS*iiAq< z(;FlU9#-u&ESd5nu_GKH4?sLEJk-48DeQ1)o>HE1{j>hpZ$0?4EG ze#t!K@RoRtrkV~7h$rou8Tk4buh*TgUP zAderfwI2HE6KB4|&nJPOo*mrG6?k46d|2Cplke)IFEnDxobTij1g-8}a zleW4uyFRCTijbPa-u4YP+xw!y&0wAo{T`ZmyG zlUtoLC!73&C|1dW;RiwGeypABP~f5Vog-Gdes&LA zZJQTtua5keo;{xKV(q#5mT{p=b3CNYr)VRD^PRe0na8A;KVz7yZAc-E$aLILfUnG-o z>h=UJD_vx*D}MyLo-ra`fvFK>ajdGsQwbT{dM~8O2_CM%HnE7&IdP?jnZbFOoD2IZ@2lpmLfD zOf-pLKsot+93K}<{pLBLoI3#cun0B?I>6lkF~d~E3~Tt$^kKH%Z}Vrfc2by5c=X5C z-4)lVL{tbR+Td~;Vcax17qwohs2|<-^=S&fW_g7N!yYnw682rSQiD>UoJQ6h%gCg{ z7kC3>!0PrM9OM`EcRP2o4^bS=%@Pu}4N|h%h!yo64Vvi&i3C7DkV_w@px+0y=~7qR z<&K6OMbNrpkn~OmBcFntz`0CAQ0X$*!|Z(>cguI0gpT578w5H1Wh4&<3%FrDpwBZj zliku*4Hf-RukUR3Lf*By-t@dd;nxrY0|I=HEke!hGMb*j2c*%(35x4e^%}qu1IL^F zixU~WIvQ?@kkF-Jsd?a58WjdyWmNWi=3KqprmpL2DLIo)5cvX_O0%!Ti+E$#un#XB zhM}O?z5yrM7|>1g$TWwH>rEb~V1O`u#zZrV-iKj*L_c|g+ZNB1y0vYUn&kmDz#Pql z9TabX%({~C08$ph2{G2iDD0jHX_Gz-7A@!={wHNu!^oPB?FZi0xJ*@50*nqt6#qFh z6OMWA0-OFg9r$|mAf~`(e^{B6RuZH?wkQQ)K zSs=H1-NR#{GXpdh*v&sio3d!M`i{)0dEpP%^RKIGf+OjQ;7IyTa3p;uIFg5H z1|zf%A!(17>{2ddE3YmTX#gKPdoO!;;m79#wBX-m@K7QYcY?k~@v)P@OHD@sl5592 z)DTimSaBU<=Qc|2jgJy2+*Cv2B1eCxeS7%bhG`!YFQ85XQui@B_w{>{dGrZdEK2GG zr(TMqMnns&_@b2QSS-P6;P*@kK%u*B>YQ{aIJbIpGb;lu{G){8^do2(5~!O-Qh|Vl z!a=bZBIH~+e_sfw8G1t$E`kF&iDD&SQR4gnP&1eaMh&?Po7lk~)@nZCi6)ViNaQs= z*rlPvMK}j)vN55vFQ|nvFa#2ui6z2rK!`C=>%|*}eXw$U7!yI(5ry=DRc^xtt=3?a zT%gB&GNpZgH*c4U!^#0LHmr8Os82a3T!tx%k{T!16{U0uzrKUhm@w56-ry3gP?0d? zmN}*VA!&d0UQgcP9snXcxsu#6$9$PG`^wjjKZ6Zo7Nr0OA0VTEaOi z*APsXjwibj9P5zp>%{I0fKa5p5e7=INaey|d{_f(H|Xt1fCeG1L==>R{5ztBaOm?s zHjFAWlk*|L0*FL0BoTNBWf?K^JQJb*H&zAOM2rP3hJ5Wxkr<15xd6=7XNo@tU>8I7 z+Ht`xd{0jKBEeNr`3xIHz!3Ld(;>xapFbpxuU%rx`Ls*og(jQ>kC!p*@A`NHt zo+Z}%4oq1i;unP!9D^8wNnG)qzHnk;H15g`cdIUn9~)&74?4qT2b4ux$9tN|MImAS zl@Q?pRpAj;vFE(@$)=cUFl76ob@GXMIz6k_8jPP-R7M)qD&>~o15CSVQHhS&Y9S8y z0W!j3x)vii%QN(kfvHF^LKGH>0Gb^;@Ds-C5F5S*F-U`@`iZ$9#TYW#bm6T14#K`= zBK&Ja8Ax>9xlnje_HBM#$9T3hF55D{&_)1ov3DyM9`*7EHQ8JECx=AhJ*l&SCo#Ua z*CDwI3=ZoXQWhh*e#XvI*QZZTtHH!$B8)!7r3hGdYjo=mRO$A&8>@z#S4P zez}^ygA~Gv`4Lihe`0_~1z%;^ zt;=kFOzJJSY!Q9{Awt)DDI3&Gtc`~={=&wN7adSbJ7$WXT@dnf*)E|{1{e|~BE333 z`nGYc&;jDQ1nMDb>6~za5XJ(mk+SI4h-85nvLRZdG!kq5SbjXAW&0j+8c|^%A|GH# zXxYXhNK*(Yuk)a^SrErMxagP!vqoGUT){62j87}zpUo`Kx#3tJ1zaSNMgXYNU;|4) zk;}$@`KDpPrkVWISIeo)4ctB?@)Zbqw#Wc2Vn3Ts89QQGBXQCq40I;8m_6cYlqFC* z&L{mWJ!}@>qkosrH8xUBmM^{p#zXX^ETyoIm>JG$w8r<_tWa&PrkFA4jY>68J9K0; zWNi1bCnHO1zH(P5z9%Mc`kn(Ib$TOELY@M%cQT9|6F9fI;0{R%+#{DHwg#fg+GU;)f z=1QBPLc6h5yXoV0^TKw^m+jUo?Y39#7}gF4g$^gH4wuIrZiNJi2q}Q?c>ABWA~-;} z2Vf1b1<(Sjm6gP0Rc^_ti7IGH-??{3Q$tb32#vDP)3hI=Bnr6uIJ@t;QheR z$Ja6}R(x9AIx@*VA;&Sb#5ujfHM`EUxYet;&8xKiep!ciMfZcMS3Wg;zO@6s^@D-U zvyWQd2DQ(JbS{Q;Er)i0deZax$*Z;SzMb&?-RP)L91d4jR?tvi-PZb?5T717{H4F| z)dC@H;P~75`Pt>=@vmRMuCA{B`s=U%oQ?n23&K&^1jfb>kNi(~hguAnL^+zNtO7+( zOE@Zb>BkHwvT+9eW&${lW%3JT7y|)F4Hij_Fvpz-Eyk6;zHkz4Q}OP7f@Ek0*3^LfR7m;*^K+*ZWs2 zk#WMB?oa+$8-t0k%~CuT9Zq7EGDRi__o+kDo?NAP{bmv#q!XvhP0j}l6OE;>On}$z zkGH21O%#Jpf*-hbh)ZEbU%^{<>c|jnB0nwLhN)QHgxZP=#P>d_^X2A=cEuz!LEto> zkJ1NWM9yj)T?l*4lpxg9wSOb#s+y zLSpORY6l^)^=~GIpmvzIdJytg2xxp85+VSya(D#Fn zlbz2$zs3@gvFmi<*9UR5Lbg*~%>-$MTf0Q(Ma%YNo`mP8sTZxgZ_3mX+3$6??Z2xt zYP6m1Za-YWJ5H9|d)e`2rOW^D)AY;EZ=d_3$T)O+x=uF7GKK7BdR~0rovqX^)qT}{ zez@4GN747{<&SS`FU}Nv;JrPBX})W;d(->s>c{csWU1brzRO>i-w!{(dHp*491P}P z!-D-Z5x~tGb!(C2(tB%Ba1H+TXgbq>Mz~lO0p_h+k3+=rZ{T=x+%~XmlzZ#(!X0}X ziJ~KDz$2KjC>AR;QMZ|*cu~jwh(y%K5i9r0Z7Utk%UlkmQeSfdQ_$&GWf+^fZ>Jzh z<12~G0{6FbZ@S3wL|R9>@8r9w^6%uhckH_qdeT_z6b8&&+fgEF8|(V#DIW* zS5XJB2~aDt(0& zz|PJd?HK+)n%nLNxHwxoI5;^wyLotcd3*a1K2AZ89s~pgK6(@!6ciE~8pdoH$7~hP zXqV1_$s%&DrSfa1eArD9_=?W09Q1Gi6f^=1ogohyB7HIm44cz*#A?$ns9WZVU zh+79F?n2@>z==Dcq(zqu*>koVX zBJ{?^I+x>IDpNd~(mbE#`E=&_bd^2quM3&RVuPZhVq#-)xHxQLVp3{qT2^Lqc2-JS zT1IAOR#sL{PEKB4UO`l0LrhU)Nl95!1wOT^C8MS-r=hd(*~?;lPkDJ|O-)^0ZAD#O zePd%2VKF^@`t130JYhZI@y(qbE$!_cot<6X-7g7^Zq3+*n$Cgx7eoJsYef!yZSEU? zHSu<6XlQhFY+`(5Y;1gTa%y^NVrpu-fBM6lH?t#ii!%!!-@SXku&_9{{CR0g zc{y{n*^zRX>EITWB1ecK0zyA+d0_U|FXA7(8>3|932vb@q@3&Uye_|ou2(I zi=SQmt&0D+ytw>Hc)tDo`Sa@c?`uM<_&_MZgNn)iZy=(&S5YuJV^$k$f@lqEc0yXmhN#j5ggQCG5dtjR?&=`qRW8D#_ZJ zs&wbFa`<(^9(+yTwP#2%QV3~@uW|AlC2m~%x%p_C_{L-bVGkysMWpB^yp`@qUXj$~ z|JHUA~X+0jPWgDIyG>3W_4aSU$p8Q+ro)=*+~Pgi|+)&{Q=bGtPx17^KRR9gaU zD#gN&*9)(ddwc(!P^HYueO4Cg*GfB+nG-$nB4+aYakCEN)w3OIYelr*N5TbCj!sZq z6!u+NQRfuEgHEzy20(TbmonbTl zTWNkI>bW+N+wEp{%Hm+2bbXB%?~t651L#a0xwv>6bJeU00~IZSojjZRtS$ZUX*%9~ zua5jVXR@2z#(7@L<2TELQNKt}4glpuVoA;h>?Z<3OBcV|tAU44Ti$dt-v_^=sTe0C z>OqXRFL`JU+;Gx4{?c)Pc3;i6{~HCd{63boWf2>}P-7KZ$2z_CJDYU&lk6LY`o#by5hAC|JM0#+6`ahdyq4vM-bF>hY4f>oBFB z?A2DQ_Q_Ums@J{&$TI@K&R-hx{f$41QVmTs5KWTz)lW}kfbYrUuSJtt+OK zJ~TXZM1sxGgDk2|F=`>4H3-Zg`!BrhwJco*WjK0>i{+b{hE4@dA^i|Dg>Srm(neDO zKQ{~0T7vmh1w-f9ut>;BB8I$@X%syoUT~7+rc=qXFg7CHO`T|*lurK{Ju1I^k`gpk z$^K((RBB{S&40^*=oflSmE|-Ir&GoC{Bo2MBA$py8x&CYjY3(TX5>v(@scc|DV5#= z(j{Zy00fZ{4g@aLsTMRF*D?+CQ%TK=;+KK~IP+8~ql~$Q*Hj?F-hEVbs{{13eGoCk zTJk(DRVnlY#ll0xac$1a5Z+G}xY!5K!4fNkVE_)7QA{7BiD}!$NIx7Cv8{Sl%P)+N zmznj1809uG2Jcw_Z+)v3*bY^V^m6DW@)*lN!73UIkHuJ(Z;_f_|Q< z7TDKJ5mYq*FiML<-5`|pU^AIErX1*xA}Cn!1MDk400py&w_@EADyeiRa&=Bp=F7<( z@3Jk1#@UJwvjYG{n|o3|NQm1=9LF6gB=UI|gp`tt%#02yS$!80gdODgfFV|Pvd0>? z@mGOwxGL9+8v3vxNiWbEf=9kdx3qxl%@|E_a{Uk9Je8ztqMA|z0&_yK=Lr+|I69=f zuHe{aEhQFu=Qohb6hq5C-qoPwlzz|C`h@ssQA1nCYAp8?U(L1PO@6muK){}4qO|C! zY#5R(^f##Ri2r*3?urMt*~7OUa2|E1SvE0N{vCULS)|fPGkLi1Alp3pzBD_tDKFNM zO?r)A&X|QfSVyJ7WO3tmN3b=ku@uu9T0=Z-(o9&MQ0>^~;OVYn-p$@oc&ZPf#B-#^zkiDK z^~8EtEdcIe<3ytm$s7=LFY28~QO$Wemy%}#r*$ArDQ)u5BZf))*d@h zw>H{5xR!C}Up|Zvkq@iH7 z#Mc-JIk*0lUdzFO++GKnj^JTuMB6LAYKTj55z63!7}N1FME-64K}cizhmGJZesu#& zz|ivNTBI|>AJ#9_T%g)Jv)?gFEeH2y;$C)KnnNiZ?5W(CklRgpM2cx10JpIydh*+63>>$p0#~l~ zT!$V7DKkeKr3FuMz)}2u`NQn*nUym42hIgaM9?M?rT*^MtHfpp`rs3=;wceC&Yx=nyXRyB( z|A+p8eu5#aDADlYUg$Pw%=Fv7)uk_$!bd=k<%T(6Kc4vGeZ4;-8zpkx9F_ju*Pj=s zL;PDnHK@`UgFmMaPJT^}_x@aXb3IjQCwK7R_3vGdwI6(Z*Jp2DU)zlT{6jch{{A6k ztNsvRj)m4~UTCs*Gv3W~av{FN!uK@)#G%S1wW*6oTZZIWy6;%xZ(4bsyI6pu%%XkG0?Dl2$Gi?i*}+WAog7Y4b~;G% z--N;{yiVM(-Muo@cr=AHG8I}zK8huJ;A}BKZ|4coJv=9B8@qQZYqwElo$8dji48ue z)>DU4X={h0SeR2a^H6(e!zT|>N3veua44M-Z+%`e-kqe6WlrHA;&;jn zEtwv9*EnUOZGTJY4q>Q_M6>l6oCRXjp|hk%edf_Q=`UbZZ7~$y(vS{^~rMxwvCL>i3V*F!K1~1afVx1UWDJ%sF)ZDCk`$A9GZSSINdwrZJ>zzw^34h=9$%U(h zI%E?|RUdDkwGc5G=aG6|z$%dEiZ;KHwKg%eO1$O|c5nw&<%i4VGd~N=SB^)uxp;O3 z`m5!mEZtH*$>p1Efmaabx&j4kBcMn?DirT+ndRCNZ2lTc1sKVG5No=m?|fsixF?<} zJJ06#IBF7_GP-XN@FLwMyXKQb<>dEfmAZ{Q5ed0=Wh zi6H{AV0eRX^ zPKmWx(@*XWPT`ji7Yt$6*$ueffnFQPJhXCpsF_`|O~+xq`%t;v<%Cl=0>+WUz;P=2 z1S)Em=Hw;BQC&fc=N~$s#YBYL zsW&xFJU0s&_d@I*21$-?&CwJ-5S~X{nqpKqF<64394yC~o6s;bYr+J?Rtn^`Lor!q z&s7c?jPoK*&GkauG``fgIh|#JJ`}iQbzlOX_5@~OjE!YTI-V?}oF;24<2hX#IoT0qBJTBkeU)I+$LRR5SgMaToxYtERby0J zyX~d*F9MujD4Up?MVtCdy>$5yn^$cykEbx(?mqdML~~x;zTX*>SiK9QJQ9U`jV-EX zdc@5~WlK!CaLr>fH>C5Wv*Uhx^6GMh@L_TjB&C}_rA?~yo<)awR?4AU=@&OZ5SiU~ ze%%XP>Mt}|gC|8eQ(DBMr&UXyZ&5b$4^h}el(ReF8iE;bqqvhsJ`c&fgUFnibSPc& z98dHeYo{1r^9H-89b+u!G51+aY>a2E+Gc%MtnyjjW#-q%A%9|^aC=%)`>nbv>$a@L zwBEqU0t}NMt7W!Xyln~Z3(S7D=Kkx<`k0?Qj&#mmbQ5{z8jmw;47KL-%+B)E*NO*% zdt?IpT|6o97|I9qT{rgPQ)|c_x9!d794flPR<#)vem+>OL%B)!7RxEnA9Xg=_x3z2 zsDCkd{dIA0P5@UIV*aym^eTGjsD88}&@IobFV5V9aBG5(3_6+WH=TNLI{jAB{o_)U z$57qLg2GH*4@deAhsm%9Q}mlZZ~INP?7m>xulEeG96ZnOjcr7o-YtC`TKcoTT;tK? zeb=$Hozz0bey#kW5HG--^nKmX%wygM`;yU~H_4QCa>k|_I+uo(CP&R#$TtD*(1bE? z(}^@`(=~ms#e-(Ewg-PM9*BCh!n~*;KQf7PeUR-w(yQbA%av!#mMY6WAXJsca920KYdyD0SC>c`T*un)OO_ZTs)y9bSm!LUdEX)WP93 z*z%#Pm}d2*{>%<$;#ZYlZ&|Zu=Y$n?ot|c$p;-$QTW4NdXY~k|GU2a{Yt#{~FD9$M z_Nm{Arzq5d6#uF(mv6`jYC22w`m27nl}OiGr_XoErBy*8cJZOzBV1r@ZJJkK?x@2_ z1mzXa)At3F=2dU!j46Jpx7*)+nUn4H;rQM&(s$zbAa1pd+>6fzUOp|C39&W~sbwrf zbTv2eX|#4a^L=Rvjeci2_v{tl8w+S+EgkB)S1#3qxfmY=KxK3z>W|9M3hfHq1_G!jk(IF?C-;elY` zHI9`vt@T%xklw|6>x{a6lJ%ry%#Y5`try<+ z(@fc?LX(U4r~m?Kg1ybcC3Fho1O7cI@s#z)g89C+Ie0LxTbA#)z+O)g*7{KlgIxmiCdyvKQ?qvC+cw^j&vz#5DPKU5JC75puV;8?E_}+G zSiA-Uv)ez;`cXF9veOCwnz_wVsiO>)=-u6-H@(4EE%GOyiqZEl(eU2S4{_J{_)oO* zN_8;@?}`uf_*k~Pv>rt$1v)PbCVFg?T<`nO%$Qd#ewpi4#JG9t(Rx*7!psP_n zLp!B(G{zp+SbQ{95-|DcXjVjHxR-X6{mYc{7kPs(^C?G*CA4o}eOa49&)>Qm#Qt@) ziPt2MY56Szo^RbGP%v zHnM|V&-?H;aK`XhL_^`=Td4=YLKFrr)p!te+*Nc;N_ESE#|R!47rb~qz61ys(0ySN zi@W0qn1OL1OYWu9-)rf66ubB`v(B98%Flb$Kgp@V_DjAaBTR1Z8=d6l?Qc}b>-&-x zudF&wMqQ(Cd^_2#9gw`*sD`}Q!BRDv?j z5zyUXb8r3}ouzrzUV14rHzCq+Rr|aI%3k_i0J1PuGnk6%@1yjQgIv8RzneJen~E~R zP{zoe1g4@)_5T3u`v5}0^c=9``dm?ais6>Y)k*q@ zc(7p@o@h6EQ`LdJ7QfQ*1tA&S#OA|OGsjII+@`oOHX_mh^gJ#JFs61R(`1P3Ve2W# zc2s>vDWFy51}J*L(SOvu(q(w+G?9JFu4L73*JY+yDf~KcDbq2{2o)a&r*G&Vt!B6A z|IYYQsCn^vYnlcPvAJJkeaCxn*A$WB@!O%X+Q!Q9bn*A9W#jY=#yhZint4nCeN`q5 zA`7CH9D4&L>JENqGDfK03a6zoj-dF)q?i|KY#xEtRtDi5;RDsuP){c z`@`UbS0Uzz79IF~NrYD=yG4huWYrS5DL`@uET&Ga$uq}8dRtu#H6t$EjAFEl7907c zYuv2C>gr&iapTce^C*LRj~JTO|Ew9!ovNN8j)-wORprWz7S|HUv~Ia8^yjgk)~(L1 zj3L42;MP{*mprXH65p=Gb)@`5-V&)wyGKPx>{ea3&ZNDyme4Ek3oy!Lga)Z($f`^L zQpMniaT5QgD<9^t&}J&OP;=_|TkA&wa|Y=2AtOD#;ElRBjL}^5FxfkT##9vO+47(TrrT~j!&j_|u@gkdu= z3!9dfP)pSBk1g-+f2YG+>m@KD>wIrW=W>HbX1#k>*DRoB{%!I(^-+fPGo`+5mi){< zzne7~c<~#=Y(AI!39{B!(wM|AZHHe?-ELq1v1i@fim&DhtUDzx5uEUBR1EE|7?%A3 zj(je?yVx+o9KWz*}>;<7VMj$WetQW+P@8AZBG7 z5n#1&xLkr39%AZuNV?uzcG{%uRg;y-`SjHY?zsDc*lUEV%a2VdTNVJUQ_Q=>md9cj zrjNrqYlVn57;$PL=h9vQgN$p(!)n(xY01Vq!|d9v8XI0%igx}@CRrrA(O1nxjwCe^ zC?fXTSSMQ;Pt=VIG~#6OUv-bM{NA686J!LUiS*UW7_19H6l{CL{!YML6P{C8^XgtC zozuYYgGYB9r*E@57U&;U!2}~#XA-u5;l7RyN_LiMJCRZ61H(8|1zBv_!!h;9%Gw7pS@9orBR3U}g?rxc9j^?_B6MgP-{itC2o1 zh|&`r#r6R)h7Ze5YXuzNU#N%r9f?6$@oLP*nQ_9Zb#>n66O6|IX6X^2YLMi*5{k`% z)NAx}?)iD~k2M2G(UV6qU91ty@8I=3 zjmvKt!x?B@q!rI(-vWAPSt>)Rizo{Qp=9|*{-hmr+Rbr(Dn~9%a;HR^DvqU@-{bzm z1&gNBoEZB4{;c-Z{dFkI8p-Aw@oi4YdmiHTRtsn9JQx7Yt?ss1JLAtPBm9sE;fF;a z0Y3T-7-f*?2U`}=tQ*N8=wPPA#@E=|y@J@=X~S7uvZ8Umm%!xK==bHeVsX|}>vw9# z9hfi5-t#_pyf#&Lam4r*)qTzh%r>e(akJf@yp36+PTOdy9`2!(4ckx8W`B4qI=bF| zw!yT+4@^vRa&Q>J@{;_;exNG-U4!%e=_=A8ff=<=(mA7PPwPgD7UB=7&^skWxOFNv zj4SmGq=`V99=jgwPz;!p6k-TwxZq#@!KN+sm}gV?Mg*BcRUc)yTQlj(W_g# z`1#8Zal`ZG>-yzR3&0!7bglqmI+0@6;n?^58_pWdu05n-V<}H}9Ie}3ne~h2qC3xd zaU!7!`1t;AGI}7yl)8l0hGTMW06608k%R-J`gje+@D>Mb zNBZxdI?Jo4#!4pe-0+y>Z<~mUO>ccGx1;~^mn=TVr9r9P!}Q1Uf}(2sGohhFJI0ko zTN>%XlviFZqx_5GBZjTjN?(lBtO7YLDB9a!eVI^PSuW+Ifo=Ja8mwYhb~PT|?fG+G z?E0blY7HC&aKexp1l4ity_$NeQA9KT<%9T|6^YO>FeQNF@y8=dxaJn%n-35Pt4UMq z7{9ynYp<}m{q89m!$MPh-}O=ME8{-m8EX19AQ`0ete^T>CV6q>`nn~Ot-${4^4FK* z45Qzu45vP)d0F#V!J#V26Kr6*OkF< zxZ{Wd7YCVHh9a4C8hSZ{b0Wq1qYUaNFSU}VqMAO%W9qy4&MeUW;_7s^+gA5a)j`5jPAf z+f(RH6-^>`N(dLMjKU#}PvuW(slO}SVH@x9;Y_%wd=dYh!?&Uoo9Rn>cR!8dwF>Db%uv6- z0#5paRyjL^n6FP}IIk&cukrQ7Ye%NI;f&{vaI)HCL0KN8I5?iON^F!%xH?F9QXbdq zraD|F*y`74!XjYQpgQ?-fMqRZ^fG@d2WuKo-+;Kp2 z#H8IOGA}e{de$H=QnJ#ea~8watd@|-x9&#KBAV#1taeioM+b-&!B?&>-xBl@!Y;*r zs~o)$TjTQ-`Gj76XeO2-( zJR&ifU(B4UkcFx#YuQ}Hani7d!zzVf(JJU3TF+EEqLFLZ*ZifVPw9kE)8|TS}u09!qp;$0?#62FFsWXp?m`QqIQGd1%vg)YEy!vx;a~ zV6cAxEhqpB5atIy0%!r#0ks5nfWXcWsF{C}8DSA|;oFh~a7IW>R!Ce{P+UP!LXkkw z2uS`9f(A-pXJpKvGUiZOi+>>+0yV>{pu?-E`!_oygZdYr`42nuH!`DsAEF%u(GG#! z3+2%EWY_g!*YhLbGdBzZ*^ENij6?tCXjCn@)vdVHZFp48xim1GnvR@zT?kmse+O#* zd(PLtKn($+F*moeva+$UvHL$cn*Vq@-Q7JsJnnlD=otb&Sjeyla5(s3?76GmSC+z@|_CU!8L@9>^$_9{r0?arCWS#@EegLvB|0QnzrD^^J zX9&p5|2r=eNr*6uj*g9ujf;zmkB?7GNJvUaNhN?Y{}Y+XCr~p51x3ZhB?M}wtgO7E zqN=8*_HRE!Dt5?0fy{`%E%>Rlf8y+4R{regtP&4D>6B84Y1a^jSO;69v%)BA6Gjnru1aO9M zefTgxKfgdAXciZjmY0_a1PuY7`NwR!w!Xgp-#w=P*h~L*m;Pfd{o7aikE!&(9Hj&^ zDZxuh07HNOH*f|>!iN3_AcSa*y&W^G6sDp1Z+6N58=O(cIUDAay7J^R{XYQV_cE|- ziUvTCI$meG^k2K=UJNoPgZJFxUx1LZ8eW?YtN#Ze%y!7pauE*r8z8hOply?Eql+I} z`u_DRT-;VP!I0>Pjf`0W2CUfMF~+w&TGF=@I|E<*UwZL(4;@;x?>{@{!V zt!#Rccc!o{5nCi9_{u9W^+48o5~!DXt7pEqWbrm|8G# zE!fyRMCv))OmkCVw&g~-W3tNo8WlK`fFGE=3(`C(2;6fsqPX+x5(ER9^=(q!c@F@h z!LPU8J(a4Hi%3Dzg9gBgqqxmfCA_Q{)esmqHzQ4Ur#1D?X%kmkFQ_>9xEOm3pB}#k z5aFag1u8TnPfo3$8kYO@jqqgR`}?Q27uBdFekW}XPF$Y$B`IH@aer;t+cTo1ev2O) zQdl^~Cnya8{Irxhz=vvbJLEu^BFLucjTAD=Ul%;rB>}z$*1g{(MfAVVB!w?FJ$^V@ zNouUE)WEbQ(vvo(0DqOtB}&Z65`-UPftpw9-TprIPFqR;6WL9~xF~ysSX^xarJ^nz zLAKvHVU0E6Le2B-*gHFg80&xAT4BCVcsO)Lh53J6hX;#1mgIHD{r`jv|z->t)yG?kvnthpz}?8TBo&ZXa~iU_aHN58FU&x zeCt7TUEar*wdtN=N+Yl5H{=yQzrk^*E$D!ZwI;A^&)i9ZpZFzJsqJ*q1jGc9`!F5q zZ+Fd)&jfaVFFsDHRouP%CS>`0Ny~M}I(mj@GI?z(9I|v%gC;)RWNVX^k-DzoOn@eA zXVU5-57iBWk6?L)C~lz_uU6qPSz)`Eq!J zy?w1;?8tx=gm7dc&Air}Y>`hSKq6_AmXUIc>(ZV>n^D+0VtOBQgNP#O_QqQf%8x9a zbMpfvCqWDOU(-pAR=Jt5v+ttl(t%;_$)3Uu^sK-WBl}kjFyl1hp(ZR>Vw#d=<=b--(wsd?iWCSTxkT|AOR4y!FUAVymQi#(USu>u(%(yMt8eu z7z>-jLAjK!fCC|mR~gFNotoD4(yrt~DBI_Gn2^Q6{2!hJ9>p)-G5U4Y=*3p!J{M@e zHx8)%YDssE$jShj<|LYK-g-b=eRL~8QQ9}vDqZk)j)b#da0G}XHrbdI<{L!#jj$k6 zsjVC@u^Yv-o#%UDE(v~=)--Un{XpsBYTJO++y?6-^b{8)LHCkmW6wNMA+d5iK+NfQjq0RKBcG&#EOZq#=W3}z6_8FvSLv?0ho5bMA<09F zxIpVzVtSZNDmu^-G29EGiWKw!9i2?oy4*yFngFa`$ko<;DkWo^i`~bVEKNO=D*1d~ ziJ*2$i($)z_xiRx;c1$>N<@mWNzcF(H?b$kxK@aR&DOmY$8VOZvffKN{(}_JLbhGV z>-W(HWe?X@nboBbiq7%J?KDy%-%W*H=|BA<`x|TW;Yab%kBLdBf8N1R?QERxSq1B# zJ+xl;+guT2B(f}C=K=Xs)iq+n&<$d_BzcFLuW<%!oB(;IVLsKghl!ED5JQpUcRaft zli>k%t`zP;Yq|`U5C0qjnZ~F!)`?+Y`nm8s3rl1f(GL}~!#^hMsVl4oMr5(w)lx-w zC(06>(a5hB!U8Q@=tobn-PW`e2TKZ{=`GU-4k5lsj-nHy)HK`b$q;v)@!`v8(Oj;* z7MNCecQB`EiO|8slf7F9jG$01;?m1#N&q$X)wlHnAcb(Bv^75JMv*;vI3t4R+NCTZ zW(ofe+GsxRF>lE!8|U$nko9@(i!E|mB^h^=Vd)|uKj~~njB=4iLeHGQsSw64&j9I@Hqk^nv!+ zG<()qQ_zV$7>R+7gsq%BJ}+|+-f;*l;}qTD6lYM7VtCNO6{8SqD$wC@YsZ)?(hX&* zw4fFW&kJw6dGy6}m!-5=5Dl~DslBw&%QbI~sFO4t4Y zSNbzvhrQU}dyjE}1S@_b`+0C5I=CJe!NgAohLid+0mkZ*Qr;y=1*i?H3Msr-o0h=+ zfVthHdz_zYaZHP3OrXTHjAh@sLcxiaTh@0exWHYc>SFGF1 zoY;ZA)MJfUm7l4sQbs8Ez@fM-KyYGzXT&WPp|WEV?!C0}y|gMgi28-LH&_#OS4a`5 z85pfOL<8K#QhG~>zB9eQkaK?}mg3_@6cM%eW{0C-zDVkz_t$w3eK(=+rVmLjuE%_G#}GiWbk)#UOqU9rq)kqAxNI(u+cTGAK{RJukI#`xs6 zrjU}E#LVKY?{Kp0huYW2B5kK?YmBx`#4wgo-y(&!vR18fzA_CG4 z5IRU`N|#<$Ksus?UTmQ%A{vScR*D4_mzIfmhY8?d!KDD6z9;}Mm0KvB_)<}5!sZ1P!;ys564z)a9Upz{@n z!m*l%u{Qyjnm8;b=K@A!62%y7V zrcJrRpD@m0iUWih)i#9Jf;mN~&25I%*e(UZ!Ex zLEwo}jAgubhLOcSS7JMt9n{v2dodj?g@96U?bcBjo=Si0IH7#7Mi~TCb5)(iqF*z> zlLXW!O9-c>-wPMg!6{UC3+7mxeR}Y5c6$#p?h+7)e8)h43qXEbuQNJ}hi#o%qoRJ$ zFmx>2PjJ)SQcRpNc5TG&S%E5SGpaH9_nPyfib2b!Fq4Dq* zK3)+MU4?fqogN>*x&MOlGln`37h9m-!H_NRIsns4!#ty+xR>G6Sd*t@*~{=cjY9u} zTPG>Yaal5m`drMEu|m}@0Y+xskced|XBJ@b;^vcs%AaXYV`Ie07WJoK>s=H-t|FA^sSt7#=v%%*f zU+|cAM%8e~;^4F{|VYs!`uN6vf;12<>DB;VG`$fUTv|r}lsk^k9TX(UTO> z$EMwu7`+e#YtA~?3162$d?CYMdnhQXwO#43NjFxYGHa{qO%Q$7DYgL^WRUVSoB&u0;V?-}SuRg|+Y%DfFD4r6v&uk)SZf|! zNu=R076ESG?XVW4>(IAVY|_{(FgQeiSgYPd*Z>*M$7Oj$DGd z^J!rPdb)?Cj_8^Xw&Ac=mgrq7W`uN`N!+NBO5@M~RH@Ii9lw1+0f~8Pl=T_!eEHcK zycVm>#=1_7-o&3emxU9?!!i!vNsjYMZujEhL~T)x!eLSbG8*y~`PnSPtATYC&)_%( zjihKa(UIG9tN|4?68hf3>1tBuJTqg81`4m+=-R-O2cxU@Ju|5t?eEV)W3Dt z{BXmw@z0uA!aCv`)vH4n!0+83Z%$1_UO zR6j3dr3rH`cBnGj_ueeimu!hkIVXu=gOOXm7n}-yUDj?f%M!|63Thm)Zn0baee~YV%?-(}- z5tfU=LwdpMKnlTPND@09RLG>C=k2RBUq2?{0U=V+yes2<2PVvlkTMnK8X(vOKX~QY z!Iwd|7D*T>KaaZIq6xYCGqB>|<3%Pa!?O&T8bPy%MH+Sh3&8Q_9V`kyRk#4$N13`| zDPAb1aFlXmI%455C-%ZO8j^gS+PVqgNEU2sy2rSJd63PE{)BQm41SA4Cv40QJn$Vz z=S36<^2eJYIH_#|xx6L-%nC`&b$K{md0}QD?s0l)Ni!IH;=l&B+Mcs!p&SD}A@C|n z1yuQ5zZvu;>cw{Pi*I*c>@K`u{&)cpWdL;;U?&E9Gy_(`fZt^xpEJIqU;ykQm(C)O z(;{E=qCm-_(A`Cm=Zm7uMV#o8xXzNK(~@-blI+pNKj#oxHCX^aF%WnTkO8m-)F>(8 z)zl8M%86Q9M|5>rLrw#IeM5bHBO@bIGqa=S=0`0ojuZBLvy;lP@BIy#>E zGxTH?21z6r)}WKs5Oj5QXO#rcp7r$b@M29oy}W!`g+U*mzviCj&Ycek2n-Ai`ZN6u z2?_l@0R2@V3=4~hh=^hp38SJeu}XiJF2%&e#Ky$L{pZDlq@FF7N z28da#%H6NdT`sG3mz#U_ck}Mo7%@M;kTppxV$BkZONxuH{h240u_lV;l@%3Lm6cUh zRn`B`!Q!o3EiEmrtnp%N>%Yc}U0vO*ULI@0$Qm!QW{Z7&{r&v|0|Tr!-q7#a;$MTs zUo%D4Q1S1nBAxzgusAt6^?#o#vH-jP;@AD}@w)$Cu4DaL|9|*m6}b^Cw(h?cxjBJ( z|FCs`&0(Dsaku`kbw$R3zl+>~zH-0VIw`LuR*_o%O{i7 zOL3m^f#hrvx#BzyP8)?2X~&LJt%$keIpU&2Sua_*j$xySAgOMH^Mig@&?OgNnt`!H z%5iGsE}J3nWQUA!Xjf*avRRtiU4fF&KC=vE@0(fxvMv&d_rC7?s?;F8K(6ptM_Z79 zQuP_`$qOAwMX7ifi4bm8U;HSa0s$G9x^oOc&xDJ#Cy750QstL5Y$|^h_DTM31Y+dL zvv2zE=2DsLwUd=g5LH)G{cRPBYJrL(KCP=PZwv@O%^EUai;nuv?bd{+xeEaT^n_d@ zNkBo_$c{Y#z0e5&Kr)S}Yhdg~q#631moW_DR)gG%!Q?p}i~jOEw0LKWKr>?w|<7+8gXin_ys1_!>{3NCcXg%l#yxY!`NueDK(o@AVNS`L+b;P|M=7rijBaw!iv87z5Rd$$l5qmuc`5R>+{thP57WTz z(`$oRYaR#W30qSu)ESMYs>Bo^o&a|Pm#tNa9O|w!SRVRT=gZb)nSiX6H|-NJ5h0)9 z{!Ua-YE#LX=m*@I^y1_?qi;C@a-u8_82BaEaaJTd>G$yu1M$z3csX$fLeq9Dt9K>k zg9Rb@{x8qaE{6|W!OqlX4s%JCJT4SOzP%mSPZ#v`H{i=@XTPdfv>0PBDmUUB;=l1q zI4I?_dh<<*B&*^8CeS?d9)Fzug2yl`GG?d|{y8u8e zE*{oF100TY;i1qc^)Ih;aE9+kQ%3bPxW|fFTh9Qs$a-DJN5WcaXJ>M#KOa z_A${nU{H2n3}4DFKFR1wti;lKvgJxl;^#tf_;CT^ zrUT_tYc~apEM+$eAV*Jw2z(-(u~e}%ai}@&hfW^^ltkn{LZykl!^Q9uT#(xLCBVg! zMu(rChvWk&;DV-PV+IbKRvRa}vudOe{xLBvGzJWChN&NQ%Q^`d5=PR&M?0&cBi>c; zF`pQg(v&HcSPB5xmyk>*adIe<`0$HYQ^D3TajcJcl9}-mgnkS6Q3(S$tk0Q`8!)2P z50}pb0|+=y^k9W(esZRQ`VxXWELPH##A&Z14gxSO31W=WhwkcI={&7TT5DYv#7HaF z&lpnL@&u>^EOA*ijjGOYkoWpKBVfGF;-|(eprW=EcyTSRy_E3gz^5|K15)gY-MXgN zOH0;vt3YorS|nXjll3RM%;J9AJ8&d@)YS{R3&D6>(rEsZy=@dL6g|>)IkkjKu zuE&{SiKJXYnze8R|1j!R$3)!?!x0xtGlHpme74vA8-x^k&wmPXMG@~ zR?sM1;`uTbp|P;W^!L7EIO}oiV;z-@8|T>QGgo4RJswH{J>MpteqYTSR-||CaY~I- zc2mb4@v!}oMDZ6p@(=Ht*X@tK)_!LCGr!!oA0LguzBvHyJaKlNCMTYE+2FlT`oIZI zWU1XV@UM^izYpP~CHfc95!(Ye^acS4{2iJ@0I>i?8Mae^r)&*>hvxqYPW*|^f0+}i zT5^B06Rc<5zsnP>_Z%@#j(bCq`#ID*qLuoBI8+ zC|Iw{KlsG&4Bhvy4E>zPpSL6{MrS=FSx?A6@5g_VbU_D-;K{!-bRPG@e^1ZRfo-VZ z9-hAv^FEeN!3xcP0Tp2bthoFipyD5$;-B37|LRi2&;4>KVkZC1rC9uxo~ONH<>tRb z^S=}GE8kc^1&gL&K@|VX(43{#kjek#<}7z8g%z4-WF)1hvx4*go}2%E>X!V`CbDbV zSk43snP8zee=_ucp1Q2`{Esuy(sJ_;HSzm-`-__Rlbhe^>0;&PtkC?AHu1-qV7+bs z#O1$(^09wMCMG5x{)x+HW+qvI`QO^a>>qIAKedTJxjBp9Ve!m=vlDCofD2Gl2 z^M8U9tj8L}A`e$fB1Z2b<^Km``0vnM#AK2|i-(#0ySk&gViGUs^N+gou*VBuL;p+N z@iHujav%On-O<$LbyNGJ?kse*vDBMi>dyRn%I$MMhST4kxUV?>OWiRDHz?6^`O$e= z)#GC0FLfvN-96vO+IDdWh2QEWIfh(1yh)*-GaS@z$-Wd*V=}cW&^n#d zuJ=%DZZ@DQxf70(H-c*Q z91w?f_q?}vHehhR>)0{DZhH2}q4{m%qZ@12T{#*b#RD_~a0?B@SaFIM?zx zfH38oU#F-f*I6R};KnG&yES0T{VgF%+`exs{TCj=yAwC$%!GyGnaVeCQ%Jm#NE1XQ zMnHmvHCl+7t3KG~+I}AOGfJOWD+@v`_Ex(f<)dFL4JgtnqTlu#2weEYMugd<5{=du zT6Iy!ZX2l5Ww{|uCV;6lT>IYj)YY=RN=eOZj~;%1FPwc<4ho{;Ue-8XNWb-1I_@Nv zJy0tvmPoS9VrPF_7iDx`RUybkJMfWq(Ie3#>t;%WMrBA92T5mh#aM-GbABsaw&_|M zHkLb~FBLkP5&dQ01*4k~bJt`MQ0K|6QdpiBQaE#a$NC;vSshVJ;Dl^sL;A6@ zrur_BC5ab3yJU>vc^XMzBekJcSMR{dh z7rUgvF8PW`2zNwEU48QkR4%8NKx$-TE9pue5#<`UU#-sI^G49AZvTR2ecAt=BMy(c2vBMT| z2DTKdzRN3|+f)_&@D}bRkN*A-wbax520f>k{e^Pmjee5g04!Cjy3bpn31nG%rZMT2 zRE!_7mzL%C$o;@hD z`XI3Fq=D2WxT1nU+<7)08N1xF8ITr<*Y)(`z8c>6A>uMWdvd%=Eu(q1P26SBHEDow zg8I~;EU3)K&>j&B=_Trgnw+k0CdeFif7YaBmG0YFozg!!){py`lhmi8pp-n*PZhN( zfM##nj_Kxle#@V2#M)sW>bkYWO*4(1{z3wYjCqbuSSicVCH#hRD3m=1W0NBdyLF~=c(_pW!*5ef)E*ecx z0P7^4YqD%%_fB5hnoYXh3j{|r@Q|g`fLKlvAN-6MMwuxOPV*F$nzh8TH#wu>gnhvf zBFgy3vgjRqH>W6aTI4B>f!=QhN1kl-%Z@JZG1egCnPNyHOi#4KXI6e?BO^K}&g;`q zQj|931&Hd>dpwY~w@4Yx7U9dcg~#h-%@bj7^SYs>>}koQbe4`0H=RpU`(TCOD2)|T zW{0Y;OgvM%A9tS%V8qmM++7m*Iga>Rn}D%9kMT9ejquNHWQXr2?}=RE)}m)#+QB)= zM#KzL-;5}t16GBO#lcRkH^uX6Ye_3GPWT9jOO3B92&lz!rIY*EI&qkQU00!xqtL#i zVld-Ajd8rcTh^jtnv(mX=qO=GJ8fb?aTLV&jxb#NvJB-G+9&)fFCkSz+I>Z8S=7OE ztM>JIH4Z|>-k;PFdG&at%?k-W>1snFbsg>NYx8LJyrmNem-IDc>5&wSP?jtMzCe}W zXd6u&C|^e(nwkr^Ww)uP=y#n@zZ}UPpeK2j79T-%75r(LaH!VmoKMcA{e#OujZra* z+STQUw7d~MW^5*AKVgUe9YI(1z8KgVG$1@m7#=(>@^;aLCh`<8tGKzibmBtHQ~44j z`6Nq}K%vIo865DCj!wFL?~ppwu>ib za3dnwS}#mh%g2Tt<0Nzf0D^>aBemI;dC!4@~0)bjfPe>boTbR z)LV}Fh_~q(xL8^P|B6Sf*oDKz$WuV%CIhibb}d&6Ua|`A1;RI~B)DoLDsgAv8$nG? zct<>Dis~`~^ccml?cEMJDh=bnL(_3Ub~aEpiT!H3{Ve<|HjarOcK%9IhBCt6 zd{xjTpkKUlCsm_%PGEZjFtv7+&5`iImJ1IUpr!x}ouT=a4v)cMa@bK@P4J7ejvqSq zzdwi>qn&JhEx9)93Z;Q7X%V+i{K|M|4Ne=8(AigFV)3rjC6``tw()?N7BWe%mHo90 zDA5vI6asKr_sRiqs5Hgm)z4?zVRx`8JZGIZ0~{ckXCplnO~|Mx3C_pym^M3XKmxk) zi^67&%e_25D;!(uYft?EfIi84hnA9R2%aMQF+Jl&RZ%-6^A3KNpp?81pi1#wUl7-QxV)@%+&dptnW*p2LYjy%FdJ^3>6rl)P?5)AitJgR_-Dz?!@OUg$l9TPV+(*Xw;|DIfxdQ z7pxWT1Bf|(WQ|nzYP??PY`k~ana9&t9Q899WhVoZ_XpMO2?@KJlXZ2@Idf(vBvMc` zI(ZNIkk$)cr@II9zF6hWu;sm-ar(%6CU(w2v<1FHM-hl>#iQKpQ>dLLxS}&Y^(y|x zV&1fK=C>KA<4b1%XWhK)NpLa0tNHne7-iYlXD6SiAf;g{B$yk^HM{~w-Rio5Bv7b3IWTIPy4i3K~J*ehqT z`ryP6u<~%O(dHy&6r@V?7 zobpU=_e`I~WbkN$l~De(ymC0%;pd#AfoK znE((xAa*Yf`GbsnM|Pf`4Z`Sa@!FvXmO%(mZ1pQNTLR<~NfN_KhTqHhzdu+r*mCiXkk3+3^_w8pxmpjpWwcY1`$691uY3)e z2OH`v8)_yZ}} zFral(26nKcF|4fS;rUX|(TKw;=Z1li{a3H=dPa`&2c*g?L-7|1#Sv=K7=^dV!N#!z z%fMK2oou}fO8D|Lw)sHf`B#=^D{~0buC%Fy3f0Gmv4RH0ua~kz)E-}r8U?wB>*!umw7WAGKy}8&6aNp*)~webGVbRNHQ#_3Ghh1WAZCvhhQ7>y|d#i zs^M|0AXB(~X&A9XW+5n$SbV&_19rb>trU-!(wEK=$Bg3DCb)9EKz3Jee@lRDZ|c{~ zsnWQufL0;>?&|NYub-0FycJyp&UG8LMFu2@RwVeYCIon514pm*p2T*2H_)L~IpB?U zuytM$iOTCR-+;ukI1ZPl9xL4C>EUypcGs*zIp4HSyKpL|j z<~|gfcCk9`CoRqVds=L%=dLHV0#QyC_n$h1xkXacPxMPmERQ9m52&(Jaj@r4BG1zG z)389r&elYqB8A;fH+jtHv7~7D zTCV!a;K}k1d!dx$iiX2qh;0yH{cx9#JhwRf&rgrQLoOE8MHjM zDs0FFMSU82hIXyd73;J6sB-=xI_CCVQIX?X?U$YjTGpdpmu!u&Xv<+{wvGdeU_yWP z4!ns)D^PAA@7UNn+?GD*%td$piN_plD^oNCuZKnk2y`=LFD+niUmS+Kvvf5vI=yMs znbJNb`f6&*4kgOE&WgtzeRD@Q-S3sf zVng<+wOsnz?#90zK%|v%~!phu%$IO!o2Zp3yW;QVMdOp7AxIX zO?-Cp*0UCB^9o;B*1abQDEI3gE`SllRzRP)pP8yWWAA(J zK(2p9=;Ujkhd9p(u-i@+-<{%+lRax*vL_6Pmg^-9yt4FBP=v8RM}&QTdSJEf<3d@f z$9!?#;&#!CZzze~VvS%S{H8|>yyXexLo~v>c0n#dP z>Hn%XJ*QVC&FW~L&C%1cg`V%J)S)>HgX;2p7t_b+u|J$RSm8eR3hd()mk^0*wVW@! zec=ZdF>=Z^|LCIb<;W7zp`h*8hL0DOZrjfbBgCRtfj`zRhU7hmIBk7dor~!nXUlI` zb6S@=lLqEukIOGe=AH5{G|N22Yu7z;t89<9@OX(xJ%^j-QUTj+PV=EsPL_p}tlQy|w? zF)+2hN2qgN;RoVy*;c(<@o$gTTkO&{)9o%nv(Ra32k@wyUteYt6U zi{%$9!?K3)l|A+N>iPbklWA9t62&eOFEW3=Z2S_`h;{!-Mwyb4;;(O*0B*)NzG*Dl zMn2kZe)d(~Htr_8!NU}#6?#@|1agb!o-%6XwXriG*7okr&EuL*>p~%$_z;cDApqi0 zYN^9kCsIV#+$jeH#j2|rp~UJVW(34}Dyd}yF=7G}R`_)?!qgpnz?J~zj05eL?ZT=z zrX2D+^@<}!{Nsy<(Jg38RhdOXUtFxX`d}zd+^`(AXSU$A0Z_wNylaK~70-;;wW<3Y zJA$^<2M#B#4yZ{#8k;Hp$(NvQ!c(SySti%|g3w0pA*D^qoFD9y%x(G2EY6u~zfzvC zKD-6fX#n$U?2Li6m}4Lim@)7=n0!pnZZDz#@~JGx z9q&EZ*^rv9m_SbN`-+>gMh=Hxb{%H6-!!6%pN7dVPfJLBc;>t>%~<{Q8CqkH=>dl^7&R7xBE?4E% zh^H#AWr}vi$3+&IVi{_jMn#HUG3pY^2D~TSI8~;P+B~SUJQZir{@D9=I_v`Y9aM+| zykk%zZ?3jaY)$m! zlkY4c1HEyLX>6}YVwm@szPe}VCQF1w*_5i>u!sG%v`y@%m!c3sHT+lwS!|?H@k4V$ z5|b>HAsfKsqin-z!2>q1I088^J>sOMG@L-#siZu1IhmWoMUQ+bYM(XbQsPB5ewZ3T z%~Y+BP&T!e+?$jC+9YCK*W zW^;^cjYfTYDe9!beZX~01lu0Se`i-g+=aT;+^lw5?U)`Mnt=bwQFf2 zS%my(@EV6kA(SbOzZMgcCEHp2DsxCqyq+_F@xGJW4V8!%0C5pC2&C26W@R+{(Odaf- zKvniJLob&DAfK+9+!)a>3&D#0sv1W1Q~>AD7+_ z*biU8Qcp7N*l3@-_DiIt#_wivmquzxU!uE`*pIwCf(mI;1%4vI3^a7P0;v=k9x%+R zQ7;JDF@!#$h{6J4UvMYv+$-DEut5Hm`;W;+9Q=iF^nLME2 zQi!$$5~x5UlSmi7HkgWxKWDI&FO2s*KR`51{CE+Nu?ucai~i(t$$L79KkA9Wp>Sjx zSzy^0NT*l<#DVYPmK)jo4G)g*mt*7&d%FRWKjgUK_LByK85>!o zz|$&6mwiRnp&m#!s`PlJyGE5LMKKU1AU9xk7&Ky59w=6PUF>VX++lpQ3#&N*#yBwt zjC4*lTfAx-R{Lf}ObjK;{K#YHT$M?iTr|MikOegavbf#Ipjex{k$md<<$BM{7Z9J0 z=M3w1l^hy`j+>7j)1j1tI&X=k`mQ_%vjNqOb-8>2eaA-6XN0L$>}6p4O={&j6ShkQ zt8TyCcY5^BTN5=`5|2IrY-KoKm7?C>m40ZGh?X*U6|)L8IuO~E5+R_6J!x{qM!dD~ zE<;LYr_)C$dEzPu;w>9_(}HN#f%{gu%=v!|W0*xK6i@arN;3VXTC_%#gJE@%uw z_31Em99d`wFs!x0IU5J4xdO#qkvHVGO1()f_iK}=b9r2l#ty(ne`w8}@eLEOEV`#7#Shx7<{g)?EDw%Bo!PqcQ@8K$5IJgwIHc)BC+ z-HFL@yUt0a=3C_bQO+z*E*YbqB?I%k@5+0}v?dvQf*@{RF#uYd?Jz}f)Fls^`ZJQQ z2xJ}zjq!JJFZR=;IisbXeB5JIdmiQ`@|rhh10ba-xYxS<)^RR=IW?bqXXKMx`*<&V z3XhL(HZ2NQ^<8M@?;F*PRaNW$;V1K6V`zF>y(nCT%X^NK!uAZ0jfy6rvly4BZ6mpt zohI}BmDx2J<{qer`LmL@w%E>|xIO<8o!Y_v1?{)z9d#@>0=h1`9c{tVCIX&3V5R}~ ztP(s=%+GbVwR?u=`T4(CpIa!o-`=O~&lg?ztRm}e*UV{uUuL7`v$c?mJujd6hgEzx zXtv3^IbnY;`aDXwFB_?k%7~ZRhL^yn9tb@_2t-fpE_njG zMo)UdRML8arA~a6u@WU)UE#9ezSx!H?DQ;Nl1*f*DOPO%6CLgwY(C(3JJsg;T#AAV zFFNM@?nLa|BUSh0;et(%c7M(Yb1Nu(&Z7^85Osz>y9BHVJmitS%Imn|D#S&2s0+yi z`@%d$n?jSq*!#am@MN{w`xx^)Au4u+66>ZqsfWI}15|>T53p(hFqK?~J~hr8V#k&r zKJEf+UrN&P+q7hV>3f7v@7lv6xZ3w&=EAu&@0<#nZLYHF{)}jV9>>b|xW$ET8;(&{ z-EN&t<%2VvC^;aYU%STe$i%qA3?!^@iFcyKGYF-}qQbqCJ~uZ;eK=Ec|KJVC?e)Oi zh{`3NucIl-Nnim>fVw9IW0{gEUzgO4NWRBw^|S-+{nQ) z-Q@L~94)Eix0HyZig&&fSS}7{+Ci>Uyuv&CLC*NtP?1$fRNhX0Zxa^jP!rafXnp|} zPX)fe*9vUj2UpfTq9}aZv;XBi;qJU!No4Jhcz8E}{{SZis-HrL6t3D#w&0<7(UNC6 z_m!-79b^^SM7n-_Q_>vd)S}!>qjJ;=Cz%mb5<+u~V^SON35J|KL8idK&Azbh`0VXI zcP%WXlcywG|Gv+lsTRlJoW9ax)*^f$(J~v<#Ff2nV4Nv!_Kx2sjRUBc^fb_5QSY`r zPp8d>?4CH(iLUe_WA?o;a2R1of*YU8ROU@MFOVdt6~eZM5sGl_gxco8!|1?IW8EIR zd&r77>B?a(HKS{u1h29y6K@P(!kR#yS3EW{B0cjGy9_@#!fiUCzGOA&3o5ccBS0V7 z(4K=?%K1Wbs*;W`2n&5l#^ z2j=^VqYB4*gN*F;J5RkW`M9V|)4y*bq!XRpzEjpdNHM<|G15or7;U|ue$u?H;r_i9 z^P!^qaoimbc`P)pn~e8#Oz2xM$9T-?X(K86<#q$Yl@3_&PFMtJ9!i{&(Algow~~+{C6bc5+Vi>!<_}N0^%tu? zwELt{is+gY?|!NH@V-djELdulN6K2IiqA?y!>l!d4r>@Vd?P}1Y%ob0tVvnz{G`u~ z*FScD*PMJ(lf+f6Efn7&Y&vsN`kmW=2i$b9K{}CET}&q!??QWwd#0SVM&h(GsXccp zipEu`CapIg6ed3W-a{9@Gg(2IGP~oJLZ5w0f9y&z%+h|^qP>r%y`W&&uWb5lPK0sK z;&ai!?ETvGr2`q(I!TsHLUsx;$6D@6pj@6~dcJ9Ffe`H5^(*ogy;^HmzBD9!YDR2n z0Cw)(eP3uO^9l8%u!#A+7a&3dYSF-wa`F%ps3He!_wM>~Ed06+a+$~`#}QzQnogdw*3cB z-{s|@$VwVCT-VJdaa^sHzssey>`B`nRB7^ASngA@%Cp-Mgn9JpSfWy?w9kZvq6d8lWlb+(dmveQPZa<*-4_4&r--e(#29QfY#DPE{2?^UNNY zWf07&HQ({TP&Cul;8woyhMTR_>CV}eMz7rIMcr(+iZLm#EJb5{@@>I@^jO!y1h=5! zv*tF)l;I_-$KGxyqS9f0rvUy%kIx~EZf_3-`kn~tDGZKGJYas`@MXJjp%G&V6rum5 zyEgY^nXH(EN?$|jcYLB&^CQZs(S*oR)pX;|ywTJ;W8U}Ujtiq+MDDQngH#a{i0oK4 zk5Jv|u`7|R-H}^)y!P2X4>a$Ypx;0Pzrdu?ko6cHR*WEA}N zv=ea~pjV5=t|TFc>@2M$bj48z-Xug4WV{CPvt2W87hoFiUKS!p%$e!$tPCSq-B4 zlf@@%1-k{g)3zZF1F*M)N7d>W2fY`1(nd@fU`;FoFv@1Z03TgP9#FA^mRxh^q|e|G z90JUQAHhb^JH}?YI9fnay=>CK$8XOu>7Lb7>=Dd=wcR`7vxs zOw?t;gk$J)2JXh4s5$Hs7sbZD=n^i`B`4887WY687XV0%-cIe(ny?pm_5sh)j)PUg zthx(%A1rHyt!oW7Yxrw7oNzTDHt`AspDHQqx@d}PVM`3mi}ea9QlbLl)%I{yvHJpg zqN(&bgjR#rUSHN+9?_$puMi<|ZH<$^epcc`CGb73y0$b0Uvjq4w6vf~?AdTc1^@KJ^_rB*j{@g04Q{+df>fu$q1* z&julhDL^b%n>KsX+0Ou=XYff%P3$%TS0dK6yvfoyOIwXp+WOmcM`K{%D!#tU2DA%s zUWK}m62}qQIgWK#HuD$gcel@DFVl0wIw3TY%{Q|oWhcDOyX(lfH81K~{HW*i(L{GG zQ6H+_1)b%EG^0^Z4-IwkP&C`mJl(Kd&D)-RN}e#MyhJ5(%doC-!J)Kx9wd#L8m~^3 z`%WYUBpp9a>=Q?KY*c_ln8)h|5{%_&*OwK|h*ThW9O`D7KIoZr;sLCIX?^|qzI0^j zlzP(G!;~Vz&~j#my7~HeWh2yZ}MA+n8*iIr}gwA zCOq`VWm!PBL+31#h$haxPcR0?#j;ls9@n>L z(0Yy=VHWKKZq8pr5OXv0nVHW0^(5i|0dP(-43C{&nqpbq=89Q_`;v*SF+njUw z8|!R^LL1U2Y>)%@*F}g716vh;>j&O1qqp{5E8K4c6ECfHuYA}N@^H53dE3K{NCjMn zwBJQY;8J~V(Hn&CYVM{qyblizF$UU_5~obahdjZYqaAX3j!CVXj_xLIO^D^BTn9Pd z+)w@IIpGU+w=U9vA+?WwRI~mu#ReF!BTcX|kpfFN=sAWjg<-74U+D0}0aI}}0h^wr~&>kie4@+tX-L}yFRGkQ4+r`OcBVt+P81-p;O;7 z3&H0Q=9((_{bvL64ylO2+wWtYJolL!o@(ALXp*CLKsYI%zEqhOw$*8e-KzAwHC}Fe zF44ZAT2A41yl~2XKMxr=b#IbrCZI!k?AGNM5!dEg)v0eE?7yrHc+Mfp5G-NWBrmVA zKcr&4-(QR+GR6c1q3T{fdj*$8HJrL6=W?=YvdfVYM$fyF7MZNaenZzyAOiwP&e=4ESsJ*&Cp^ zpLNY~k}dD=-Dd^a{r_%;{f}UnoZ1oA;e4+-|~5}5W;SJrjCe`mm0y|{l2FctvzXJ?sJhx^lo z`%{GbhyMCqf%`A|7pwX9r}hT1qJV9Zz{gWr2G~EF%Pjqi&-E$`0OS1&0ILJ}{H=cl zccVjke%;vny~xbvTgT~J&pNjEOaJ(ueNOMiFZU~c?qBp*%;aDCSIQFP zZ~6=L+x=p@{0@-u0i3=HzWnRb9?Jj&W^J-%Z2_`Bv1NZ|%VkMmJ1hyzF|pYp@zy_k z%|7+7Scmjj9vEwXIW8`q@?YIAR)6nLb1yUVPjl~IwY@)`y?^wtvcKDUe+;m?Iu`$R zy`ka8{}TXXHT3@70Q=toVE?M>u{N52HT5Q^{${{t{#$47mj(7O4D1E#oZf#n_gEwt z>zv-&+AkIC?c2A%VX*hCO=s5E$B!R>^I*Sro&T53J=X5?%zqTsB>n-z_`tOPesOeq z^q-5Psukmf8W(=;KD+(}hPBoXl>EB1=QvsW=i+F6LLKYk=y~fu7e_f-SQkgHNc`S? zu5w^q+N<>Wb#e5>B5U{g#O$w&qmO&Ypu`|}LVBT&9sEWnqodiiEmi^{t!Qi_tZuq1 z8Fs&G%WkZ6gUe~a*&-JmdnJ^$`|P>;*w5%{b*y9X+xbrS{^v?|Uxc34T|n&h&+a&1 zeK%%a`eDz`#4&p5{n-+QdYb9+nXUc;G2;X4tT@0Rb4 zY~N*$>}+oxX??Vp%=`%un!Xmr#s_1NKMU64qG5MC7;*0otcp^2vSh@G|+%QF48iAU+GonMCx3+KoZBX9A!2^`&Cc3 z<64HVC8%z0gq6(0(sgd9al704*~fI}%5&&SVO7}%-ecldN5Ycb^IXD>tIvD2A9`HS z(&)`l+t+G_g5`y3E3GBHo!&SrLq*5|M3^9x|2iK?dAoK4qZnCoSU$`iyhPj+6iPFK zmly;fHLt?z-=3~>5sc@EAu|LyH=0~lZ=E~vc7>a6I-f7IMF~`DZ_Z7GTzO{rHlK4N zPi0@1o0pkk_wzt@J@r+~a>bsFf>i&GhDH1Xo%W>^ncg2)>er)TU)$FQkO$u32YFm& zl(N zcS;Tv2hYhCqt<7RLdVk3Dlg zM}KTbg=O6NvQl&8>JFo#k@&Htm5KBD(WV;vGT?v3a zBa7o=5};T*8CwYhB1&mS*GT{cPkqo{SuEI^fpWEEwJ2$*=v_SzXg=Ucx+yq~3$LOBb!18V4 z#gL=10{X_-y>=5RCWyjoij@ODoUuzhlmsHZ7 zc`A9{tdATStfn4Ma?JoWvuT`i#_k86=gh=|I62CJM*8Ahkg_FRae8{En0~fttBY=t z5&OP<;2c;oS#doRp42*lD+@>z;nhvMsHTT1BS8;g>-estC|q0H21p?wLYoVm!M^-L zO5%JR8<>E~JtZy8p?h0jatSR6D-{X0!W;;Vg97_1ly7PzpWoI)J%PPGm?FY1Ax_?QqEV4XftzN(Po^N$xwpPSvmprD(}}aM~3SlKzM$&@6i1Xcobej z@bEGdtV}LtcNfUcsm{5M4)a6?Rf@5XhK^vl%Jul4#flbMx+v~<1PFvm2&>_SwC$|O zvEu`*kL;s?9J=s}r*z|oH4;>9tx(5DvtZ*?3L;P}L!!1%;JfTF;<7UK)O~SwJ>wC& zF%e@;#|f|G#RwG;V4RX9L})FcF`DVwE(|p7S>ofENtY0VWFUODn!2f}hTmv3bLY;)H;kH-qo-tggopsEaoCm3mP7sF5d!6{&mfZ;5sg zDpZTauzO159w_Kkr=qx)zv?c@6A?rq_$a=& z`mDA0T4%5Ij(x`ao-xii=N=h;aPS2)z2}_Qb^U&S=FrZlmpd52*|f;*6&^2a;%wF~ zqb27OuRd!24CEp{gag;xh#%JvE2NE~sPMeO$L}9gd7D}OkhCY0n;$0dx!NO>pnVhL zr&#@M5@}-46T%%GhiOCac6#5|p(c>z*6irjufe2pWg%OXK7`h;vu&+I6?U680xp2R zz%kSnW$SNO*~`7jd8=!c5U-jaJguRk5Jp!Oo&s^@(=4tUa2?2urF`MYDeOE&!#_oZ zs^$T&jaAU(>YSsv@Rw2!8Bb-J*wKRdr#RQ@&78BZP*!*iu{zXS*c2L6-CTJ3Gl{us z?*ww}x(qOl7i!Jm$g{e(RoA&J{W`?%@#`JRfQFXKZrY}~OuUiP+GDNF&pww!4rq(M zsx2!gMWCtgt$B`l&OAE2#k2J46$-{6ek{P4eq#ybGLZm?WlY9qKqbn z*{8m@ajSxnsm!_`%{3A<{h2oV$1z;1D9EC0WZGr=9a#t_kl_d|!`|sYGfH=gO#8V? zVIIvetcyC2gPY<&)ZpmP@{kW$dMahqbrt0?5~HORiiwQalMQK>MK%n$zR*L~V}QS5 zc(rSH*ctNiLX1A<>OlkwQ0j$$j-hG?lN4j6tWZK6*!sEPsdYaZJYuT~`jt$7E!p6s za===H!Zj39Rf!=#1tWrnJyXPDcOWgxA@Uf{Sdt2|9DqGb zGUJEK7Ly5*?#^SZu6h{8ggS+A!udg2Qzk5(|ENC|*nwsJigy)a2W_a6my+-_G0p=5 z^a+@%><(w?gB5S=kYgp;D3FU<9F1^Gka)Yez05(hrMLH<){yCrhqf1?y zOri%w(MKc{R}QmU4y#=bdsGf*Ne*{+4(~z^|4|N_D_3xm6v3<^N)!kV&Xqt2o$St) znG}^ix}7U;C+;twr+g$X7nP?zDW%q(ccw(v?^B*OBHv3sU!N#@2tyQ2b^%@Y=Ch1LuC9!CY9C50Ee3w@&s{ErIH za}@;xmv6fwlc=J|sQl>eBAtbzxT8EgSFwg#aZ*%XN>s5zNpadyZpK2f=0H`RWq5#PX;r~ij{;y;OSPcJ1 zKmVmUsbs?s!U#a2oJ!WhA+t654_GHN_;d>Ct5Qmo zTpdmOmPVmmO6H7cJDsxdg25QD@a*lR<-}8`K^Jv%JTgn@ScPmYTzR7kUbKg}3crbc zHwwB?YSoAHFeSpBQjSga&!^C&$f-aT>@d}I`Jh*K6FIIX(5Hj5lt5iM`qMBLw<_5j zM>d8fJ!%k17K&gK$e{uAG$~bZPMEdpveDTp+B)-Fq?DiI@mGP`8`?5~XgxP%(agXk z=%B82SL7gprGOK|^5UmeCFnI?-&TMMg9fZB0)SNo^UJD&a>fl?09KVZfK`RW7v))@ z_bRBXn3IYMKN4>*6Sb(z?+Eh9gq?w1a&8eQE(!(VQb(xl$SC(@E~7|`5|9)b!tKeM zBLK1uuB$(%5sko?IxB09x>;KO6snkpZbC@r!Y4r%tF{*991KQGE^-b*;(AK4O9Oew zET7aK0g-G>Bf3QY_KmOwB1Ib{l`lYDf7?=4pBdKv91`0EmXreh5~|cz{)14(+YXqe zj`=B6>3n8Klk!WbGHl)cUN6f6!xYh8(wbO+H)kE<@UCmsO`&ji22^O2Tk|!s+YKym zV;O@|VC>)79g=2mDqZv&XV5PjXwE`IL%O@^PU-Qin-p>Azv^u;s(9S4o6G(kC+)}) zMf0r4xsY|=1U!uyL$FsQx0!Ok1JNX%&6{gC9w}k6(WAyxFF)c`O8)Xp#_PeC=VDO| z2lIkCejV7$*ZqoL_^!+yyyPJ=M7#)|@e6+)o}C$S{Ko0ygTrTWKdyu>Cama1E?M$r zby=pJ^0!*aVxfsz#h96^ohx*@b$+e%fUbEHtYK0|O;9VH$$rC%Wvjpn6W%v)eMG$!QjGCt)YcNV%M1Kj+ z0m8)7hvM``478P1U09Ec#Od@5J%@yR2bqE>y5gTuLm!sN@u&$RMbT}#dpOY_aAzFWO0zib1LppBY1}xnIpl%W9T877v~JEM{3w@NC=E!6{3s) zVbmTVs7EqoxV);~Gn^oB7=lnKk!Zc0E*fm#AjoWS#J=NjRMtD5%5uaBj1-51AqfmD z;6cGRo&8d{Wf_i>Lf@b{XHbTMbOw0c;;C7^w3eB;23--5Ra6q2l^_Cy#|UnW>z(Rn z7Si+`;Sl`|XPn6a+OMg+%dlb9t3L=~>vV!ryFs{X{g zuB!?{C|J2gyp|dU@{vK*o=eP{jfSV6r_P9}QbD+nM80bqpF1?GtAf=Nj4Y}!^^A(3+F% z3DR2cIX#`L-BwP6QCz2;^Jy)FU%ey#(Kr3&^T90PvD5ww`I3dJ@`vIxW)7i6%da*C zAM^nJ*6=%$YCGZ2dkd%oB1hBJJ{3OiE7J*xe)?YRpyT=7>coK9cP2fA`NMw7gFoUv z@ccS21QN!ec80Lt->nBG-zKWif2UD7A4>dp4wb+95pRHI#J|y~06hqx0r5AP%IBZ) zBmaGwN?NiCk&G@G%lHW=dz#I5mrcz&b?O(}MT3c`a zCz;Bfzspqm`+NRLrUJME0h!9!*wEiaf&Zjb0Xh!99V$S70ca~MEWUgDZgX+*?cei8 zRyRKY{e+#p{eN<$P$;_;%Hh$`(Vsxb$hSX_kB@<4mzb%cF1hN=6&jX*r%Txu4_q3| z5kLPQx)feJ%cV)+I`fw|?S8flqAeP=l1yujf9g^$WSS$G?@rzMsY|)+ zVb-qHvhY)vazS;8pc41v__PpR*v;2~E``4W+fsR}I_wKAziC&XVgH{k(ox=Btt;as z4)fim>B@T%?Hl@UfR@3F(m!5})>=Mm+kDbk@XSHmKXh}B?0P>-&Dr7A)je;o$_Krj z0XeQtKlsi2REHPiGbNqQ@T%>v&tBHZHovRBzWJ=p=LCIK@8Ql%AY@o$Wy;C%c$P<6On&-*R zDw4Xe)b$43fKsFZpAO!vFM`pR#0x^qf)i+nua<~nMEVzF(H;)VlOi6rf#ZUiI4YP} z8XA$N88o?XQ2OX*_lW{_d5{)@_4P1sogM1$k@s*JVCj{Ma z@=(U@d{^r0Pots-T4^U;Oi_k>lHOXZaM$~~4<&@@l`gtj-7Kq?aJ$hIMPMNLw(X!lx8FMmd22k*zewDzC?E+Qn?{OqBd zg73Zi-+6)s4P}Sv%vo9Tr%&;7WYQ^`X?_b4WH!l{o1LBhgzALF3VxbsAlwF=DOEST z1zT>dYYBTWhidT`Rn1oReV)n)&{9L?&B$sVwk#dTpic16J(;ivGsrv^uA$d*GE9Uk zJ{PYY0o%%km&A0jy4o$lmWH%={-Q=q z9am*ve0-1WeX|g)>e2&Tu0z;-W1rZQsP!m_I-*1$)px<-9HlDiBX zKDB06v}UXB#BegGM6`g^w>hRFF+Uo@@U?@2Oc&4u&N>U4Q~rz@Nch+{=A5KQ79?&+ ztpLn9wMEKv7jzdphM*Hv7p)2ukO;M>bBeqN6Lw2ihOl&w6cE<6K)yE9t2(y{tzYdP z*vWjO(uxBQ>8YE98e+;7TTlZG{P=uBjN_-4l&QmloNNg3?I{8|Lsr}^$3b^>#Cqc1 zg|syIU=>s;+z4g(!ZO#&#gInP$V~hL16)7@!*>?ZfMpiuoDgrIs|eEjb;@Ma5QJCk1hwL!+1*XqYT4FCfkOv{f8UzNFaZZIsv$J8$(M#a=-x zHpc1O8i(rWX*B&4e6nhZ7)>L12|tLf(vyqUw>8zFXT^)b+l;0fo-%Sj8W^1sUYo#u z4Jp2-C`yY&(|B?!GVP^1YsUp<_>?mlFq+-aiCt41G7<=^+VsJ< z&iV-_aG$5ylCJUAw33;o&3&RTPy3k8+f7TLV>tOq@!?oP?}Hq~ zE0hZjtZMl|O~z)v+Qn(w&yQ-*?~^9x1eF&;~`%rdbt(`j(Vt zBe|>`FY^g9Z9TPQwGSe|vCRRn`BBXQyc83*#=Vk6t;H#udRlx@aDk0Y)!#MSb(E<@$ls7c7=TE^=+N?^9rBA1*znXj8l_z4JmU-lcu^ zJ_%x!g(#=(zAa&lOopxvCn9oJMQntJa8w5Andq!29C!*w8c^A0qDKf*@yeWmsjUgn z9X|kcDXOsJGsQt5_Fjsu3)w&wA4nHN8osa0FOzlCgl}72l1KTZ)9ig0% zK7-G{L`esyJC8y(6HeC&(8XYMIt9yNr``x;QsbTZ*GMUx5392R+Pg;`^1lo>IA_ar zEzIxZD_U~PiVp{u97&wK5NB=835y3uZ#>7xjr9bdy-Yh_H*^<_3B5z8 zVhmFh;H20#vn|W5tLwPt8^qb>$)#+%cH>xlWHEfoT8SKyE(|PJ!6&u{0)lW)?Aki0 z+K!1NkFZEPu6@do{H{O>Tx^BUv@1oO5a{b(uEYHp3sbE;{ZOiTn0N3`nlo`hA6$pC zMy=@#(k~IR139bTGA}~gBb>9Zc|U%A{zgFG`s-R9)R4iO%y7Xz9Fi@M!b6lD(`*Md z1U6DHL9A6p@~1~rPGA@dL4#nADxy*!hDE`a`ox(5nA)xezk8uW`IF}Qa(CjHP!+KRTyN~S;=rnB$czr3{$jV(?FzX6bl>Y3 z(SWEk{b7J36ovcYf2Vkt@hWwBxOo4Avu_yhBq_x1Ao~?RXV-(ypef5&k(wuTkFk{I z8;K_=tww8yX=c zum}$=g2QED1yvCbbFUNv zRVb@*I)N3M{aprAlzfmVLu7{+V0F-t&0nd?m{CZug3x`yhcj59t z5>;aMQCDFD0U&YkZIH)WAR|o<{lPB7UR9D)1EZ#mv~~moW`OQs@j@kmjv$LHY+!g> znCieOZyV@gKaaJ&?rI+)gkp28tNM&_Z!^JeAZ7a+cCBEiTb zKf@9^aVXw+Dfb>I?tb7!)snE@qRaMCISxm>cSMS8+S8fm(UC@G(Iq)_Pm5f-3VJT{ z&x8SFN3r~;qALwKFGeqYZ7Aj#Dt@$7fJ<=P8g<;EapIO!>ck_<=UJ-4u4>3Bd566DtR2trtb6T|QG_#wIM;Y?` zRfKc&{Ue3vq7f&H;q0AI1uw^QD##8rxDOxxsGC{lB!g{9D!-iFKHmS{QRVVNX~1N< zJLPh`vS;!$AnGKRtIad>z%xhLYiiuH=VV%0UWOC9CWMGIp%^2%I2NNo#roxmRFN2xGHxLT+Jw&QD&sh zoVGq%t8#j&;FEs9K}x{!*A%I59zFYa)*R7HW*Z)K4-7#kLLM(wK50R@Ix93+_FkHaC$Iq2Mr;N#CB zTR4{)WjehD7rt?U=>gb^|=@)5X4Gex` z!52ksQeQwiqoaG&>H2QCT=`lRvydMDBFyQzkh6`n+vV_(;&6dtxc~FUZnbMUH>!xU zK1++**%CD}rA_0vo92q(qyZ362uc!!t#v_T52@Zf3$gGBeqeyJ)+^O7g41?|SgvbN zp8$3P~=cd$iW#poBcX8UD=`nYscZ{ltyUCfn*$X2f2R+sUl%O<#SR6)>QxPF9`Zw&n-+7<-^mlJ7<7k95L z8JsXoLvBP!sm9<<4AZbD>z1B%>Gg&j8>WpfdrW#G$(Oq&B}hVL@Z7Jrl8jPPHl^4$ zV!Vq3a3&RR#a#I#HNU^8^fW zaubGc59_)osvISB#DK9Sr?&UpEaR|m=5>rp5uFus)PMutlmk#ILLfD?OFN=Cb+Ou;sN?skKrsf%P`qSMfFJoHI(=W~rf&x32XMSFIdvb0HelT2b-(*3sr z8C#1`w)|6!6#X6&7ga?EYLwUjW`hCCOXo#q4=s4t}GcBrq8fK3mw2C=g?p? z5ON(AGxvmU@B9wGWR%g8bo6RgmPR2m3s1ZBy7m*;ZDg+T-OFIBH z(M1Si?#ANoo?5(ef!06n;#DtoyvDRat~Y%kI5XR(;UU83dTNbhTAHvvifr9+sa4dhb(VcI*J~%RCXKS5i?0|RQW$Sk;`+C&Vwf{-6=#|OSBuFW#Y%Gpok&P=3}5zZyVfgFBB?zdNw!d{AEQQV<4?Pp{8 z={L%9z0M<9ENGk|A5o0Z7v~Tb;{piLW}lh3Z;Go@6(e+_H|dVAfj<;G%;lXWy2H@< zx?cJ84271m$c;tqCG|&L`)AaweWahyGFLg1Mb8_Ry66x1-+z8jBD%sl@ak&E1a6m6 zel2t%>}rOj>pj^y9%ZC_nC}wVd1-)7ZYl-^NZ8~P)=W9IaKE*8#J;M0Vxaq@Zu)&PjptK~I;H;GW5{We zv)Cklbr$l6ttxb-bGUil*vmaHrLnw|W`Dq+B)u@bYxQv1Ls%I}0JX>-#`*TTkf0Ed zirivgQPOu|y$}!`^UL0nI??NluP`Wk6w+{io+WlR`S{|nV@mdYtWQlS=n1Z4kfp-6 z8Gru@=)re7p=N(tD3_8iPNKg*ArTrRtC=wCx zR$9l_l-gz{kh^$A4++yLQ-dDzd7+ES$74Wrb;%?UbWVzNmkTxP7X0M^2qu!M?UKuvV?7_Vm}< zJM*uVPP+Q3Igj{!@q|t$L$A`bJaf7YnFY>(nZGNAYIaA-c?c+ImEM-B>hRN{k10 zp=f#)$m${mub1AkNIASJxm)@6FyQ!}v4&xD$5g{2I_YvsFm92~;ec>Ci9PEh59!<= zgJR)FrEQ+6D}I+dgwB4Z)@Ta0-rLS@3VBb4?c$g;TG{A^PoWmk(N`Un_dwN*ui|xS zSol=Yvy1Ug`vYhxPTxvS#QFrECJ1^$fpB~6yYYw_Uk>%+oRJ}9t=7N}Ui~0Xd-ttr z9NJ9K7d}=snGC1VX6-VmD2n1`66{0lR1`)%2`*Kz*`4Zhhu(A?ExqvG`qrhpE+e#% z37gyQ4=VM_I;21yehXA;(dY{`pfDK24IcD`oGxN<-}&dloc%ar@dMqK9h`ad^qQT( z{Qh+Hd+tWDBA31ZIeG6bF5ideZ%?dpfZt3Olt#f9P{Nl33t1Y6Putjk(<; z>hWz6nWkHj=3xszfUTU+e6K_?tYcm=G&$`g4VmO zJra~(443p&U|{%qQa*`{9h3X>AXBUxmNV z)J&$IYC4-!r3R}{EMy5f?e@&er6<9y^*V>cn$g1>FQdOPa>Weoo6CKQR5(=~pKo!! zQD&QIoIqJFo2d?|5j-Y8{Qgy837!F zliSYGHyXAak zXf1k1Z|m-87*cstf+$7Qy9yg}nWB}5&S8(z@uJ!cIIP$I+`cf=xzz!21~cYIZYk8P zxE1C(aVGG%ZNzaqz1%lnHksN;Zfn!p647Jnob(?}P2C}~?Kf;cbq!t@*+X3v)0BOt zmC@^CkhFdxrYne`+?+cGllwTWODOwOcoy0lHD_njUlW{*Ded^N8UFUl2Q!G%AkyH~ zjW{rzwFVCN0)n#bi8UH86Ix+K36Gy@2F-kISGk)Weqj$3tpCDjddB8*#XaQ5g3hpo z4-S1n2`GsKHfvKuN~8NOkY|&>zULCY(zzu$<2$;6IETn@9p`B7Ki&Uz2BGpQ`on`8 zr=vchw<@|q$i3TzA8sDLk!_3dIv#$%+vFYnbmsb1$8Fa?&i-ieetz%jj~_6!%?=0V z$6hubri-IGzN~_wshq(xB;g<;hG?kyY$UQB2UY4CY_TFGUVC5pPBwx3{K{;SSlu<4 zg?Jar{vjkaa5T}lOPl+dY>-6zQlC9MM+YkdI_*hQ4evb7#9qh!O%?ie`%Ve3KPuJi z)e}Z!4ncgiUdF<6R6r1WSI{v{!CK9QIp%hANNBA_t5K$yOFo!L_yTKpBo(lw?D zP2-AP=0mjW*idHEepRvtMnzscl1!p|tmQ(jkk~TF643ITx%YyhJwl@Lp|iJ`82JGK-DeOxC#Y)FA=aKf1{)@F$;h_zGZ6I;^x)RIy_$Gu2;t8ku1x{ zK} zH3^2%R66dHolFjGmYw7e2ZS~^!VD|XO=m7HpT z$3Y5;si`R#B9E;Lqg+=K{WCSAgwQGWD|zdunQYJ1KhDYvDlu_n)FSk0PADTv4DJNG z(9<&s^UhUUs-hE7g{V~#%c@NESM6Ncp8nh)(Ns)tGCgj!7#%@@j8iS`T7mBVJo2`>8KZ&T69H^ zRK$r@tK3A$Urmz(vp7aHzi!@GS8N17iGUvA8~d)8d$O-$;+vv@Y@T}U{` zqqihDc=MJvqaaND3Vrb~zp*hOXqG%MC*83u;Ko#=wi2NwWjg5=in(% zHD`#lHzUkk{)`{n$v#)EKYY5GQ}2FYj~il*n~em8Vrb77CrDiiHGcxiKVw-f5-VDO zQ4oEj74!O)1aHDei+;K6bVc3!tZy$wv1p0-Q7)|0IoHJav@NBX^d>u=8)1cGi*82T zeP773{!kZ#D!|tIZ8aA9tvgpHj@0<3jwP(s%e6|_9WU{%XpB=-T_7zt1C$AT7FA+>#_cNP>c;$*fstqNCw|5}1 zFiM3kIn@;>-Mmrw6;yI{fMFq>K5+g_Tx5?ft}+47O(G;$9jWC>_FeX=ZU#Yah4hW@ zw~9S!$RMXi6(`%0VIFujj3hkRld8s$IekdHZ<5N*85)O9{lhrv5+y|%B(EXS+qd;c zZZS-klm8UC5eI6>FHRlWD|UR; z#G~4nWG_yqkug9@QISG2l;lUDpb#U76FC(JN-$1JPK5U*ME;R0F{Jt}MNP48fHR}g z1Q|5n0xnT;d5>v^52D(6lTc!Q!P=P!4ceD|wOyoBX!xzK3GMchygq48WSHF|u?v?W zpqa+6ftM+Uxr@UaY*T1;;QUG5Q6T6`@m$^A1evGw*g26OQHSV<-u;cnDM##OcX(?NJWLqJ*?6oIf-}l7t~7^6Yje>Zf9PTi%QyZ)=beZDD4u%p94s@7q`y~ zYrj;zf1__`$S_HoM9L`Fn=MiIY3|W2p1j49%xB2&`08YNUpJ~cQ@)#Y?r6GduCF>2 zURlh23Y2}U3y;5go~>Tk$alM27jMQ-M2cy`+Yxt9#u9W1Bt#RcYuHVl`Q zQJm`7l`?IFcpb^|I*_2`m8T*HjV1G}0fXpK1-v;0`&&$+JtjL{d<=tS5(+R)uqmf2 z(n{FWdy4obsr0QPT&S^rzOIU4y5qV!ZP@+MDc%{TZM_Gp(iL-k0BW5fNE=K7J@VzK z#HL1gQrShO4jAE2HzqMr_@owAzB<^OPL{odC5378#UVG`~ueZj7Vo=hlPY6uHz15n4e)%1o4YXFG`%xkc8`ZA)l))Sj%Bk!obILoP-<;{2{K zXA3vZTULxyg~fDP9flHIdNMvKraUVpludF?3Ry0wg48RB$|g)c5Ne(C^rmKz*hQ$_ zR#`_kZT=u>@}9>IGZGicDFO!CHo8>_&NTdM~wM zb_gfkhAPZ+{|}!Jybm97c)q@~vkCATb#D=s!VVSvsY#*qKp(qIJs<%_H(TBe z!h@t#0JeMz9Q?OIvHuK~|4!7Blv9FY0#wBY zs&)~oaS5vF^f%PpR8+(4FVtK`?)SUhW)y&6yff1m^U$WHb1}c;wOXt3JBPG^XAQe z;?GxsTrD6r_7{5olRW>0o&RLce*x!z(dGvS2NVkB>({Tqq}cC4F)-qvLs0)6Tb}k` zu;o#04ZwVUA}ycsoWRVlK{31breAFNrJsXh`Ic?o09*ckxEVK`@Wr?T7!=bQy>Tgq z_2$dYz?C?6lg=HZZXj3dv`^aP}_#1bS#=RWyUX=i9_pBhTs>)~tx7lr{Hx zzErgLxFX5bycJf*>%JVWezOjGnJN$UELvM48kYU z4dh19hKg~6xn~~{IZYDg-TZ3Vs`SJzT1_q$wA(2siaaq|9;0&fhc|e6bu>LMt?xd0 z*&)yaV9Pz961gg4Wg5g>0k-_jwW|nkPpc&M=#839kuAYdQ}~7 z(tNbsLx(0fcn$M)HJ3SqJX^i_E~2PBaE~WN1L;8)rw%c#;&)u^d}hHiibDu`lo`tm z(@L&JVK=l}Rz&ySY@2T1Cwsk9X!;Qg9WXClyg&)k=VS#dA7efZ- zR>7IGgN!fNhmVgX<+af-l|M~N7xuAENm18)nv%Zu?Y5=deN6>RrR-RRhiX51LoUKf zyd}^&htnG~nq0aAkA)(&(@vmJev0=DS9E6|LljRNGg7UhXy+_B^5fA_vIAJ=P0Ic` zbz0}wx$@svALpukCD&kC4<>Gh9y{1_n6Pe+9-v9n732=E)L??AaEHA*01mpq@pIhZs+R~mZZ)%B$dzUXM0hBx|-Zy zKB#Hmqg(BMiBA`L@KmMT(}+`1wB1Bwmr)m@ahKCgeJS~(^f$5k-BwjCHG-w_Np_+E!*n{m@sz+8&zm=(A|Poz z30hoCO~pP-!z8}S@lkMMC$749+v^;9MN*FmLhjm4CdERW@%#;WHT8M|{FAfeXXkQ~ z>Ob7#_SZ<(I9@`A2!m0_y$WNh$3fY)yw!YfH^x;b9%VbB7GbmVFeX>cPmUzI;x`1}R0U;o9m{%Z z;-O~&4qePzhj+tJTd~0cDQ<}4JfftYiMD7DS+qb~sHS2b@97{^T}er9FG2liggN~X z^n&6DT4E1sx>0Qxz6u;VKm?~oYM$ebaRp&4gZ9^4Kj{q!du#+s?W<+3h2gl|kNAG+ z@F4Wl#T1bvi_@5qT+)o@DBq8q!E2GT8fZ=D6C)AYtRCjc(y^{*wyV?5FH&<}{OryV z(a9j73ASfGo_~F&Wsf?fN(Zq=NImtnmBE+%f$a%y2zfh4>u~T7PrrN`#dmwDCme?P z&&^(z&P5}6A7;VEW>Y1PMd@O6oxRRr$JLUs=~uRwGrtZPXuL)A+0VPOT@}J8wLD_D zl%c~RnyjsCI!Nuds6$J&2~WkxV9at>9!(}Y;kU76E{uSXi7K!Y@Kq)c>Xcj2KHCl{3h62qF3dR!YB-_KSY=MhkiSotc!Ze#K1QQvn>PjGxW~Q z6m?+`!LE3&hj&^AZTXeaioX7KA)legeMH)W@PoFfi!YL3?+5JF*-*W&tJajeF23sD zvq@sL{`1ZH#TvfwaAn~ZQrOh1g`0x6hRS4O5@#95?ZbN8U+!rMgKovOtRR_hL64Js zfb1?#Enb(^8ePQgi&U#M*=6nfXKqC^hEWEt*pxrKO5FmJW2JE1ce{uaO0(gWA%SQJ zV1D&hD7A3iF!chOAVd!rqF$(TI0%-83_(IZ>zo)X-mb?v@W%%Rj8)BoR|~^kWdm~C z_cr#tDsVS0owGyhDmUef%Vmpsa-sOpBRWW!L(D28YVh@F{`&p1p0g{dCe8aSPvi{a zR1-(AqWI!OeUNygPV;794c!N(bdS|qwv2{0ql&<6-`U3#o%5-jEAewMv_b{NS_y)qxLJBg;mpkK#`qJJ7OPcb0nn z!RXxb!`SC^9XwCXylpf{oFq2C!hN<&Oy!f&yZ}qbgs+K#MiM?PT>JW14OS8_9;@Gez}XxSGeQ{ns;lVx^UwzM+QQm>kpZjEUclU1bMoWb^wb-9%3-LA|hpqViS z>Ph%LwFvdj-Vf1SOpnuI`sT_+Pfb20C{yj+>y82MV({0IIFHb%eb=3qY#;h$0eHQBuI26jMOEX z74zLCBZ1f__GF9qd5N3*8ZaxAE(-Y$Egkrd{vK2EdE4Z3q$J}BR&xSI2F|a)$dI^- z`~=cq1jkgcXkN5Mb&z6ykdbYlc>*|7Lny3XUCbJRgjGT@A+3v$F;B>%5J;TFTvcMe zEg+yJ78essyNKoU#7Y7|<~*TBHLcDztsye4u{iBUS6a(_+AT^NnKS*iYI?hEdPih> zS8;kzS9;%kdcUEd$*1%oxB^B#V+^hcM7K;)w3KgWJmhqpq+~pv;2%`YdA_%rj3XXYYj)??MI)s2j`$gHX2tS!%s?fI-BO4ja1`X|-wKHKbr#&k+?c2`&S zH_!BAN_IOZiRxrJ#EwLcB*8b+XuC-*^CZTmGypGeRLx=aN(1ony5bz}lW7-zYjN9JcOYJtXJ6{)G1xy;vh&vKUfRv&*Y_iCVyIxW@W`|dAYS(34=8`e z9ogk^Qr=c3a15TAEL8AuBu2HZ&+us^@|#81Y&>o}F9sRtx%2)xz)Mt`^>%!C*%boB z%L2#Xw*!ek<7K<{RwnW!ogVf9@v^hECfCgG_Iv^2ZGGN8yxV*DadG%j`iXP>z%h8^ zNwd=oa11`&UAcb^I0k?Jld}K*?aaMXhE;}Z^lA-v_b0t%Ct#Ktw^I&5o z8v{MLO44QbTFo&OZCcGWRo-09!|I;|T20no?+R=^n%)&Ugm1np!lj&CD|RdNS}XBv zXj&`v?buu^3miMSULN|)YrP_Jt!ce7_F!}U8XhXKQI*8*y-`gNm2k%V^2k9zaFEnl z;LGO%v4HAfFR{tH=BNDCbyIO`nrISKWrY>gwj_ZVBR%7X5fvqzOIwp9 zI2>yzSB-}^F+^2n!Usnn5H4d)I36ow!iF#pHH{Y#2Qx^jPeCM~t-K|iMAl^Z2Fkzd z7Cd$KVXWQn?*T*u{H9XYK+VM%%mTX3FMCE(Tn2#iL}ZjjWK;xY)Fh#R6*QYd4EaTfd1yvbBizVCzb`KSA=9;1H!_u6gH%|R}(yHv%Fify>F6y zZWRZ176*1!g$-7P4K+qTh%adZV!yNNZkOEbu57(q(|+%FdUs3TSkL(E-Kod@_n%BZ zd-d@7>qjr%K6$zH{LRXXrFU=MZLVx?12EaUtq+?YcHe*81CX+hpFZz>{_^Sb!6D`7 z=Rk-szsqGbsxc%Z@{#E0xIL{jOHm>(MV;ocEreq7n(UV%-Ujd{?HOej**%Y#QDu zh&?RUTIw7v6|~igiyW%I&&I@iIb?FER?A?RFOlLkXLyc2OJqkpc-7E6S9iMPRP^~C zV6zc8_otc3*mMa!*yT7TbWwMTCYz%{u@z07p7pj|EqC$BcoGQ{$>}?xBR)MoQ|&Ca zh_CZHe9o-@O~lavr5i-UxE^Fh&7mT@JHTSDI=~c2kJo|&C2b}+y$&Zc%n-y3Y&MZx z8#YXO;*ARiLRh9ER~9L%<%M@RU;CbhC-Bx8TZlzQfem>} zK@nF|=%onaI4mrfpk5s{grY8pDlf8=2dpbnk|0qcOZ|S@7XHAJ>v{2 z0p7Fx=o2Rd&}cy+AyHvrQAtS|;3cc5r~=4QH8sxylriw=>gwz28W6;0=H}t8Cttwja2r2w%TAD;+7yNQ33h^C|vQd5Zlw@W0Zr={q&GqX|jX$3zz&m;aFpvHV_2})}ZEbDs?d^Bk+dDcsI)B5zfPl0IP>}Za z_4VBa5a9m)dw+M3j{JN^0sePvYi#bfczSyJKQyGXv$MZEq<`0t z{;dA~F8%($3P|7mtOBpCudn}qt0Dd0?o|!EXF2}iJ-ehujrxz;w)ZR5AntI0-BP_l($;|A)QzjB2u5_w}FjLV$!Cnk4j2 zK6!|&?)_Z6i8YJMSwXp2-YkYXu2s>izG%wt&~n{;*_Etx z6Z1CJA5UY9Uk9o5EX{{7Z~_FKVxsG(9*Cvf6}bP9C0Orey_@gxi}gdPhCJM`vOot8M#o9wJjE1($%l}qH$0C?4FR_NqiC@agix#Bf6&x^pfPCYUYL^9^gYjd zke$vXQPz!oYe~#~G8hp1AVnLKkiEyr72JDO+*002iR~Y)zVmBiCi$%%&+5RfuA!3D zGa-O@5{E9A2xn%DLCs(4<+^?I=)*z1DEC@Hh95yz5GP>SEq-$gUZW**f5fCM5}N_! z5+drQLJ|ZQRrBNw$X3IVak3xaZSY(12dz!|m65W#6RLMB1nI;)LO9o4QqB|jgrf%C z$g0@89O`~&zcDI+gY)&Od~dBVK=io0{#>T?^0&{LxNDo=1ZbRZLXNA0aCxnWVRe_= z66JM*t}h&MmiD+2HFfopGBfXxlFM4XMA_Zawkt{~+9i&C&D2 zX+B=!2(kKcBoN_YII&pRC_7%F z3feaZ>hR--`IfzM6dS2b@gu&ctN?VpwZ_WO$flL`%8&)*;OKa?_dzAVa*Kg7^ZJ&9 z`)_&U;=HH;DoO1&Od63&9T%!k4_O=49S<)-C(~Tat%#L&;4RzS1#!Z0!6d)s{U3*t ztoj%&ck}HGjTzYB2~DGM>>NAyrNnJ|N3u{R+|H9)vwel&^}PhTcoeRo{zs}kfDjEr z;^tOkA9Uj2hgar6`4U|itGBp|m>$-dbN64rPj9aFmhDbBG&e_~)K~@c5Jq-X1r*g8 zp^}ZjKb(n!uX5zNfs9%&CI8{cJN=ikceOT!90};Y^JieDe#M?FI(%DAZ{8(_R4wF}sLHB-KuGD#_3J0|v82^`7FHOzJ`7Z17Bs3f)fkkcq6j@_)O*`Rso ztDe?znfdh^M4nFXe5vnXfL!y}vS3#?y}P}>hDUs7j`UBTdkhj|eG z_GD>9!@{ZOS*j+_)SZ$U3#T7%j=nJbRJ|wt&_dlUXxlbe@@|Gu*(qJ~U<>h3kxYxS zdco|McB#<}hmQ>WwL~Z^npCLe<$|kYO{f^U;BagOp_qC*Y+wnhnM21J^4e_Mk@Lr^ z#F{0~pkP)dNT{P#i@cz^ZA6W0zQ2z|%J)}VP@h7d-UbAmOqZdVW2h8`T^6M4q#S+~ zA(1CMNEp+kQUTc_Up$8jg()xqXEm@wHZ>cG;lbU4$+&u!6)9a0S$AifTOWQ*ca=JC z(d&!vUv;o@JWyslQK4YMfvF!_WqA1e+7E1i!8u&C9i9&Fwu8dRg#3L?9CQ~M40{Yf zgvu2}WLGzbG}j0v+ZR0`O`L2|3@QJD#knjNJDh%~xr>&wGG zRF)SnZk9_V>T|a2jRHH1DJTRdS_`bu3VuuzBz)Llojbe*;EIA)3idU{*Bq*l$Pi7z znyYU=i==Gy!n$agAoBe?7j!DcqW$ytagx`3RjZD2?U^pA z`-VD$hqh2k>Lhvl;fF-izUySiTXOUdA?7SZP>M;5#WmZ^TBI5}8_}#kCOE&LgXa zyEI-az6V4vT1THKTmohk5Ias!h&tggzAqcFGR-k@IFgs|M=&nTfG#2=gK-JBM)%VU zIs(!beRvE3QN3461O=p*((QTtIra89bBtFlXr@l6&uZ5)#d`0-<9A#9`tJ_B(~FlW zdh*_PpwIZdX65CA7h}u4U-}{p`%`NE*lR!dY)54H9kbRiIE50CcLr_)%nz<{0;Y2ACRuW| za{bTmpQEZkTBBr$Yf%FvnRML?hbur@iHk7^h_hJDfU%$y5_&Vp6&wf%w1|}ffd}dC za2BYQN#&{1q*!Q`905H}^0^Bca(U@iFEUhHL>Z)T27u|N!#YTqCbl>dpkqO>J}m4g z40dT%qL#z|hvv?a^yW`ON4z|`oglE6fD@2}H8MeZtYix##vXi%sUGw^D@}rUG>{Is zGozyk0^4|yW~P7@1>DJ|tH6-LT*M$LJUu=O&O~jWpzn*%0KG^Dv9j97QK73@pw`?! z_Tl1K0`%2DI0w+@0Gn7J*x;%_OA8{e3S3XZoF^l?sSqpjVcZh@3V_L4MWqG?YiMvq6kZ$mvfp^N=e zDDuM6d{Gw_RD^p$!w*GnjuoL_6oJbkHv*46$vSqT>Dc_(F`?(jB*a7<=xA0B#l1Z6 zt-Gj-IBI~f&tVlk!WBp(`>K9Mkv)Quh8h4t}Z zr{SSrJiSYvp^7B-*U`WgaVStyAQFySsgS>NoB5zM^lP5?4!hF(w+ok5n8wXW)j;19 z*ZqX6J&wmpl-;Q)mCkpwG{%uaN>d1BTdm6;x0GV_paWQBS7ly5`=D4JN|pfa;+5@C zD>bl>ut4Gbv7RGvWjI9U&^Bbq~gB*Zn{(XQ? zRPA@@AKGbFqWbb2B` zk|{GY^*41b$j|)ETlu85wWYqTwSmuDf5$3a=U!-5-Cv|L|dTeSM9OT=}r|uM(GE80$ZSt^DhO)L+*F z^9&GJ)+?9^7tHy0*Mo_!#=ouyvMBu}n_Q)T%19~?aaFwWZy8BaVS)e1NHWk6Q=Dt2 z{FRZUVUaDiqs@-Adgk_d>PUgyR9A=?NG;}`*% z&=vgKyWj@{d7_$!ruB!=BCu_v$RRRFKop5?@R;I}p5A0x79LuxzYfk&aw2VDGZ{lO z-txu2n7=PJ3(-g=D7*`M5*F})pOLiTF0A7u3jJX5HJW!STjm$@pGA>uWC?lZHu=*~ zqs?g^_Q54S^LK+!aKYnLEco8#KQ*}kN+x?pE1&uQY2G98>ctZ}v<5-7221dn|6vpX z3lcHr5g=HH4QiIC1wU{PrO#*n_67t1gvF5Qc}g{TzWJ)fp36C+ameMOZSSo(5FBQe zP;BZt@wC`PA854FzZ1MtLIJVJIyzHBHm zeEqU~-J$ z_%}zIAEs-*AGo9tqLVpyx?LyZ)wwtC)8AdyJRkRAHY_sg)8lsCBVXUY(GC8&sY&w% z35T-}!sI(>o+#b7BoDz|S|40-&f#4y;)gq;oTVb)>N&|B)mqz4JRaWdpj_7xZKr7ORjr=g+Bd`3tqh)YtXr7;@phfO6_Sl_+8YU17Xu~cO{Mr_K*4=e@D9)s96+$AAWwOpA&KIUF5^)H@g!aeSUoG!scM| zAMYZbq_+zs@}3JhZItB5AM#r+xH*ydRDP$B$t6AK&F+;-$-JcJkcio&qo}& zz3JTe^15q!JjCw(`oKF1?n$)RQz`o^k3TJ2wy*e z`TB{)KP>r2CQ=aK&!MpUOX9%zy}kU%Xpjis>+m&II3UTFmA_ucKQ@sv{I5PpfW;y+ z;0WpM1MK>dTw!q)D^H5=b^2d_1>(__*$#mo6qZ1R?C;vKf=eVX5`5Hbf4M#?6#tWXRjOxiJzMjXiyLV??8MDmb`6o9S8`eqD^5LGaa zSOxH?2-apBHn@I-UdA6nZ%pAqURyg(VJz6=+v87#_6eNQhHJs8dvga}14=5KBpj zcS%liO-pr4PvbA98=dZvle@2=(6gj$e@Xel6DPfDSzf2=y-zoI*EM?Aw@_O;sBN9T z=lcB4_w&Dr`1fA%@4M>Xe>LC&CtzSG;No!LCH~Jy;Fa-^(b>@P`OwMR;Zt|RXYYsK zd~k&O=*ZlYh+9h$x1UDdeHOX+oObU8?cPh;gV#|H-$Xrn8};~I^pp3|ygy=|uE#$6 z6#L>!?8~olufNB?{TcsmGvWPzTSWfvUHSj=-H_{m=C4PFnl0u~K0%uJ^(a8yw6Ydy znSu~8Ifv;9Zl+KXgt$U(Sn-1a$^}YGdcG zUaq_doIL?rFQY`@{`bX}h_W!n3F3M0*MT zmN1c>Y5*y3e}*uBVEaz&vahB&#!?>>PEsP4;H(j~YNe}BpKwG~-xAJ@W|E&NC7@;5 z3NL1VKjO$={`%JeBK+$SH#XO{a4_2AVddfN=t*_<3q2SX?H8LI&fj44oTRLRw45S( zela62FR!R1=U7>8OFh)A)bdpqE-sT*2+wX=ThM4FC19^K<+88YK<{04|_I|A|K7JCxt{ zgzruG(uA)|ek+oH_z}Jw;hPb@6Z!ivxNyD);Y*OehQa-t1o_K={7rxSv-|iT#m8UP zjfcf}^wZ+_`h-+3YShyZb*=8~@U43=fa|ch$zf zAF}rU+l~M3!}Ndd_56Dh4v+u+jpbiu$$I6Hd1Jpc%J14JqFKY=b+xOz`mfq3ewOSu zyO_UfqtHhdWB$sLJ*Rc{uNDC*{bRpc1nS++|Ei4&xO0wwPr9-Z_bW>_DS6vVUrH;b z=EnAy=wYnd*2Gn%TIR&0`Od(3G~BcT(Xv%Uh>f)h2JhRN`dnw_&0PXDM+cp~Se3NS zyNRe?`{c^CXwdM|-P-iSXmwbYxjiULTc-C&iTXk<@bkl3?n<=Mw(|yI>mj!HC+X76;%*ZxMZ8Ul3 z>C~i{I~acQDZhVI1drk-m0pkklHK49PfP?CD4F3$_sLW!>4T7T`aDV+V?0NYzw?a5 z6qPf}Xt~Dz-ik21{DClfu~X;EEDi zl*uh3_?0YGcQx9WO2YQlOy!->{gc9oCKgiW_MZLCh~n@Px?pM6erD)*lzmf)1p9fm zg1QpcS~cTYy$x6$?}I%8-%iSt6CrV|mDR;YB^9-XG*?TH3rd6Dw}l~zH4w$iQapgT&#uN!4AS}Fz4-k& z^yE3iHS5T@QK48EQK$(m?q5##0Gai0nw#T( z4pO4|RR7j?IG_?Xi1K#H^T(?xgip)v`|{yt+T`mFkhz+I_Ts*c7H0pxgNu^zBG0!v z#6<_dc9Q@*-VrnWZp^!GBDnq3df);&^&{Ed?Mkn!8>|F{&)3e$joN;AueGufHvx-c z7R@!ua+!rnXNNG{K%GI{fi>%=H2gKx^O$BtvlZTh%w$mAkg;Nn9j~!R7@W5mxiri54v8&`q zV1_at%cY5~FcescYlzuaMdavqxPA%og$Rw98RcXPwYO@;X;SWyKf7J~ZbCPZ;AN^B zo5Oci`G6vrOa(707XA<S~;wJ>4R+tGE5hMAIiYG>Oy-Brvl3YA-*u$|~^K29%xDi5)@GZX4&=G}Tq6rHr& zWw=)2H0c0artWg;$N+&lP-Ey)$t*yd7{m?b!Ri|*f=}+FU_ek_Ex^(6 z9@AL?s?YBRQ1@tL&eCkQwrQc<@T%Ym#_*Avu}3+loQwPWsEs}y|D z;Wjkvn*pSaz9~J4+ju%-yQ=AeNwI`J4s8=m&X)VeeOKIrxciZ-=eUW_f=u%e#^GFn zk3*gi^C6VRDh%`E9oM!#L zGYoh0Iw7Q$a!rgj1o;AmVhtTF*VrA^{_WESNtf?bW;ggMn?B+f zlWeUOhq^eVsBkIw2@R`y;~qog-pK`o0JPO(lJ-kUf!2T;~CPkGoFs3eq;0ek5q5ePjCO zckW*Ok^w5i@1%kwRW1KqDuS1J#$mo9(9$>F2|`|3C=XOW-4SMcyTaIlbyRDs#Xh#^ z#=1M)XmLn52Wz9FG$H6VFf4IHFh{>5PO$aVh}>h*U>S)|Hi6`{FNc_q6{=wuL_a9AR{I{nY!k^~(=8H!rk7bZA1NgS>(r#=~7_R2+Da=rH=SFb8rWYgn`pKdk4oyxwAVWkm`!tne@arK!Zyv>kw|_L5M` zTbf!#-#P5XlMoJ8^;9A3aB1cH(pCf&eiE6@A5&lo6+P6g0^;gZojss9Npgva^>kHSfPXS6v39YR@kR8|ZP)a^VF%}k1`|MR- zTgc92X{{@q4_YR=k&(hkqKE+{R1!nZd2|qgF4!0)Fk|UzgTWSBzzGSw`TX|5X30~(3X2R%T=O0f`d@--19wykqXETo6D{@Vcq6n@}r1l-yjtc zSXU#sVilpx1QoJV{8jhS27M%U2NxI6xxZ526>H zM3c{DA_TF33~ft24H>c#_4rFvSa41x9`L2fR{+SZK!WN*5{Qy>Brk`ENNI@EyYBkYClt?;Aj! z-pDI4&R4~6O%~aHenUpO!To_ED4r>gqagPtKnz#&8({8@5M|Xi6*xyF0<7x8&?B|U zxmqZj^(gLGQ+yMTjpRY)=}3KjNH!gG%}>vciMkBNt7B0H{(xW%U`9bz1p(n~#Mm6D zh(dIsqqLYRWBx!SJq${Wj#7h1;DHztWXlO?5)d8rG76T&M1|xEn?T;bjN-L2PpLBF zUPfglV!tmP_~_z4Vu01)AiHs;7oW>G(X{F|4%m@V50$)@hBrxSNm?oT$WL;peu@% zz)XL6BJ~~+#w?NFS0dkfB2_(Fah+}XY}XPhZrkI#Vq;T-}zWc-j?A=b{!Ai?Z$D(yhk0{R$LWxK0G&bK>TiP8C!n^{_4H{diOn)ZrHRWmOH#Z#DPk-5IJW3d&*-gu zddUVcjH3l6Pv?bH<=K~Hm_)zIuaYbQmjMiD2Dmk$+KFpm&cwz(jSG{inYF8V?#6;f zu{so3+?5*8S>HBMD9->p*{#X9igczYG()c(bx`bn&9Z&Iyv6|&Eb8t$oa;PShzHQeQ%kY_6!&UllruF$HopP= zT0N?!AU3o$LR!5^sZe1Az8R@OmR<&x(ACB1h?NacFbjktA=KTm{sd%9RA8f<=@k=f zub)|Y=owpeln)cwPC;oW?}QqUiCg+ALXzxt(2O>; z?H%szG3|VpgE-!IT*1{h`oSqBGlQU>x)4isD-D0(nZY&HM_ z{SA9#e=#v{KpqhJtEYpH#Y`+oX10dA_Utluv$NjsZ0qf@Cpf?IUC&1Lq%8pUO8+Ji?Yn?NBa_VHZGj=DkK>9UVpPMiV9It z>&@n7SY?b#Yj5prtJEHY>8Y{O+oap?2-+f+ZE-_*E|1zHQOl9!55x+p0zmpDSG5I^` z_cr>qhfec1(7=)Ui~QY_*mIk|QPQui{C%TPjvx16vAlbG01%5lpFU-N`hhKUKz{$L-R0-cpZ{5@z&}5+{|A2O9}1DbKslX%Cv-ESaT2l9m*kXeZcjVYu(>MIb6=F z%IWOLyWP$wZVOkTGW|=QEul|uoZ5Ytc<_|(lgiGi3+E5FC3LPm{C9=Mhr1i)2N@{UDDfh4%~=;7Jcus z2u`c(&GG0#;m*S+oE{jqya$gXA-g^dTK@czEqN~yT5*A5AMj&&I2s|VAOKBVWlV0J z%M%$AL~e9V3l0ZIW!k&k(x2IO*(wUa3@6SPX-+MD5Kx~}dOAiBJCF!~<7F)cw>50f zZ79(4U7Ff9gJA&nJua3rTm7~rH0CVC=55)6u%b?GT^MwoO52);pCKqO)hiijHLx7E zEnwYp?0eFpr*#wt2~*p+!6F$|4wo3qc*P}9(8RY(jWwP{*!6>@yb^d@%;-ImqtH6+#h&htA;R?o#_lj6}Y$jSg9fwpKmleQ52$^Fo z<4qZKvBAV>-*)0dE|`>U48Zj+S1Q$&)h44uQ44ZRq^_$m8?o(6=Wss6mE$8qH}a$i zXb`Qa6MC-Y+fM3em+ziZm009W$s>To22$!4;ML_1-LKIowU@pQ4MExmC;>jhA@faF zif9mMw=9FKS&EFc(E0hgJXgE*5eG~PC6V=9nd5q=kQZ+evEcKu-_a(%uuX6PubyvIh5veX;s6*(n1fN_1k?sqQDF zw_wQKUJo-7dI~ygvdW>s*Oi;0X|fSp@n@Pms*S8TB>>rT^@(7g@|XHlMUN@d=eGvE z4X*`_Bw3~zWxY>$=rwbMkg2?(kk|J1;+OA7#KKOk*=IZySwjt$7kyv8WVE>vD>l`! zAU(J^9r-|b=Pn9&rqJ{m>gq&PpZ3ltScX~;NBh(3gxph`OTB6Gn|Hn*cr+6$f_(I0 zHsKQF@!qUfNw^$UaEb|yDrzi1ZfIuQ`8uLEOlxYICgUrN#notYm*4bF_}o7^FStf6 z+WMnFvQljc4^_za!w)k|&p2>}Smr_s`G%WFW0-C@k8ZvVBC5d262nrE-c*(nRV^ns zf?nv>G=&|m0;T%z+fErQP;&W*z2oGQp$3f5Z<({+h*gQh^;JR@?=iF+%#ZmyE}KZk zJ~|RXZ4P{^#-GU}ERm>pEO>Mo_Iy9J9K$n7aXRj z9kK%-3zLz5BQjxgX5fRyOi|0W+_mW;$Mu@Ua(e@_vYCkF1;qy+XN<<}o!*BB>T$JN zP4B9UJZ3G_ye$ec8L4KDO{|}u+iVJQUY&Ffe_f{a6BE4ik-gLy@*|7$UDXSFgYrl% zR$1^^aNw3u=llb=bj7Z;+2G3NjwOpVLY18Zb~?CTitTMu+cRbFc4PirM{hGJF3?Wk z7U%3kI<#^1=~h!*2KToA&O|*~;P6xIq<4@(Co$A4Ah@pTPIiNR27^TL=@SbchV5)d zrE9$?xOK!MUN}T(i%fKBM?pmU6FpU{`Pc5nvE@p*yz9zX4z?#$bf*yR?6)oYr}8+( z9j0CFsRRq1f{j*U_K|j*$7fBYR+vg^3gz{^d5Ur$kjIJAF8L8wc>$CWNp;q<+Sgel zGJV5A<3;6ip)Mdtj)hpWNl(qd-kJtA6PCu z!TH#xSMka*n zHtCC>b%(*JUPN$$T?XvA$N7{~S-BmZ9IfJd*y%?>7D+U^e5ngnwQ~s{Pvb~#l4Ph> zL*VvA8C6?wr&C&iWc%==zV5!xh%`;Frw2}5JBEQt5IM_(CRE!Pbg6KmlXct9oqJ?ez8by0(dmT^VnMpZpZyqchL`G90$r(fhgdLG=O~|NMMq&hWFAIH|M45Vy(^t-#QP z6JwVnYnydFI^JiNet-1+P6Yd-VepP4=B9E!ZP-YPu%Qn>Eh z;PRh_l1A{peyg%Gjn|N^DbT0D`#52zYyO1>UVAPbKTC( z^*?_6TtEDE^NVPL4*(+p6e57EqNDTZ5DEZaJsQ@PHu*&?!5lcyMaQcF637f~9NjQV z5=6_8YNHb;GKh0gn;AeLLHZU}@Cp|m8@KPZq_|=m{bz|Zv>twiltAYJZ6#tD9Aa`6 zlSRQK!O7va*!T*PNbnwe9H^O!Ndb(+Iq+tY%xTF~uurDY9Bh^>Sk4nMsG^IQi$l5) zQ{)&SEPRNG#bm8`#{qEzV>NOcm=wzyt%i2d{f4_SsFBI~I(} z_62&jPhgY#JohY02`Rz)x!`aT<}mwkE)A%}7O6EL1_^?FglHrikzK0dLEo0e6S&id zZUhO|(jYq2z#f7in+ZONRpjp=>|DN(dHz#109TTKav~oWQl=?nJLO_i5`9NzvJ0!oOddyR!&AS&baW;XCWplN>yx3G=I(&6Lv@6N&<^!10dxR zX{qUWRl`OJb|+~YQfgY0$_CuDhwW>zVAb!> ztci5ABLj4So9P3;_6-?!bYfxDVab34dcxcJs#|}MH2Fh z!7lmbgeouvU7QGsAvhY*>E(&}(gnqG1;ypOdT#ek)c8OvL#e3N{RkreccYg6uOTEejBuJ&g!%W5tVtI+bR3v^pam zvXxzmX9|2=3$ke53SRO*xE}1E4B4T0S}PjzZMa1>S|1E*7O~VDN-VsZA^Lt2e2oLw zCJA)YKy-@0W=XU|+vGMrsJ=)Ci7E%UrWNXO+fH)lvS2h6DjF)f!N8 zcLGPDt2@FHOsZney!zHMP+}KTV7_x}W@Li6eH1c30m;Q;eAxmEB!RZfHu+)LC>QqX z25gdS+>Y(gk5z14HLShgE*uT$V=;Hs>-{IN-UIDHo-Q^ z!py++p4%e@3gi%eAei=gfjU6|p{QYD5Fzc>m)PcLec6Eax@ClA9r}$J8oM`9<0ys* zyUJzR4Yktfz+qZ;5*DLPh4j)fZY1y^=}eCcQkaCCVkXK%;qu@P#jXfty?StcJ($>2 zYJ%vx-_Yhz-%dh^Y2=sF&vw=b)SRp@-Z)WX)&Qno^g`kSjYTq^TGv zwYW?+^+A2UU2~m@P&u>nDXP;EHh9HkF#N*hE8h|7udvAlkC1hhSm>{> zz%@*;5Cz@iE>cMrXw6X5HL|b`Yc`@-8uiLPOOe(hW6F3yAm{AInyl#@u@4uVUqk^Z zOiVT*8o>iJDNFRTd(#EWnLv8PCOVv8Z#L=r{3@!^wey!$yY9 z^fG(Fane~ZrQKovtXBA0-J<$Z6BU+B&*7ICndD*hqKp9K(P9R0gru}U!KBeSJ0I*K z($I}dm}=I}x^hhO-ofmc0Ea1eXW8rNF@XKx=$_innf~jyIHS+R;Z^P!I!9N5hfd42 zn*zaxNP^CNYA8v7~r=C{F{KL=CfdC)ynXfYdV#GFOZAfJ99?by&_DilS9ZRbG* z$Xr;bC*l-WAPs^RL@Di=x5v(jdVijioB>(0A+nF=j@*Vi(zp&>E^iR(vb zr0aL1((afNpdzHjtQpswpSNN<0oOgZiykeieYyz+-CHWUX-tO7P;OVsxmE4J)(Fn- z<;?2{-mib;+W6>h%p=Gy_T1Sk_tkIT&wY3Io7cVGH0M0-gR74o2$eug$Tz16564bH zuDyG3`55JII2-wQ@)`VJlh0p;$W~^*v8Sb-xs|=;KdKLHDUJ>vZhO4E{saGX4G3`y zKH?q{=@AyaHzICdWWxV4`SeQ4K9rjOn|%5(O8Ml|Kd(CA7%Pa`6jFXR;$;7k+P;X| z{)p2TBI^bs>n=sIE=4yFC-vM(>b;wC;eOg>UfST&(QD6+a$fvP=J|gi@T6aR{ul6k z%@}?E{~+P~zZ7r&PYCL7^2smtHV2SWc&~p+KF1&g@k3Tfa?^O(KjzlUnQtNOFHzc4 z^|!h8&UyNgPQ71ye$TC!!~dfv>$cBA3F@yLHqF^~iMnLD-TfW=+li+FcTDknvd&V& z&ZYf|#yJ(GUT}p^KA#**%ANhWdw{)^ouT}`Vt@1Pu7J@kJ}k|3m{tJODqlm*ZM4JG zQTQPV_KN+gIbX4$!oO^V|2JqdfBboH{v2Qoz6Uqj0-l` z&v7ivf*{}O73p8{T6!qjsn$5HQrL=3gQU2qXbkZpgnNHkFN-x>@}^JA{D$b zvOa6{UIRvtPFQ`UKZS?DBLK7=PaN?vo(?U>H%_Z}lA}2olmY~qjkpDRs^20@XQ)l6 zjTGWP-j1Kt?a5nZXxxxh%!fJ*2j9q~x+i8L_xjrB=wy9tz-Vvn2-=$>d}W_c<;O?N z0J(AKJY8y-9mSEkLYo7Kz2$U&fs5M}%#l@#e71S5Tm+mJN9aL7OT8b$6=sjOb9z8A6kjJgtwP^cPRY29iWuad7?np{A! z)mD_nW@uy)C^q?dA6Z#A+KUGqvsB-z~9>gScKz3x*g&rJXtxvvxSs{bxdx>b339W?kQHzf@PT<=d4r zk$vAR+B34oNCg~~lWKp~dq3S+70@M6g9z0bI?^B1`0NTAGg7{q*Q#i?!N9aOWGmi) ziKzYX#fnTk>a?;{1WRMnkQ-3#a4tZ`LijxtP%$h4xjz`N_kIa0wgL*B9mBAjm-0lv zk(W&!y&P0bMoYLmGcr_p;PhAD4?cQ5Uh!SZUym@v&}4QMQMDL8V5OO2S#u078yseL z7OY*KQRF_z@*3)Suv**P2*4ov#Tz%D2(jUZ)Lu%s5@gQ>k2dOWd0J$uk)h7?!Pv5f z}`Nd z9k@TQx_A2oHDYq=&^e_O(0hftx%Sh?C-k*0YqjW~K$ZL5$gZ5`lh4K%j#rdoY8Ks9 zNC<`L@Z^}P2V!#_$FClwb$SYIkqBkfPw0i5Y6_UHex8BS<5UU}~C!c3ZU8(SU6>%1YRw zIsjp`G=~8fzBu_XWKF;N-n7H+8vUj4ShJhe&G!DwP;WXoSe=6{`LALWQ{FRWF=gms_G7n1D=;;J`~xeFTI+j)fjm6ANZu? zXp>NJJ4s$!KtBweD4ggA&dX$ay zGJV@}qx`Wh#7#BdV^>x!=BP@F(y(AMY?*L86{AN?rMz*CcDWF}{uB-hZfcKW8MEuM+D4HH(1f>jh{{nH?5w-kd4eZYSI0!(aJ|stiBg-mfsxgZSim_SS{-=Ky3H@RzBdQ z>a<12ld^AVwgLU2OcSF&ar4J<<=3r{X3uiKl8 zCNA_)WQrCIv19okqDoH6mQGlkO^Y9iDat8%@mTBO-e_)gT>sWstLRgAKP9E>QDAH3 z3E*A)DNI+}?@6W(!F`P?unl-?Db_05*4=19%i!S$(;YgW20CfC++gon0;X?6{s1iA zJO*8ryy~%UHjKP}V&LMt@PrG_z8*b&Vy}#rb8jdKjpSLOgt$-#%BYy#Y+&m5+Y zbrtp>kyqlhnTUOG4R~y=<%9*fezZtge7^12=34^h=UPH!L|=5vo~ozO%`|>$w+|-B zb3{hLCD-Kjl5pn4yTYf~7rVokMQvX=c)hsPxc{NAq`0|j)&tz}0uM!*M~QUpkKot! z?pXEC6DTHjSyEq(w13a;Cjmc(1=YUv-$2>hY(2yaDe!MnB;K2JIPF?in_8xtTP5pK zI$vJ3oviU>)S=-?a)E7~TFlStH)A78jm5`PcN28K-e}BPtGxNc{SU|U1)-bkVnf?s z+Unxw$~Hefb{Wi6PR|DoA{$Uu8zofwubgL{Ra*tH-x6YwIPv#4u@Gpd2I;fyInrkyi)^o$SPw91^U` zZ6eEg|83M&I-)op6v9>8I3cPZg?rKpyUxZG(M5kG;Pf*PBcv!n9_%6(jpI=UaRO<4 znf8nx?6H?la2tz-m~HG{kxb!2j~=I@i6q1j3v-r?*5RQKa75+va0>|#8igUQmtDpJ zi$pQRiK2Tr0!I`@omm2X^Af^U$u^~mylUh zg#@y|Bo__I1rm8GuKKV|eNkjT=V0S1ew>U#@W77!=t(+SorIDSfo~{egUQ)@?QouL z*|?$XdW1+K+pHgq_BPsI5r8>iV6$@>eO5(Okc7;%#uRV?`)Gj%7KRli(@sL)U<(|e z=bwA2(3e(>)M61}Gkt*mYB_urkK;1{G7y76=%I4X}@e(3M))N=;Aq z39LcY;}!O}!Zd~zkd$JQD`KTIL|BB_!j%6$Cr^ysOAFp(`7*BG54~+63Ym$J5rbW0 zq7DB)_U=3y>c(&U|LnVreP;$SM8=Yx#!jKJB%&HyDAiEbsF|^3H})km_9beBlBhwl zga&Q2T!YenHK?vu*YDlcb${;9egD4S`<&nXJ?D4s-}!z1n{#kD2gi8K^YwZ$AU*su zdHB63E}{oyaGi%S=74wcu%%?(^8zH27TiLCdke6wc$7E+lOmA@Sxv47sdbV!Fwy`; z;#F_svC?!Xu+iDvEO{Hc$j~e~2c!nL>Rtk?&XKhhU1Ts{=oKy&D5YEvHXRls)<0r> z=pbGYRF;iy!KWAmqWUOslmLE%Y%xk^d;p_w(hy*2IXoA6zpf5t_!Wh(nFOnJi`S8nyehc>~g-g5Jm$bR6Te?|CK02!v2WiE> z(qs%F!ciyN4P^`p&@lqYCH9`ZLX0Ncc3~YcOxxN3hF_!Z_^59jB*g9oiI-9_e-1(} z6Aa=BI4`btK&U|lH`B@`&1xKFngFflz^hTndVZCqS=!u&Uv)h{GlyRd;lTUnGyPr` zPiqy=%okOoN*p(rxUoXE1|8jx5ci;ArwvjL_^ABRsi!7T6q=E)<6}b%YW82Lk+S1_ zOi&ah$=5JBpZrY1jMbmEfI9`csMI3{_sOEmyFbT*3=SGJ7Wkc=SO0yuX3Y^*fA#P* zvr>nSjo?Ghb1-szWCzV8PZCUXM8)E<@`PgwA1qB@%efhct{0&1#ejmydtRm6mBsNeP!`Lsc#sc17ZeC^9(2&mChyJ^lz`4%6)Gsw8FUV#V3 z6yl?6z)&8$c$lH)peYYw#vfIyYp0ns-nZOHN`!U?H^(0$VEatt`qUH5@g1Lxj5(N5 zfnEN*`a?$_EXK6+GA>08_$KC~UL4wRuKXNanY0j-O2=Il!n-)Q^Fl}Cxid&Myq^QF zD1i_1jq3^M3&pl3bg->jC?yeOz{ci{7sA9?ZFHzMzxZ}#qd%r&7p7?@5&GN@KPlhT zU@B$+h7XYQf}GP&>_rZwAhxDieL**zlJ{|zXn9_D!C>&h!Q&U)J{k=KUUc>}3Fn3p zqV!jyL0TM)aIKjh9NE8YaVlafC9&1H3!4yXpl;rF@MEI(4izd5>&5rhT|l6CaK&)s zRX)U+hvDJjY80&9YdIEZ1Iz$kEoi3fBgjX$ZGSUdxG-O|mxD1g!_YY$ehXFB*-a)q z)ZV>Kogid&Z}qk>UDcc}%biWKLb4kW^b^vMLL$K$ADIN>OxT`Z#tloIKYJvo+@GnT995!(*hj)!4E<8lR{?PM5+JytYH zjh%$L3c#%Xab+IN5;RepLyegPr*WX|Nn$ZMz~2-|^WzEl?on$xOq6_E+GBJR4d%eU z-G`&1Dc~J^Xk5QoY(I1-6i86kMib?R&PR0d;;7a$fJIrR`X z4J!dD(xJ~HCkF#Yjd(Cc;nZu_UE}?e^Zny7{ZJPQ_*2rI_uiup444FUT9P<<6L$wP z;2!fBY{G@2ti*Z(P)a<$$UgV_fC*W*nb!UZrRtfd-s84-7*Rn+cJJKl2N0qA z{saGaB|I2v2b7T~6W#vXJ_z98{^aamKKK&{v#Ss0)EvyO|LKE8Er&|Z(`)-rw2sD{ zACK>xO}zZ@Zx9?8F z0YnHaP#NQ;|0<^cS45~b1SQxR$U5`ufvv8;);8_TBRUjH`U)HW-kvQH1spYa?1THEw-akgpmuTu?E=MBM@zb=+-8pJpNMCgCvQ1$idomaNr>GgZ} z`pNApYWpjbp;+a}kD5Nbot-y>5W4dXxT9r#oR2w$T3!e{Wn2_i)H$_{B&J;b;o`%_ zgE_k)OddwZkGOEi*x5zuS$>U3^rs|HclhH0k)Vo?kmX2);q0RQ=jQ3Yh^t5Fj*^cN znf|9k_FNI$ySVyf=IX|u*6~R>de5bA*Mvn|2aS7#Xj9FLxqqs{4PX9Ttk29K#hg76g(&IqVeU?R& z1XSuxIU~M~q2rN1;ifdEz=1GsaHm~7JKTWqaFS5D2v#!^KS$ePcn6TXT~$X6CUuFu zf(gS(UvXvuxot@xOIRfCEA~3Kc$+wAnUalrTk(AViFoQ4uPUNo5Jwbg2%JK#dajY3`VDcn;!=oAXY-JuqFU<&q zAfGjsk$2uO`igGhs58!>wt5+Hk%o2EEElL3Z&l8DWq?jWbh06~uymZ^2)Al}XsDAL zS!g5D_kHG3Lj70B3iyPhMMr&G4jstjy8Z5-y*Fc~SrY$DbqI{_jYdIQO@r9z9!71e%bi%+PyCdQ$AuN&L zx6-z{F^p86_P_-L)-8<8NuF$9H!x&-2?j9;I~ks{o`i&{)`~nnB?p`XKAh8O!yIEV z`}TL9m1c?O34-6jwFB`DhS6*%HRo$u9rP;q--6mFs?)sN7m5|pYFdPpAFCVS zo=g$7`G}JWo^fjIp`0yS@AfNKBQx9e;{^*Mxp$ov&d2svo4xC>$?*~?J8tXX#M;XG zfS8S0=-aY?fXOcNpXov+G0?rTI-eJ=bpMDA`{<6FmG2Gczbe9f_@eK;N`5=$4K?;4 zYisBu-tjeKhG2Y)Z22j}E52_cq3adz4&sFxq@!j-1o+U2l_zDqFP-*q1GK57AqUnArgCSha>!)t7s$T&ND zj@ejBiFPE20`6-uM9IRp9^x}>)KkQXoSQ$>NB>C5Ng4`~7p|WbDmNsvVer{+rM({O zZl86~Ob36*u>hm# znscyw2rklB4Lyz=d;$bLBjGvf)(oKR&!)QqG<1#oKR<$R51gj zS)Pj32~W>yJrv#RRi|#xOk)2J&I#E21|9)2Wdt0~ze>|2tm17O8SRoSTSH+rDl_G)_C~AolC$ zKoW9}-5#@pey8R@X2L~=0V2FEL{1BBF> zjV6mwLd17f*|^yfYax0qlr--uRXmXaFDK3P%&!h@nowxRryNY#_C3g&=(?5_cIJ{^ zM|%nFUrTOL8ch^EDx`NFi)t`ADO5qWlIQ>l4RnX(HRCy8nzoDl0k9M6^gJj7`1Q7=4z~R-y1Dy zXaXaPHfEDL`w5r3M#FuX-IGLGmTuZOvUjNYvR>Mj$VF_h>9G7$e5QFZK{OdBVJ$X%QTb`V$7_B~ctQ3E-QM9xu9MZ*u;L?0%iw-IuG#K~TvL)#n0ald z9nP*}#yY0^taT1>@ca?+%|+toK*xg|kBIEZXKHF@F58&1C&b2{X;^$2f0+@Tdx!K~ zYyI2=wB6G!O{%6pOzSc0)aO<0D9dM>HFr}zH`eqDELThph)o^eShwy3i13Ek9e@bO z5EyHZJ6|))uYasRZS{7$-naQ<*FRmpVfp*(PbGkMuT@B3rq_Ocu$+r^Lo`K97{F(-NJ@h#B|~5Mvdw{nOs1^(PXExO# zdj&waM*^zFjfh!wAFe?v2iqNk$r;3z(?PoQkc$FbI|EWiCx(F1lrl~uZPIEo@P55% z&qrNnI+30AR!9n_nrD)84`(d&tshal#5To;qY_JSdBW3@L6|!{1@0QAS%|yAho9}m zb%#Uy9?A}&wq66}#BP8Nf18iKA+SEc!xoJoDr+%L zOL8%d1WynX2p36psyah?WEOJ8#;|Ti7hi|@T&wb3%y+mkF|1MU4Bn-UZy<@(ILWs0 zCgVD&P)(pbB%v#SYLShVQNwYmPz3^<$3fp-!&plMw*%E80oMUki!==X(}vt1aTu~E zwwVrwupufCksdnO3x$5vT(m8-Xc~#;Q+Hh8!wK=~#-QS9ry|RQqUMKb$6ClUj_6SW z^367!A02eX6Dr3?w}Zg?Ow{$ivc+MxQtZ;{?KbFfHsZ^3Ss+^!IanIrTG~2{JNl(0 z(5!T!-stir+tUdu-b4EwVUPxbngSWSA%mc?O~Tanp>L9gZM6%cwOcP~pAGVQG@LKa zst`nLCS20o`<(HyUap#f)TLoA^62HT(Ojg4j@m4ZM$`yyx<^PhxuOb>xrpnnN5PpIZhS2`adh z8e>k)En7p@yxfljkT;)j!oY|?gg);D_t12YqAKNw^$|AYp;EbBUHafTg%{5&zGXzc zM{s@%)@zPnk9?Fke-(0#htLsXE&vnEcx?Jd=a4T3K(*K-jK09O8L=y^3Pj#m4?*(L zd>=%_W~)nFq<++C87kJv45!5|vOufte_eg=Rg!Ij`u;?4g`j9p#<>H1$^P0+$NG|^ zyKoqy8J`!=+h#O0O9u0jRUB_?PTAXXcXk9;o8TCK2MK&{mkJX_v_Nb!3&L&Oi|wJc^~`@^E|{0>la)xJ z{tOnklrtHXW|fRBUE1geC8dLGqzS1ld!dCDd?lgcik8I{FAleEvvsNk(R&DTgQlxS z!(5(+lrkV9c+A-bt3nVKVn;`JQwWA$iqCAy&tEmf;_~^RgWN#*;!95o@D61PC#kqJ z3PgSmq|FDJ$Kf9O;*PpN+J(424$`ka*c&en6yTG5R2$po-K|>ZgV2Fj-H(tZd_kk< z##kd_rYH)d*7%{(nAhl(P+fUKJ@7D;5DuNd_dqBE_p>jHcb+=JR=tAIE#Wc<@h{hI{5af-moUNNM82Qp1qz)8L8zzVkT!hG z{T6)>KK72Ff$_qzH@t;N^fSggBZY&+BwxrSObz|g@56nUvK)*mrFIs;Tk$MD9oIsI zAUcu9J|!zUpL(Bp>VZ>|Nski$DpNn|)b6O$)SR2g!+oxF-@Lcl?ye~QL}ujqgAwmY zalxJuVr|l-rqmxY$#XKdLXw8ZZr*AzxkY!~6>T{x^d60!bdRqZec3&lieib za*Q?UmKrej@PrhaJC@T<&9F>bA0Es3K2|;oDoq;CZpV)?aK-%z6;=4NkH^cejHz%? z?tCzs1_iDqxNuQBX?Uyxg&~p6jBurTd+rhwF z(O}Z}Nkj=~Cl8gwhao%1i*oRiRG6h;5?V68lQM-6n|P3fw%Ggl*LuppQPXI8Ca zyh>*d8Ur<<%%PccmQ!=ax$b|bglG^6q>}|Wq1Vr~g#fes7emzlX?FTQdEkHB0DtM< z|5x%pfb9NN_5B}m-2d1Q|LKGvDx>b_waow3+QP?UQHx9{1}6U2`O?qpPd}Zo@YyIE z>i_piS5vL)k`=#Bx?-$)4Of1hbbZZz>D=_w2^VIy?w$ATI+PY3bN<0aT5YVz&y?_N z@SV1Ow_}4BF4LDMc7Og9jI-l7$Jk2LzI77IbZngO(p((H20JtZ2n^dJ4eT|vXbn?SIl&MnGXmaJ zwK`5>$QIrLYc$AUWIkv~FRCCTxlW{d?L3PrKkjDGdPEcbbI=cny(`5af+REg1TCd^4C$g zf=;wk@m{w~wdy_QL4#ls90UP5m9tua$P!X&B+p33^N?q!vI_Lh=+Nr zg0lQ@`UO!H=9UYJH>NCJfon#t9l6r5o0B1-LIh>BFapSDFOGis>Woscp=U^U8Q7*p zOPp?iXWx)_4oMz9`c}D5Sp@>_^0jhGRiB$dA_ypI!e03dDal&WPW~VB{S6{l%cn?W z16F#&)U^OENI^1CxiAkUR57dPu8Z63T6#By3}7QkDH)7IH@&}p^FN|+|Amp3_L)he zlFBTd$Pyjk?+qe9(xztP=)l5T{~lR|ScY^)#(5jHl|xoWc}U4*qlkMG-^9dnAD-_S zeK7M}p^-6HUXIAu@mMA1CEE`wrna}AxT)vm`i{V?w@P>(49#8>3^)Dw)D`ncM10p> zvB7cKve3(;ai?}=!8OcHd&|*9<(&r=Grb8$-BLzJyVH@*QD}UMadi4)psXC1*@W6S zmUi1j^h6L!Ff)e6(r3^ZsfIxaWWSC9IN6MApRUbDS@yoh7BObO&8A;5os8HIo@9>* z@k}Xu)i{-Tr6-A+rN7CgnH!nflbmbO1{qrt)Lv6HVGYQ8Q(F+ZvJNB|5mKPL^<>`i z{&=I>GQ%XN@C_9++s${OXc_xlPet=#jLnR#TcAL+_VZ0SNI^Q{CfG4I1018sIpZN* z1gYX-(mmz+BM&#H*Yj?Qn%*7Jd(Cml1zOqhJ;s|HpX)+--ojaq$O&DVUTMnJlZ zWut(#g{~oXn_9;l%?k_iazcnit??aS3kpjo)sgzGX~{4D9Wu%^T9Z`@i^czfShPtw5mM)FDV$w2v{{O+WO@R!`7UA=?g z2Wl&KrR~bkY<4@&5Odw8BJI|tBvCfrkcZJIKxReJM2Ign+({!+BAF|OwKD-G#}HKy z$Vj!ejjJzA_K8m?m{IWI^r!uI6C`4-VMpqSdEqOs6a+fW+eDuHMtJ#g{2*6}Aj~`50SKxm!s~ zM}Vmuqo!prGBtHTNMDMpx(^;~-SRfWqUUb;grtsp0>-;ewWRe{Z1?V-*Sk9>Hheol zXV35ZIB}#*%?}mC8#gvd4Rkd)oFLai%=3dce~#{@TzM`5}N- z;E4l2&YK+UJ4}AB;aY>agFTj5KmEHi88xoNh?XZ*E#k|#N&yfw?Y`oM>?htC0v99} zmT@?D+sBHrlGS00r;qQNJJk?@#fC7AC;jea=4xG=4yr63&zp-3f3%2=yXNMKXzU+E z?_2@72~y?BbcSR%vAXz^@@LdG>z6M1Zujhlq!k%Wnnprq_9A$*WGxf%VNjfLft~B8 zp62E&-4&JnGAVAs8tGPvTs?c>$M0n~etdj?8ZRjb2eKL1(=VWl2u`=A?;zIEd}0?c zKl~<|fzP8#1T!=m;~oyb@6|{*Y+n@7nfX2Lyi@MEqG2ZeaRx$&I@N7{wsXV?2*82o9*-?#Nc?fL$fjwWWaMMonl4S&2E zhAZD~b`Nazs(!umMxTDyg_ywT>KX)z2jZxLz&H*F@v_hL`qP4ZJVQJ5^tA=Qq1e16 zbr4+!^z_AbaZ8Q2$atayNih({RF&8g`r8+9x*+3tSRnY68AIG?HFK-)5Sn#p2|oj}5hF37Jk}y8OnCaeK0KFzN#{kNC~!A%H9Z=6 zpAZ!=g7kr6j?h6w_Wo1?hRFj*vQ?}&`xZ1okHf@%lQ|`(f6CnEWSX5)0_WJa7_S<( zm&*s@8EVM-CC~w?XEhmtrK+&G$a<>$IU7_G8B54N_& zN9AMXN{?ogUO^Q}SB~xefJ6l!wzq|EEYCs82`Ei27$CkdvNwtjjGyV1QBj&?@D?ii zm_R#~14okgF+or=9nIpyH*t|vLp_v7Y!aTB#Y2h`KuUN5uu@r+ihuMVIdo4pw>&#^ z4?>KEPN6|Nnhh_Wfm}dkUkb;QOvMn-4Rs0;Ra{tp8rp!XwJrgU#-sX8^a9CfG6Q7J zKm%iL`A$j^e6%g!@8lq84}Qy8)x2lnc^{d1R%2S4yxgY`!^-NlzK-QTWU4WqQzY3) z2?KD3Fy5REeLbd{Rj4tytdX2f${Hf&LA17|pOJ!SMn~uG9opMdRT_E=nO3 zn!y3Dt`m0hHqdHwvN{y>VQ(d)WoS7Ks0#&AqX{5-rs8P25pumW%4IWT#asiQF2O-v zT_g8>3Rw2snjC9Ue$`h&PCp^uBF;?r9t5Et5u*}Sr~*PefFP-4lsFrn&bIQp3;G5} zcydq@Li6Q%_yaM;*B?VyLkpu= zL$Z$7^3^A%mGwh)p~>ROtZ^=kU|0b_IwtE22Bxy zl=wLmbJjq3gU{9KCz&O`Rp8rA8eGjz>d?>!%TK-eVF)hNJb5o)Zn!aJ`!)1l5hHLta)R?2CWcU zs8eU7)MmDM9Fi}DRCZOmFhHjCL{9>mK!>EU(YiEnF1{*b5su@*t2oE_MNpNQb6xdd zLPm+FTw~W5(iPOWC!*w1B7P8v8TnPN)YyxAvj@Jm*=RS0nVk&apmz&Ey>W?q6G6xL zTCC%UlMZ>M1bAOb8Q!lcTCHRGv*Z2OCU>;AJpRz6Ab!Ee6HK6>uSSD`J7h#ILXiuK zrIxHmBhsjZCS$96%lT@-)-UZ;KJ6u_P|+b=&~^qobD~xRp5bGn=gmRy=SAc$gRNw< zs%UAI!uW^MsCpsF3uO4{9%OwDRt>@g36aatgGh44pxK7ubbX?$7IVojb}F;F*SG7T zS@pFxl(9uNgwWMMKHna;?Hs3sa=zNj{3MD7!x&&nX>h6YhbwKt@obrM9E5*^z50ou z2impQhiY6Kdn$Q7r|f!svU|QySRLGn*>gM1oNQmphVS(PpC;HDsk;}FF|Xlvv4>MW zC4g-0?YGiF+UzT$Yp`W7>I9p+SZalRQ>$XHWFSCZaKcpak#a&YZ$jYVvgCx(xa1cn z+zN`vy5l@OVwBYrK)`V%+Mu*9PCFo+ zF0f6-sYxq37Z8RdRn6N!Q~8uMC>BhYCGvx?Ir3)OD1pEhoCY#4=L_&eG+y34o@~mo zO$EWXHPCE|N3u5IwYIuaAvSgu@;F-^7e0ZB6FE0sT*-cIixKa3w0gL^bqFbWGrIaE zbrFUVG2wuS44p@9a5Rm^r&3T-js+SF&UlD{nYM=xxc{<_7m)K0I>7@7ufyl;K=`2N&A243;PxJp(x<2{~g$M);pyl zZL=&n3;`uY<=^iy9#zx=2#;lO{PpAUpI|2zk=`94zWEo}ReF=g&ze^3s0Qwg+~e40 ziW?>N`~4PmgaU!VZ8|-Mm4rc;`>P_T88}Z zpys|;;0&I<;MYr(x@UZ&;q2AO(oRv9X0KmVcU=}a#g8pdm6Z*=>h?oGU%D=~!IE}c zIPv~>OAWIty_P=`UuNuO*K=Gn=&AfJ#>Q!bwhsY5GFmx0Hd~r zLmpB;c4BSR*@NQgM57F7vFzqz?k1=^p)C_uM%DLHCM`H}r|3M2Er9QvZMGp0Ike%3*Y? zAJ{F?5xheNi|Ry+BXt7No3M--6he(jK+8jJtpbl|OGtQ9`akKOeeK`6S6kOu&cbKP zY+#>JUGT^+K6{<}q!PpHNHk$EzwU%YoST=i-k1(yEIh6z1D8-jEp8JAJ0 zJJJv*n5HFb1U!ANVx=-C^4e<^nMm%KQcPt`SX^RWs%=f4FT)m^!eBB~f)yK>s+M;X z8IorjGKPK;-=3gHA=ihF2+Y3wy@@0Kc&Prg!L|CsY~qenD>iw1-rc*0wz}kS0vTd*^hM+9H^Wzm z!_15!d7~xr;?d)+s%lYZZoR)%FE%qay2GiXX7J?M?`K|r%4;4}3s>QHC6K6OB+>gf z%OSAe`~LTY^G?bOHrEgHOs5G7xfjB<5qG+eoQvD0=VnhgaE$DFw@4|vyq*gz0(?oX zr)rd^$|+Kqrs{Q&BWh0`(aUrN&Wqv{)7@&*?CY#rpl$lhVhjbJzpg}a`aTDKWsrWx zv5E!`cxf-M%|=KQfVqlt(5q+~T4ggtbnfer`b){`RYe$dBR>NQKj>@sLJ1-&=4+!q zEmJtvaWVP#BKLh&wkdY0EXhjX!jMq&vOm04LSq&Rdn=XVn4>Dm!ZTCDn<q zt?B5Aw5k=?u)X}&>Daw)<*8B4mRis|mnPTGg>M9ETK_R$oOmI<29&GnHg-wAIe@W= z_B~ZWQDA>ARntVqbL?R0^6)^y3NYG>dxH<*r7l+&uTV zthq)sGEu=Q<785n@<-V27UGD6?W`B1))nO*ZNSWM4pEiNO}E*^;xqo{!O}0D<;En{ z?eW6mlZpSvgOW9<;hekaPLfGW7>-cCm~ z9VE7RNqg#M$MDRZ`ym@e9a(9EKdI1}t@w3C%`O1?ewRm{?{tB$` zkZNfsyRm(BpXlD%y3!tQqwdkbKl~TvA3UGl%|u*0zwiC)4u``AN{d-jG(z@*>Q)dr zFTpj07$_?i&B)aFo*!;u!JBcMWjS|q;HLyB(%Mv%QRCoYB%h*wj(>Oi_uH0F>mi>> zwF5&HFG4nNG8mk`d#Nnb`5;LEor*Lzsv0a0vxds6RUv}wiMdH;105T4wmFbikB%!G z=mB+W-k`UEE`m4HmU#C*53DsPUe!DFfMl|*fWpIjey))GPEE1P*mcDEL|ge_I;*2_ zI=>IBs5KqiboteLdZxePEBPxE)07L$mlhqlI%f_}*3M>De>+rla&w>gaHW{KEbKiM zZ~fqie(a15Z_`$0s_=-ZI_)7;g~fx|R}rtC{-FHuMAtQ!H}huhv9DHp!!8;ES55BN zWqfEcIaC$;p3v51eDQRcpm|^G)J2~I?I~ZaBeyXn4)H)3Pd=h#p>(Hc4xx=COEI*+ z!;qo?XeemW1bJj!KO+5aXt-1OXn|3*MJsY>K-kgJxMA_~%8!rvC&Fwg=w0PQQkR8k z`s#HeQO+GoZQmB{B+()gcMa9G^`A;t{o(Y+s!)5sheR;#<&7-_C-bMrio#sgFh^ZI}( z=#kqmobKG71j0VaSC+3mE`GgNbp7LnKP}$|-}o{=x3PBl#*b&mFMbFuxw_C$-*RBz-Ut^;kOE zo3ag0h2`6xHdvIDTunj+LeEh!p+fP`d&Jyr;8k2YmIE#1AVoORpFEYa*l^i+ueNSY zmqzQGMfd=ooOL7KqFme_X&5dWK0SlNu;BUxiTdlVpGj{S3FBxEDb$~P!i(R zOvP>&KuQ?Ks7^=`Wy_npo;!LJKSrlW7pLG$#9TK=G$K#=6D4)%C;}B$%z!?GBan1N zk}1)PkE)}ga#$d5yynL4HHhvUJRjtrB$Vaw5lB45&Jk6IM=A0UinCC?UgnoFyqBq% zPX->C0A49e)OCPgT=q}k#-zpgW;&d_O*xEZV)t27T)EXeZ|zMhNhOo2%{P1ULJEm;{AW3Bb5xGW4#dVz*9dhRHs4r zh+cvicd1H8gY<=}`Yh0!a5WEs&WuL>X`adkm;iw&NB7DaLlId3J%&ex+rlO9{+m4V z&V*6?e#)az|KH`2tou_Qc|rdyk9_G3@#ZmP0|SypaUt=+n{6>!LJ*1w@_LzM=BH}> z1vKrHR!&A43z){y_)vVB(Rfi3typm^?TJT-xWthN9(pHze>njPoC4Ow|I|kjviLvr z@zJ8@pH31#^^tm@0MJJ+E#hkFkpy!NzAVV4^k8Om>iQOqR7mJml>pX0TYGQx{pbZ>i4IIt3xISTKyuiE1L!0}sijXc{w6 z@|P2(V>m>sh(1!T=Y8{@@~36OuRgY)T|y}4*TuC`MQD4H9ix|%sHQ%s^F ztTr+svHVm@W)TTgOq5M~QgPaqhn(7jc&P<4rc@z4vKHr)YRjvwt|qs%;I9suUtJ6S z@W+we_vLhjsE0nG*9%m>+31T8*Q7ESrL}gt{@(N03EiWNi=up-&_lAocf?UhDY!SxeOGeV;u5`{F-mBfaAV z3Mz#F_M@RrvYd5g4QT{;78#7>=}9)gzs^v0@hx06s`&s(@;O$^Mh4?dfFzMjv=aNY zMTFWx`5GI!w1^#;gI_^MDJ2+YT2LG6P-4@g2Re3z_~Mk@w{r*2%PMaGDoL6!u3(r{ zk)b_>l;j|b(gfl&Pt@g^h{z8uCk-H(U)8fG%+uuLE8AKO8Le)TmYQS~hhcZK-q?M& zM`M*r) z+vJwh<*y1|g+Xm?!WG zshr1znuVWKNIos}h(;}Yhk1ib>!vvawC#ze*I;~fR&M#UVTj0ED5lnwK%=0HgN(ZI+Uf*2oP? zoqalF&`kW3TnUNRR3DGGGHY`CG7z@o`qG;szx&0ZhkMR3Am?b%FES` zN157VlQSpKwS||LetQE`^gV51mS+au$@Q?v| z=2SF^hW6taNePi2aoIDq5CAXjeH=C4#>K&g>bXux&X7(HWEYLVpddT3=eItp%zYsV z$fb(&Uvf#ud(#j6_8@alXWFY#aluXRIlp8h<|!ZMXKtQ^iPxtAf~e?^_OD; z!>wLnk-&uy|5%oPd@3-M3TP&c`Ey+zFpCNdq5`J*YZf*BBL634cHK+tx}SXQH()v4 zsez@_e~qI8p!tt;9ROkiYv6#R7rVWl6#%wAD%U z#lbYZ3`huAJ$e&V^qdg1q-q0`La9rd7Qk-7<;@ZU@U$TSE?pAI75WaDn^YVXNxn3@ z)N*K8G~qM5VF)R2zXNxukYrnr7RjLT*srL?s4{Iu2H5r<+_w7EZfBdJ2VCD#X-WFv z;Cxd=t+Wx|7Ho$nt?hkA?GChU*zrTdl2k@x;*Q>vclGFfzLT>_PSmxW$MPHrQqzv) z6V_~}IW|bxUzb~OKMk`8FigLH8RowX^PhjP|Nr>-mtm5q|CM2mrT~VCYh@5@{@E}O zDVqHU!^B5qbtg#tZJ1gA!7$x3EB`jkKyxe3$B>7BVYcO(UVcltaQNrtmL6Si5d9S} zoV=%LUrWL_jIyij@^5u%5?l7a6$*W7NkOs@hc<~yR1~ouUl8tHX?4nKs@xCC$Ujz{I1#*vw}=;P24YLh57il*05Ars#esQ7|xQrOW09wRFi6O21+8( z;hOcX2E)!TI%s30dh#U*M$L82QS)?;BwRfvwQgK20G0t^qAZzcEzYFaAXsYuXQz<- zoLRICD#IQ))0Q*@J%*Ic;IQtYU-0B=av&MR)(Rnw6d)_(DQQ}`wuCsDYOrIoHQ!!d zv7GG5M5W|FCge9!GM81P)`SgZ{;}wr>NH=29cmh5XQY$kx7mA1?*D0+q@RXK`j3XW ziHaXplg(jdsz|<-n~=h_EM_W;p$c4pQ@L3`3&p{uMoA7(C~o|fC^kUiK!=E{;iS5( zAhI$h&}e7~;>{?OS2?1?vlV}l3HuE!CR>PvZ%SgKu~UevC=|(_ic%D-5{|{*X^K#+ zkz%iCr1PpV2EuIe; zW{xdf$Dr=GP8I2*s~ zwcCz+J57EVsZBK{;GgX|S+dO*NfVAq*M*)L0dwkCF7o>_8{n$fBlsD^>DNU5KtydDH94 z+4x;k8ZSH)-glSPW}mya4})@mX$DXFJW_^b}nqZNGfWn}7^5-cwdNnzq8dEkW>BNWe0&7BL{dO}r1${C8q3(_21 zY3gR&o2;J&BlQh-qo`#LJs#X=8nQ#(ECA2{D`5;uu0F2vaw)7N(Iy>n1_jqLvPqX3 z5IuTZ!BdmkfP6Qv{VQRdF-1T|nUaNPVb~LJH}4gkN~8iNkBUaw48h0O6$G0Ijs~`j zXxYzpBK~gV}vOsiL>LjPMHEw)oCtqBD0M|s`UUxfCe!W~$OT(aS+%=8VRl&DkF|2o) zqPYXZ$^FlP;p8d)5nwnuXIk%y_;hsQ@v4LR&E_Lol2`7ARSR7wO@QI#tCL;lK5z6( zm^T;QIW_(K+y*XuH!$bI+M32exRI#sq+%>TFsFu|0<|11628@-qrtfj4>YW&=(h~z zpODfXm$BcIKGb~Eah~49fk-|Z3{&*ALy`I<3i9yvf>)btPsLo!{@T=xs@1Y}#oSFX z4Kq`M1H>Xoz0UeBV+Pz|kG#txsMjlxpd!OeP17uS$Ox|O)m zzK{1lxkWWf?A<=^{#DC966d*zXbjT3t_zm1?d7y7rXeJqvpmf*U^qn(d{aC&U}>)s zoy+q@;UN-O?Rgt(VjBEzl#Qh-MB^M6q)1~L75Lh~K2v)rOWL7w3%l#2pI@bkocA5w zasSfSPwnAu=l4Fq{C2tTV+TF=jn#|8k5=x7YQ+ui+&}!z-!11X{?E{zhe-S()vbiT zIP=!uoO$=9l@-=y_CIiDMiZDt1Hwl)5d~{)RKECAi>h(&`%BPn9XE23AM;Q;#QJe$ zqii1GPCwGg!%qDg>`7plpmF@Anvt>&goAy-LXWT^gsflN;X(5)d>mi#n zfVpp$#2(by=WQC|C|O&~Ugwm*IJ5Y@$&IC{i|OG$&#T^YD-Yfjl(IFB=jv|D^bc6R z9A`aNZJy-OQuUNv7O2&?HdT|od2dOC`CX~eJ2`|w0?Wi&GMpB><1@j92!!-~mJS0!xRT2_AW@;t^u5Vh$AH z+I#O=4EBhU3lqJ$`+K{dN(j-$!}0j$=z`p&5cL=K@+yO4*>cCyAb+bS!BBOZ1JY*q*As~jgM`u$WElnS0|h$>Nq>eV zipYvRF2frO&<#|?R(%G9?fMfo=~4fHP243f05&(jRF;0ZyQUi-z(uBe_#mTFZ+?vgTg=vwQavTN)pgfMn`Yf0~rDx<>e7%G5A+U z8S-~WDIi+$Q}#Qjqs7w2SJTje>EWh~LnKdciiJ@DGMa3g2M3Cry#Q>|H01%0FlZ=)>8n|$*RHok?1+&y*cer^t3E|$T>%C5L&}f`@h(G&!DCoeQh_r zgP}?{2^}e+NJj(GMGQr{8hTYjM+6MLs-brby@*N|5H%njREi=hYQTbBKvZlz=a0J9 zde%Pgd)|G{oH_eFGyC_656K5Inat$MeP4GL$gfrN3=OG63&Do~O2tHIHqF_DYH
      zedQWuC-vKT+0U}!;ty!A&}EI}@blr4!660}g; z0-H1llD3tsl-DrW$3`QcY5~E@J#_q$-)xg62Cz-o8u;uc^B3G?E8Cc;1=fo`OogCMyd+&@mGFpLLtx$DB!~oq0O1ZH`=JOL3ufBr3#{_*X+?M+#{Xv?; z4syhjLKgL1y_3FH2`9}@^5UD zK5HIs@rP}if6I1Z?=Yy@>0PSnkit3HEZHzrB zA|3S&w0Vrc!Vp0|P8D+0dhMC|m&=OhnD_!d>E4teLLM@(g~_V{DQ0aE&i(hqzw+*@ zQgsG$m)iz4a2iclW*cqR8YOLtYx^53bfL}BC${h=XYUwaCnh#B3f6VtzUT04?_(m1SMYP_?~pwRNbD42cnp3Ijy>;dazJE z*%!NCMynWEqQi6Q#!B=qcuGuv=I?97E3TlP=m=7OK~AZEhnxp!Vl}_>m{XZNBNvrB z5zB_V-&r!&0gV07+lr7$JyBsc21D9ZcoWWL2Gko{oZDw{IZ(6(YRNL{hnG;{-1I~# zRO$6K!~ieDmvt!GA8os0G6XIa%M3hwr6}j2>(AkC#i`QaMV-8-Jt9leKqQj`L^4@r zePbH4jfhjbLfH<|1?Wb3583CHs;f0D8M3&TtHC>kD zs^q`RGbcW-oKeUxc+0O%F3*pC%7TNjm$J?|JR!&z7S7+c^UugcngG#k`V zHr^1VQrmpuUCnsY!gymnsKafdL+p286Asb?DV73Y?LTMzb8+7N&tZ_iTmSwulOTS+ zCP9Iw$Bvo-2Egzz%l`-S$R|(6#>B+M#-92=kA!&tS_t{Q4f6L@5Fk4Gk1>z~iHSD9 zk!xXrGms&@+~o^U*L(LO?%$7i_%Q0xLKHBS%;7{YEky%Pz!xuKey2+R=QZQZjEt5JlbMs9otvARmzP(NpI=y5SX5M0TwGjQQc_k{R$g9SQC@zI#j32V ztg5Q2uBxi3uCA@AsjaQ8tE+4Hb&ZXUP3O-ypFe-0x%t9{3l}e5ywuXt+S0N`{5m>1 zJ36|$x-MV7+}+*X)6>)2+k558mA<~dt5>gHyLRn5fRFzv2LuT6Z+!gYN6LR&0{`=f z@Q?M6mij+!^S1t%S^p+rPZ7!w<*>&JZxKG9&Qo#)tba9&IBxT#Uh_ZJzmVp9g?I{% z>@Krq{j=QuZ`Qv8VN>YSx8nb@{zVpPJIHUG;UD22>$)b{G4S+C^=9N>u`AkQzT-+I zX1B|F{9n#JRm@!t%~3%mF|!}c-8={kgGhzk)Vfu+$_I(Jf^utJqzo4;+<(c-N1R~K z?B8`ld*IwDkW4o%QBQyV5jTX_k7hm_551*Hzk{Hq!zJ*CVO2pNZz}hvMeHR%345C6 zlhlAZlcYF5b5m<-aCJ89yj5rxT76@UORLTpUz5?B6bT~EsJ?n8cFT(Qrq<}fRE6^g z6SL9WJs12T#0AO>MSSjMFlEPd%QRbRv<7dtWA;uwd;4eu?~+DO-jt74x;`~Svy)=V zB_4^;^{Z4PHOW;(uf6gOt&D$0sz3Qz_^|Y7%ru+0kQRYe*yM_79q}cUo815l!uIY% z^O&pM+}>gBYlZ8Ev}eF|<*Ln(zZ{zvHa~{hr^q$?G68}e9u4tv_IFwKDLufOGl@o0 zI1^H+XG`gZJNcg$8@`I>wcRn^Ou3R8(Bs??K{iE`=RgCW6=@r)wVas=!E3n zy+@%tS_Zi3Z+(nZ8GzhR`QrP80q&B!4;015M`={}0C`{~ER|uU7KZcQk|}8Sze)FiW4j07i=CTv*h( zK)Qod-=ERYo5sa`=gPr%;+PKl(+S+S2fc}(TW7TbqCjlRG!M=}*4WppH zfHe<*Q29!p`DrLLcNNq@`GImW8^*`i$Z zSI0xlxbao!c-seR!p`gVgq{mX{L!{I*BZ*47xkJCN8a2>7(^|O&|d|;iQLn;v7{;< z@!&v&+f#Jl#rtbdW**C{ofbtge%KLbCW9O0h#AT{uhdH3@A|REr!f8rVYB0V37=la zI|>7Z8asw3W4&Ioz-LAg@>Y}#6#{{Gn9X?h0M7+2W(c)&@}1)ay$PxlU2keZ_6WP;a0VRi2`j;&K9-89l(T5pFWzFu)T%>+Ic1^OD#ze z(mCI+#7#~-JT-l0Zt)>_oRM|>ElnhZ;-Pe{)l4f3EnLB>Tx==q7PIwEEtss4`7of} z6SS7g3PQ{5wHUV@sL4H{TC3nFpKa3ygafV8fN?fosV?I8f#g3kV0cB?I9=8RxBV;OmtlkJUQu>4BR7n3Cs+P3YW{ z&lgvQ!!J>nG;Q6b#a9f(8Piz-Vbx;qXn>Aw+V`SH4U~Tiq0Ok4tk zd&Xh*H^V>Z1gLE;C3W*;Al`CBVjFq_sPS+z-t^!$5Y%VBl52Kj?{{we(!(Gv85#{C#Aar?k_TYy6`XvIjH(ZeMDyR} zK2h=o_l+Mv`WiAes4!QY>5Zd^%~p^6(CLKsz4emkQyX=`9$gG;O<%>tfagED1S;d{&z$qrOg zG`ubPu)Y|0YaFYS+8`elO@Hh9 zr-WsniDa4ZioGetsfL3Bgd5x!@}Y$G=Z zQz^|Uvaiz@sRpVUe+hdLuS{cmFAznjdnkvmub%c!BHozwhmXpra@9q+J*bQOrm;a@ z^T`)WucDGfcCt)0dwI@;{CFzz~tgh_SgyPPmEM8t9A^pX*WW^oV`hifupTk6aO)F5N9zw&&{WowxFqn>Rn5lf5eIe&A$E z&9%?v*A3pQU;6&Y5f}zJtG|-CBk+EG&PL0Q-D^9J$9P$L|H=v0kr=OCPl4cM_L|n_ zwr>%eNg&tOYoGeB$L7nOlHP}=Yz(P@J{9hfE^@;_p0xmAaxXKu5A`Pbv4G;v>c( zDiIGr+^i|yO-G(RJm4-4E{FjV;Gpi&jy}^6-QJEO%-itH+wnE%pcYX@!M29lNjs$j zk!qM;DkPrD^@M}%CO`*Rh$HUt0yeRzMWnnOk5d%Bo&(v|n!pzA0h1IZv6)Dg>BJvQCCiwT z5Zj^X@f9g`IyS4HtAXP%L`KcBxpK+EkMX#;5HZP(Q^?BX+BEzTp%g&2(%!0C{&crA zA}GYfQIF;rBPDIm8(u<0AI|5hptyik9)^aLd< zi6W^L&CyJNVqe}n7j2~a~7RB#H@2Lk8tNqmAA zC8X~-KuS}vhjg$>LS)?0SBRrj&9CdO{kzc!2}S6N6qA&`Gjk zRY=>}m?zc3fV@zRhLf&A+-QN|@?3KXTyE5WBP+P{Fpzm7D45I@0OD<8V*dgI9y6rq zQ|xjsS4a!=8X|}|oqV@G*?bcXutKc8AO12S4`CDgxVZc>BX3b26hJFOtR~kLmAj%a z3l<5EUk|uQ>9~w}K92DA(Nsq)dYOgpSTa)n*5--6gPdX83nr?9Gj{w~@n*c~l*J zA)Tqw%HN8HPSBC6E7)sva3&siG#q<193)G_#Zpm2_|R)BdxlJlW_eH*c3zJWymd_M zyHLbUs?oi%IDRV5Qin^Kox8h{dRs5?Uu>hym=zx?q26pi9Sd8efHT^o#}$IryP!Y?m4!mq(R||3*|yA?G|)VEmDvw zacMO5+N;aKF5ry{ifvB&oU1Dkc2D#_kn5^yS)!hyb=(K97tRDWl5-@>E)dEVrf|pU zxF;*vu=bjsPG~F#r^Y2L#n20mf&( z31>$ez(jfNGPLf#aw+?!c==->#{&|<_CYMF9G!<;BVZdbYFX^Sw;rt*qqRHv+8jRP zXb(0qb&Up>btQCq&kOp9A7SA<=s|>F#4QSfi@{1|d-ya!*%U4s9WqL*jSxg{qaJf*l^A;jFb7wXlNx6o*2l+&e0OVDF+x$FczKY5KV zdNSlYntF$8fX+DaW>?Z+c$6q3c)M|oX?Ja7OU^e%cYCgw&prejNIP5T0TuIYrV+{N z7kk_PWC;;1z?C`W)jK75aJ$-}DcloUE!rxaC74RP!w!uEsfgV;wu^9O1*0M=+Jm)^ zow!ob!SBPw%A&aMF|Z0Wd>sX=LQHsLcJ4zY?;U>e2MU&Z7UK>so;n;eWcIS;^`ITH zzs!4hNYyaz$YflQ;&9w_tPMDJ#w_-2YU~5#h-Y*4V$DcZWZY(4*?xu?kOmx065F2= zGkP;`G^5NaYhiQ{0Bbla0Ic-^DnRB~iQ8~NAGN4?taxE8e*{$HHeOkVpJ#AY1)ZqR z!UKaF`N;8-K4@bZdLI?;N`N51Fe4VICC0is#kO>8L|LuM(8E(G%58z3FV z$Gr7m5LU>BAy#27Ajl>C+kg|WU<$lZw%?kCE^C=8V@_PszjZSUFU)}H(5L3^z>3(n zFE)=uu0pjLFcI4H-F2`t0WR4xjTd*CUJrut5@u%dK$diPY0K2A*wlqS=!>jd%HsRA zj^Y~^ru8Xk(Uw{L3p2>oS*_T~F{?Yb?#$j=m^CEBu1(&#`RvZN{dXVTff}&zjM!Kkn!6T_`MZUO@{CBpoP_{dyX`k$sW#f2|H`iyem$>!mTUl0WyYyasLJ?GUTe# zu>2^0M+}L?Z#pe%1n{1IjVhy&-~raKMZgp|_Qw<`e1GQW$*50ndjLt`Qns%|Y}ZzU z*dRvman$o^g4;HvHhoyO8suRvf-C}pe3e0lNBHIBHb%grZ+XV$@;9jEN9nWyb2tg7 zhLH=@rc|ZZ#PSbik;hU0NZ)B+76{uDqPdi1!umfNPl=bQHE)-f9GL=+Rq`>9G)~1Ic ziUb?+P^2-92Zp`$x`Zn zumZ|`=4tcn%Xls+l0`|+48oiQaTQYiT=}Og&%-+BzTpqIO=#|vOGU;Yj`kWb9pXv* z^|9tPfDq92{AlNIA)vWnm2K<7^&Q^9Rp(pgWY70kA1YlT;#V>R!uFaQiN|ggg>&m` z-CYea8C6LgPb??I{4?XTAd-?|D@VsCi1(h3O6Pd2^(HO3wei-pimicv$Lri49M7E* zbQG1e+3;f~@T)IW-w=p7>EzL>>%VG0<-U?8xc+WfVf4DdzLjf3rr)RWr|tE`S_H&a zLMESdDJg9(gxEE7`6YIJA9=E#UDq#Z35!Yd++hQBgayto93KYp>5V=(U=|B{VtL)= z*g#uA+^*RpiGU06F4u1tATr8tTQpookI-_7zYtWpEX>!XU;T(Yf%^pResfsngX8VP zSKP1jL^Ia9jz5AzqBKGVAg*BvMsWWY24j1s73Cavfw_|*DJtySkd#H0{tvLnNNZik zvwd=837%%W#mkwU;2)&l|1Z8d8>UPNpA3Qu=Wl^Ln)Uj$etA)`Vn}oHw4>QJN47J;(0}P zR_Z=^{>W!6^YG_^!fS$wjS_^nAjl236UBN9XY~t@RD>SQ-Mw8kRGfg#?K~Qu)^piB zh8-FHo$Kbtu7um&+uST#V6x2&MwEiww)J=hv4)wfVcx$@Q#%O(E9@yY+@H29@Ya-@ zm0CGiN!2Lu@bsAijc>FJe_~?rl{(W5eUMXzb@)-coBj`5PIC5^Vxy&l*Ni4DMaNm#;}`m}E9e$-6!JET3Fmn^baSF@7Vp-17>>Q1VvOmkkD*zCN25PYgXglq%z%(mqV&5ztN$6r(uE zwt)DK&_N%gDQFWv7P!xas10W4N$O+izu##{|mkp7i<1S^buymaaSGw!cD)C7uZU%H;o@)hR{-A*Yj-lPYokQ8fh1ovC_n7q^M-zC#>% znD)`ZW3V(zD$~Q2?R=0fzuCEe?WkJ!GdbU}QM+3qeivJxllP2`ds=@ydZhJ*_T|yZ zQ0|ZAAIqNa{!3>(Q8_G!@9Im^DV>?Ziiupn6liv;mt3tJZfAS-byeRtgKabh1P4-p z;DIJI4b(hg{6<*^1a9E{+*x|)x#cAf(7ZCE=X$KPTg=?H6{8HQb`|QGapt3oyq}Ni z(r`s&56J*p!C5IozH?4Dr0MKFdCN*uvP=jNP*vWovgVY$H&5|xDH zk%I|w(^G!#Kw?a*kekME(*cqSSp^8TsgocvRMJ*ymjlZH!Hc&3aUr1DdxWGU>>fLB z#`9n#mS-`R&t2u1jM7l8x9DA)b1OCmUXt2X0qd=xBP{B_9Dyy?e;k2yXb{I{!X!~M zZA%kqL$w1^a0Xps=U9X+vK0SP61AJM%hnc1wejabv#GYmAS)fdT~Kdh#6j@`CImHU z;>SvY4MSehXNOgT_`;&Nnw*GiD1Xa>R%=t=J<7a+=5u%u+PI`dRRmBLlS9ltYddJMEL6a#$Eyqk?E zC&02hx8rDVRtV@`(dmctr=K*Onfs*@o?F~h1q!-aD?C7mjk5|q%YmXms6>!nIn&rl6Sf|l0inlUk=qgkYSCi38Zyn& zW8Fvcta~zJs8Jfi2_r)J7CM<-FQdEHeHJ?1QmbK~59(kN3F{=Kd)eXxZ9<;u(uJ(! zAH4T{NfEbUDSfn6K~6!=GR1z{74BFpl$?_C{mLW5Nd|Ij4Q(piM*2U)LGn)8yf+R{ zwjDw_XUpak-mu;Az*dnqmAx7X+5r@DDSryNPBc{b4Ww@fNU#NE`Kyy#a6|9N9BAMv z?0Di~@2@<89OQ%vG-qn}9psiK@M=TmB`41!c5J6m#O-4B>2%03PQL4APpoTC*;ToO zBl5{yhRHm2TI-d$FV>2lgq*auQ!Tu=>t@Gs11c(klnqYR9x7e(p8k_biia)SL`2HEG1xYoWGHfrr!Zv#&A?F!}S1TVB9U zX{EEy-i`&{pI>SfLJNy6(2^Dk7Fb zs8C{!j02jsIX+qv%tRg(hQ%gXw7C7FX{$PO7c#T-r)k?KiToS^3WAd%Pft`~^0_w3 z+nP2}I+B?a=yGuaNcDWig|sG}>ZW=}QpHrB_V;qrp8Y%&1V29}l^r#ttFJ~iHd}{k zhatFY&|;sPaSs$`#&?mVIwz91>bEC=a2hLR!IF6BduKokMUP@zz<{UQfKP!)uN|y_ z1lqNp4*EmSzrOOokzgbX68c@inj!&oY-2^-p`v2evP$Y4WaDo;Afb$48l7ks8JQYc zTyGj05rL#d+VVtHoa|96?5RqxXsC$@TaKvG>rF4^-!UD2WP~3O1bScYZJs#VwJ+4K z@JdFn0X6e-A6znzhKiZ%U$)aAlv`PvlRago95>3&ClO|uyqd~K3rH> zc=Y)3<0o6k;cyn0mX@Aw-Lq%UoeZ{)z`c3%=Iz_J@3!tea34N= zSXo(F-MY24wU6uT>z_V-`n+{tHa0fCe*OCG+qduEzyJ900wR*8qF8PdOkY*A+|{uN}4rC?V1 z{%yO$seeo^cWQ+XVofu`$c0jd?~9iI;IZRp-r>oRF%9bf6J$f`3%N6(fC7*mLt6aP2K}KS{`WR0sCa3|qBuS|Y@g;o+n^o3b0?=w z7M~pmaKOXm4k;1_Wz57_NzyusAX7=9!5Wy15rJ(ad7MRt$iz|EK5`CZR*GyirN9Co z0a1{vB$Aq5mY;Zke=Ep#hg2WECVcqMN<|u=vD?J9Fb07jo7TYJI-niamrP(C5x9)xBBoCeEso+}p)yL_9jBWoWfq+exn7fHL^eEDg9sY&QuzNm`%UJct1?O&VH zJLiy~<`d@Sr`}(D-W?whapRQJ=Ik*M{WT4Y%=I%6cqkqMe%2*H&=`{&t+>yTtFitv z`6VGQnUfXts`*NUUV7B;4BKA18Y1xGyVWBzj}OI1#>8%WGb9D9(g#{ zoma-(c^x;?tu1F6E2P03`&IgcM9(}tz1SRSaG}pER<*I8Xk>`l&jhP&y13Zg=O$NM zE1GY+jFEiH;f-#q5W;QqTl>9 zvwuX4h2;OeTOsz`ul%Ao1*nk1F+l5+^xB<2av{D5XnhLto{3I9Pds=`Zp_kUO*tcX zLRxEVtbp2<%fd-psTFUxDOti zk4Ez;&Ht#=&L6nhn)R{Fwr9~+ znSzvz8{j%vRcSpqOM1g#^X}hd6YrQhRh+Z(;g4m@zR-l~K4FC_FI44k%~^b-19KK> zUe+(arlyD?Pe5x=A9WKhR}|*gMp~Q+u9BQdjFzfI1TzRzRQ9p4(5i*T(_HhSHKFyE^G=_=TLR%opZ?qM^XZJ6( z^-VuTxm}!@B5I4GpoC&@BviO-?vmJq~0UA!!fNB*0!K(jeX=xDI% zu3non5x_bv+}|7^de@9*q-DoA=iN;nctRb)Jnm?#OvPF1_u{L|H5O@nkYr&&2EK~*Px6S~?Ue)e4d)7Jali1|AC_@ir~q6^Ptp1d9! zCY*2$dC%26L}4KxIM)e|Mb=AbA87c zF`aMqWz$_!YCebPcrK~R(2claEe%CU3WLONdXzmML2BAwgfteH6l8i9z%c(I_Hztd~U!iwTZ1C$Yu=0gtZ zI&Iu>eqeh<7rnZm!Q$>$KJhhR5F@QVac}3K%;xghcw92!MGADE&`(gf_>df7F=3(E znOg!1m0>EZdL7B;FEWP=m%`sQoNmp!t?MarZ+=+oO#0b_)z#vw4I`Il*C(l$cVZfj znHx(`vOSI7^k56$Pp7osNz)ozU=uzP7vG;Qh9y@41RYAh;0Q9!B}G zw2cKq%>UdMeQDzyp0;yHW%FbHhp~tCA7#OexOMm@6PT|}%Gqbg-X^&B7yR<<9JB3h ziv^o+%5n=qWojO$iS&8PTTBhg&e73g>l*$r6N85~E`h6javac>*=|Z5Pl0vi$0@hQ z@$tkWf`6Iqrg-0(@$6d!og|}m7$AHLylg6nqaiAg2*)=ZP=7`PkJ_MG;YF&ps@jPq zW{idi!d3y4^DncV)`|eaiDxhszmFNuo1KLGWwsO5<{2@B=TH=p?(i73{o*E3+TQ+$ z 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 z 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 z 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 diff --git a/docs/v3/v2/img/office365-header-icon.png b/docs/v3/v2/img/office365-header-icon.png deleted file mode 100644 index 529191ea65d5d78f1964a4ff675c064e3324873c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 312 zcmeAS@N?(olHy`uVBq!ia0vp^DImwho^t@)Y*6k#k0@(X5g zcy=QV#7XjYcVXyYmGuB}I14-?iy0WWg+Z8+Vb&Z8pdfpRr>`sfBQ{AMOSK2Hc=iK@ zR(rZQhFF|_du1cZLh;FKR%UL^VQEdTT15*_xaf=PkVMessuWl!PC{xWt~$(699F$b}j$_ diff --git a/docs/v3/v2/img/usage-2020-eoy.png b/docs/v3/v2/img/usage-2020-eoy.png deleted file mode 100644 index 3f89f8022d893ceec0ac27abcf0980715d1b3f4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41944 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 diff --git a/docs/v3/v2/index.html b/docs/v3/v2/index.html deleted file mode 100644 index 4f6e2f2ac..000000000 --- a/docs/v3/v2/index.html +++ /dev/null @@ -1,2410 +0,0 @@ - - - - - - - - - - - - - - - - - - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - -

      -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      Home

      - -

      SharePoint Patterns and Practices Logo

      -

      PnPjs is a collection of fluent libraries for consuming SharePoint, Graph, and Office 365 REST APIs in a type-safe way. You can use it within SharePoint Framework, Nodejs, or any JavaScript project. This an open source initiative and we encourage contributions and constructive feedback from the community.

      -

      Fluent API in action

      -

      Animation of the library in use, note intellisense help in building your queries

      -

      General Guidance

      -

      These articles provide general guidance for working with the libraries. If you are migrating from v1 please review the transition guide.

      - -

      Packages

      -

      Patterns and Practices client side libraries (PnPjs) are comprised of the packages listed below. All of the packages are published as a set and depend on their peers within the @pnp scope.

      -

      The latest published version is npm version.

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      @pnp/
      adaljsclientProvides an adaljs wrapper suitable for use with PnPjs
      commonProvides shared functionality across all pnp libraries
      config-storeProvides a way to manage configuration within your application
      graphProvides a fluent api for working with Microsoft Graph
      loggingLight-weight, subscribable logging framework
      msaljsclientProvides an msal wrapper suitable for use with PnPjs
      nodejsProvides functionality enabling the @pnp libraries within nodejs
      odataProvides shared odata functionality and base classes
      spProvides a fluent api for working with SharePoint REST
      sp-addinhelpersProvides functionality for working within SharePoint add-ins
      -

      Authentication

      -

      We have a new section dedicated to helping you figure out the best way to handle authentication in your application, check it out!

      -

      Issues, Questions, Ideas

      -

      Please log an issue using our template as a guide. This will let us track your request and ensure we respond. We appreciate any constructive feedback, questions, ideas, or bug reports with our thanks for giving back to the project.

      -

      Changelog

      -

      Please review the CHANGELOG for release details on all library changes.

      -

      Code of Conduct

      -

      This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

      -

      "Sharing is Caring"

      -

      Please use http://aka.ms/sppnp for the latest updates around the whole SharePoint Patterns and Practices (PnP) program.

      -

      Disclaimer

      -

      THIS CODE IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/logging/index.html b/docs/v3/v2/logging/index.html deleted file mode 100644 index d22a640d8..000000000 --- a/docs/v3/v2/logging/index.html +++ /dev/null @@ -1,2622 +0,0 @@ - - - - - - - - - - - - - - - - - - logging - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/logging

      -

      npm version

      -

      The logging module provides light weight subscribable and extensible logging framework which is used internally and available for use in your projects. This article outlines how to setup logging and use the various loggers.

      -

      Getting Started

      -

      Install the logging module, it has no other dependencies

      -

      npm install @pnp/logging --save

      -

      Understanding the Logging Framework

      -

      The logging framework is centered on the Logger class to which any number of listeners can be subscribed. Each of these listeners will receive each of the messages logged. Each listener must implement the ILogListener interface, shown below. There is only one method to implement and it takes an instance of the LogEntry interface as a parameter.

      -
      /**
      - * Interface that defines a log listener
      - *
      - */
      -export interface ILogListener {
      -    /**
      -     * Any associated data that a given logging listener may choose to log or ignore
      -     *
      -     * @param entry The information to be logged
      -     */
      -    log(entry: ILogEntry): void;
      -}
      -
      -/**
      - * Interface that defines a log entry
      - *
      - */
      -export interface ILogEntry {
      -    /**
      -     * The main message to be logged
      -     */
      -    message: string;
      -    /**
      -     * The level of information this message represents
      -     */
      -    level: LogLevel;
      -    /**
      -     * Any associated data that a given logging listener may choose to log or ignore
      -     */
      -    data?: any;
      -}
      -
      -

      Log Levels

      -
      export const enum LogLevel {
      -    Verbose = 0,
      -    Info = 1,
      -    Warning = 2,
      -    Error = 3,
      -    Off = 99,
      -}
      -
      -

      Writing to the Logger

      -

      To write information to a logger you can use either write, writeJSON, or log.

      -
      import {
      -    Logger,
      -    LogLevel
      -} from "@pnp/logging";
      -
      -// write logs a simple string as the message value of the LogEntry
      -Logger.write("This is logging a simple string");
      -
      -// optionally passing a level, default level is Verbose
      -Logger.write("This is logging a simple string", LogLevel.Error);
      -
      -// this will convert the object to a string using JSON.stringify and set the message with the result
      -Logger.writeJSON({ name: "value", name2: "value2"});
      -
      -// optionally passing a level, default level is Verbose
      -Logger.writeJSON({ name: "value", name2: "value2"}, LogLevel.Warning);
      -
      -// specify the entire LogEntry interface using log
      -Logger.log({
      -    data: { name: "value", name2: "value2"},
      -    level: LogLevel.Warning,
      -    message: "This is my message"
      -});
      -
      -

      Log an error

      -

      There exists a shortcut method to log an error to the Logger. This will log an entry to the subscribed loggers where the data property will be the Error -instance passed in, the level will be 'Error', and the message will be the Error instance's message property.

      -
      const e = Error("An Error");
      -
      -Logger.error(e);
      -
      -

      Subscribing a Listener

      -

      By default no listeners are subscribed, so if you would like to get logging information you need to subscribe at least one listener. This is done as shown below by importing the Logger and your listener(s) of choice. Here we are using the provided ConsoleListener. We are also setting the active log level, which controls the level of logging that will be output. Be aware that Verbose produces a substantial amount of data about each request.

      -
      import {
      -    Logger,
      -    ConsoleListener,
      -    LogLevel
      -} from "@pnp/logging";
      -
      -// subscribe a listener
      -Logger.subscribe(new ConsoleListener());
      -
      -// set the active log level
      -Logger.activeLogLevel = LogLevel.Info;
      -
      -

      Available Listeners

      -

      There are two listeners included in the library, ConsoleListener and FunctionListener.

      -

      ConsoleListener

      -

      This listener outputs information to the console and works in Node as well as within browsers. It can be used without settings and writes to the appropriate console method based on message level. For example a LogEntry with level Warning will be written to console.warn. Basic usage is shown in the example above.

      -

      Configuration Options

      -

      Although ConsoleListener can be used without configuration, there are some additional options available to you. ConsoleListener supports adding a prefix to every output (helpful for filtering console messages) and specifying text color for messages (including by LogLevel).

      -
      Using a Prefix
      -

      To add a prefix to all output, supply a string in the constructor:

      -
      import {
      -    Logger,
      -    ConsoleListener,
      -    LogLevel
      -} from "@pnp/logging";
      -
      -const LOG_SOURCE: string = 'MyAwesomeWebPart';
      -Logger.subscribe(new ConsoleListener(LOG_SOURCE));
      -Logger.activeLogLevel = LogLevel.Info;
      -
      -

      With the above configuration, Logger.write("My special message"); will be output to the console as:

      -
      MyAwesomeWebPart - My special message
      -
      -
      Customizing Text Color
      -

      You can also specify text color for your messages by supplying an IConsoleListenerColors object. You can simply specify color to set the default color for all logging levels or you can set one or more logging level specific text colors (if you only want to set color for a specific logging level(s), leave color out and all other log levels will use the default color).

      -

      Colors can be specified the same way color values are specified in CSS (named colors, hex values, rgb, rgba, hsl, hsla, etc.):

      -
      import {
      -    Logger,
      -    ConsoleListener,
      -    LogLevel
      -} from "@pnp/logging";
      -
      -const LOG_SOURCE: string = 'MyAwesomeWebPart';
      -Logger.subscribe(new ConsoleListener(LOG_SOURCE, {color:'#0b6a0b',warningColor:'magenta'}));
      -Logger.activeLogLevel = LogLevel.Info;
      -
      -

      With the above configuration:

      -
      Logger.write("My special message");
      -Logger.write("A warning!", LogLevel.Warning);
      -
      -

      Will result in messages that look like this:

      -

      ConsoleListener with Colors

      -

      Color options:

      -
        -
      • color: Default text color for all logging levels unless they're specified
      • -
      • verboseColor: Text color to use for messages with LogLevel.Verbose
      • -
      • infoColor: Text color to use for messages with LogLevel.Info
      • -
      • warningColor: Text color to use for messages with LogLevel.Warning
      • -
      • errorColor: Text color to use for messages with LogLevel.Error
      • -
      -

      To set colors without a prefix, specify either undefined or an empty string for the first parameter:

      -
      Logger.subscribe(new ConsoleListener(undefined, {color:'purple'}));
      -
      -

      FunctionListener

      -

      The FunctionListener allows you to wrap any functionality by creating a function that takes a LogEntry as its single argument. This produces the same result as implementing the LogListener interface, but is useful if you already have a logging method or framework to which you want to pass the messages.

      -
      import {
      -    Logger,
      -    FunctionListener,
      -    ILogEntry
      -} from "@pnp/logging";
      -
      -let listener = new FunctionListener((entry: ILogEntry) => {
      -
      -    // pass all logging data to an existing framework
      -    MyExistingCompanyLoggingFramework.log(entry.message);
      -});
      -
      -Logger.subscribe(listener);
      -
      -

      Create a Custom Listener

      -

      If desirable for your project you can create a custom listener to perform any logging action you would like. This is done by implementing the ILogListener interface.

      -
      import {
      -    Logger,
      -    ILogListener,
      -    ILogEntry
      -} from "@pnp/logging";
      -
      -class MyListener implements ILogListener {
      -
      -    log(entry: ILogEntry): void {
      -        // here you would do something with the entry
      -    }
      -}
      -
      -Logger.subscribe(new MyListener());
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/news/2020-year-in-review/index.html b/docs/v3/v2/news/2020-year-in-review/index.html deleted file mode 100644 index cce127fa3..000000000 --- a/docs/v3/v2/news/2020-year-in-review/index.html +++ /dev/null @@ -1,2525 +0,0 @@ - - - - - - - - - - - - - - - - - - 2020 Year In Review - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      2020 Year End Report

      -

      Welcome to our first year in review report for PnPjs. This year has marked usage milestones, seen more contributors than ever, and expanded the core maintainers team. But none of this would be possible without everyones support and participation - so we start by saying Thank You! We deeply appreciate everyone that has used, helped us grow, and improved the library over the last year.

      -

      This year we introduced MSAL clients for node and browser, improved our testing/local development plumbing, and updated the libraries to work with the node 15 module resolution rules.

      -

      We fixed 43 reported bugs, answered 131 questions, and made 55 suggested enhancements to the library - all driven by feedback from users and the community.

      -

      Planned for release in January 2021 we also undertook the work to enable isolated runtimes, a long requested feature. This allows you to operate on multiple independently configured "roots" such as "sp" or "graph" from the same application. Previously the library was configured globally, so this opens new possibilities for both client and server side scenarios.

      -

      Finally we made many tooling and project improvements such as moving to GitHub actions, updating the tests to use MSAL, and exploring ways to enhance the developer experience.

      -

      Usage

      -

      In 2020 we tracked steady month/month growth in raw usage measured by requests as well as in the number of tenants deploying the library. Starting the year we were used in 14605 tenants and by December that number grew to 21,227.

      -

      These tenants generated 6.1 billion requests to the service in January growing to 9.2 billion by December, peaking at 10.1 billion requests in November.

      -

      Graph showing requests and tenants/month for @pnp/sp

      -
      -

      1) There was a data glitch in October so the numbers do not fully represent usage. 2) These numbers only include public cloud SPO usage, true usage is higher than we can track due to on-premesis and gov/sovereign clouds

      -
      -

      Releases

      -

      We continued our monthly release cadence as it represents a good pace for addressing issues while not expecting folks to update too often and keeping each update to a reasonable size. All changes can be tracked in our change log, updated with each release. You can check our scheduled releases through project milestones, understanding there are occasionally delays. Monthly releases allows us to ensure bugs do not linger and we continually improve and expand the capabilities of the libraries.

      -

      NPM Package download statistics (@pnp/sp):

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      MonthCount*MonthCount
      January100,686*July36,805
      February34,437*August38,897
      March34,574*September45,968
      April32,436*October46,655
      May34,482*November45,511
      June34,408*December58,977
      Grand Total543,836
      -

      With 2020 our total all time downloads of @pnp/sp is now at: 949,638

      -
      -

      Stats from https://npm-stat.com/

      -
      -

      Future Plans

      -

      Looking to the future we will continue to actively grow and improve v2 of the library, guided by feedback and reported issues. Additionally, we are beginning to discuss v3 and doing initial planning and prototyping. The v3 work will continue through 2021 with no currently set release date, though we will keep everyone up to date.

      -

      Additionally in 2021 there will be a general focus on improving not just the code but our tooling, build pipeline, and library contributor experience. We will also look at automatic canary releases with each merge, and other improvements.

      -

      New Lead Maintainer

      -

      With the close of 2020 we are very excited to announce a new lead maintainer for PnPjs, Julie Turner! Julie brings deep expertise with SharePoint Framework, TypeScript, and SharePoint development to the team, coupled with dedication and care in the work.

      -

      Over the last year she has gotten more involved with handling releases, responding to issues, and helping to keep the code updated and clean.

      -

      We are very lucky to have her working on the project and look forward to seeing her lead the growth and direction for years to come.

      -

      Contributors

      -

      As always we have abundant thanks and appreciation for your contributors. Taking your time to help improve PnPjs for the community is massive and valuable to ensure our sustainability. Thank you for all your help in 2020! If you are interested in becoming a contributor check out our guide on ways to get started.

      -

      - AJIXuMuK - - Ashikpaul - - cesarhoeflich - - dcashpeterson - - dependabot[bot] - - derhallim - - DRamalho92 - - f1nzer - - Harshagracy - - holylander - - hugoabernier - - JakeStanger - - jaywellings - - JMTeamway - - joelfmrodrigues - - juliemturner - - jusper-dk - - KEMiCZA - - koltyakov - - kunj-sangani - - MarkyDeParky - - mikezimm - - mrebuffet - - naugtur - - NZainchkovskiy - - PaoloPia - - patrick-rodgers - - ravichandran-blog - - RoelVB - - siddharth-vaghasia - - simonagren - - tavikukko - - ValerasNarbutas -

      -

      Sponsors

      -

      We want to thank our sponsors for their support in 2020! This year we put the money towards helping offset the cost and shipping of hoodies to contributors and sponsors. Your continued generosity makes a big difference in our ability to recognize and reward the folks building PnPjs.

      -

      Thank You

      -

      - KEMiCZA - - Sympraxis Consulting - - thechriskent - - erwinvanhunen - - PopWarner - - VesaJuvonen - - LauraKokkarinen - - ricardocarneiro - - andrewconnell -

      -

      Closing

      -

      In closing we want say Thank You to everyone who uses, contributes to, and participates in PnPjs and the SharePoint Patterns and Practices program.

      -

      Wishing you the very best for 2021,

      -

      The PnPjs Team

      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/nodejs-support/index.html b/docs/v3/v2/nodejs-support/index.html deleted file mode 100644 index c7c459630..000000000 --- a/docs/v3/v2/nodejs-support/index.html +++ /dev/null @@ -1,2398 +0,0 @@ - - - - - - - - - - - - - - - - - - Working in Nodejs - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      Working in Nodejs

      -

      As outlined on the getting started page you can easily use the library with Nodejs, but there are some key differences you need to consider.

      -

      But first a little history, you can skip this part if you just want to see how things work but we felt some folks might be interested. To make selective imports work we need to support es module syntax for client-side environments such as SPFx development. All versions of Nodejs that are currently LTS do not support es modules without flags (as of when this was written). We thought we had a scheme to handle this following the available guidance but ultimately it didn't work across all node versions and we unpublished 2.0.1.

      -

      CommonJS Libraries

      -

      Because of the difficulties of working with es modules in node we recommend using our mirror packages providing commonjs modules. These can be installed by using the package name and appending -commonjs, such as:

      -
      npm install @pnp/sp-commonjs @pnp/nodejs-commonjs
      -
      -

      These packages are built from the same source and released at the same time so all updates are included with each release. The only difference is that for the sp-commonjs and graph-commonjs packages we target the "all" preset as the entry point. This makes things a little easier in node where bundle sizes aren't an issue. You can see this in the nodejs-app sample. Here is that sample explained fully:

      -

      Install Libraries

      -

      We want to make a simple request to SharePoint so we need to first install the modules we need:

      -
      npm install @pnp/sp-commonjs @pnp/nodejs-commonjs --save
      -
      -

      We will also install TypeScript:

      -
      npm install typescript --save-dev
      -
      -

      index.ts

      -

      We will create an index.ts file and add the following code. You will need to update the site url, client id, and client secret to your values. This should be done using a settings file or something like Azure KeyVault for production, but for this example it is good enough.

      -
      // our imports come from the -commonjs libs
      -import { SPFetchClient } from "@pnp/nodejs-commonjs";
      -import { sp } from "@pnp/sp-commonjs";
      -
      -// we call setup to use the node client
      -sp.setup({
      -    sp: {
      -        fetchClientFactory: () => {
      -            return new SPFetchClient("{ site url }", "{ client id }", "{ client secret }");
      -        },
      -    },
      -});
      -
      -async function makeRequest() {
      -
      -    // make a request to get the web's details
      -    const w = await sp.web();
      -    console.log(JSON.stringify(w, null, 2));
      -}
      -
      -// get past no await at root of app
      -makeRequest();
      -
      -
      -

      Don't forget you will need to register an app to get the client id and secret.

      -
      -

      Add a tsconfig.json

      -

      Not strictly necessary but very useful to include a tsconfig.json to control how tsc transpiles your code to JavaScript

      -
      {
      -    "compilerOptions": {
      -        "module": "commonjs",
      -        "target": "esnext",
      -        "moduleResolution": "node",
      -        "declaration": true,
      -        "outDir": "dist",
      -        "skipLibCheck": true,
      -        "sourceMap": true,
      -        "lib": [
      -            "dom",
      -            "esnext"
      -        ]
      -    },
      -    "files": [
      -        "./index.ts"
      -    ]
      -}
      -
      -

      Add an script to package.json

      -

      We add the "start" script to the default package.json

      -
      {
      -  "name": "nodejs-app",
      -  "version": "1.0.0",
      -  "description": "Sample nodejs app using PnPjs",
      -  "main": "index.js",
      -  "scripts": {
      -    "start": "tsc -p . && node dist/index.js",
      -    "test": "echo \"Error: no test specified\" && exit 1"
      -  },
      -  "author": "",
      -  "license": "MIT",
      -  "dependencies": {
      -    "@pnp/nodejs-commonjs": "^2.0.2-5",
      -    "@pnp/sp-commonjs": "^2.0.2-5"
      -  },
      -  "devDependencies": {
      -    "typescript": "^3.7.5"
      -  }
      -}
      -
      -

      Run It

      -

      You can now run your program using:

      -
      npm start
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/nodejs/adal-fetch-client/index.html b/docs/v3/v2/nodejs/adal-fetch-client/index.html deleted file mode 100644 index 5b111c8b1..000000000 --- a/docs/v3/v2/nodejs/adal-fetch-client/index.html +++ /dev/null @@ -1,2204 +0,0 @@ - - - - - - - - - - - - - - - - - - AdalFetchClient - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/nodejs/adalfetchclient

      -

      The AdalFetchClient class depends on the adal-node package to authenticate against Azure AD. The example below -outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected.

      -
      import { AdalFetchClient } from "@pnp/nodejs";
      -import { graph } from "@pnp/graph/presets/all";
      -
      -// setup the client using graph setup function
      -graph.setup({
      -    graph: {
      -        fetchClientFactory: () => {
      -            return new AdalFetchClient("{tenant}", "{app id}", "{app secret}");
      -        },
      -    },
      -});
      -
      -// execute a library request as normal
      -const g = await graph.groups();
      -
      -console.log(JSON.stringify(g, null, 4));
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/nodejs/bearer-token-fetch-client/index.html b/docs/v3/v2/nodejs/bearer-token-fetch-client/index.html deleted file mode 100644 index 537d7da08..000000000 --- a/docs/v3/v2/nodejs/bearer-token-fetch-client/index.html +++ /dev/null @@ -1,2203 +0,0 @@ - - - - - - - - - - - - - - - - - - BearerTokenFetchClient - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/nodejs/BearerTokenFetchClient

      -

      The BearerTokenFetchClient class allows you to easily specify your own Bearer tokens to be used in the requests. How you derive the token is up to you.

      -
      import { BearerTokenFetchClient } from "@pnp/nodejs";
      -import { graph } from "@pnp/graph/presets/all";
      -
      -// setup the client using graph setup function
      -graph.setup({
      -    graph: {
      -        fetchClientFactory: () => {
      -            return new BearerTokenFetchClient("{Bearer Token}");
      -        },
      -    },
      -});
      -
      -// execute a library request as normal
      -const g = await graph.groups();
      -
      -console.log(JSON.stringify(g, null, 4));
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/nodejs/index.html b/docs/v3/v2/nodejs/index.html deleted file mode 100644 index 10c75220c..000000000 --- a/docs/v3/v2/nodejs/index.html +++ /dev/null @@ -1,2261 +0,0 @@ - - - - - - - - - - - - - - - - - - nodejs - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/nodejs

      -

      npm version

      -

      This package supplies helper code when using the @pnp libraries within the context of nodejs. Primarily these consist of clients to enable use of the libraries in nodejs.

      -

      Getting Started

      -

      Install the library and required dependencies. You will also need to install other libraries such as @pnp/sp or @pnp/graph to use the -exported functionality.

      -

      npm install @pnp/sp @pnp/nodejs --save

      - -

      SP Extensions

      -

      Added in 2.0.9

      -

      A set of nodejs specific extensions for the @pnp/sp library.

      - - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/nodejs/provider-hosted-app/index.html b/docs/v3/v2/nodejs/provider-hosted-app/index.html deleted file mode 100644 index 61170385b..000000000 --- a/docs/v3/v2/nodejs/provider-hosted-app/index.html +++ /dev/null @@ -1,2220 +0,0 @@ - - - - - - - - - - - - - - - - - - ProviderHostedRequestContext - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/nodejs/providerhostedrequestcontext

      -

      The ProviderHostedRequestContext enables the creation of provider-hosted add-ins built in node.js to use pnpjs to interact with SharePoint. The context is associated to a SharePoint user, allowing requests to be made by the add-in on the behalf of the user.

      -

      The usage of this class assumes the provider-hosted add-in is called from SharePoint with a valid SPAppToken. This is typically done by means of accessing /_layouts/15/AppRedirect.aspx with the app's client ID and app's redirect URI.

      -

      Note: To support concurrent requests by different users and/or add-ins on different tenants, do not use the SPFetchClient class. Instead, use the more generic NodeFetchClient class. The downside is that you have to manually configure each request to use the desired user/app context.

      -
      import { sp, SPRest } from "@pnp/sp/presets/all";
      -import { NodeFetchClient, ProviderHostedRequestContext } from "@pnp/nodejs";
      -
      -// configure your node options
      -sp.setup({
      -    sp: {
      -        fetchClientFactory: () => {
      -            return new NodeFetchClient();
      -        },
      -    },
      -});
      -
      -// get request data generated by /_layouts/15/AppRedirect.aspx
      -const spAppToken = request.body.SPAppToken;
      -const spSiteUrl = request.body.SPSiteUrl;
      -
      -// create a context based on the add-in details and SPAppToken
      -const ctx = await ProviderHostedRequestContext.create(spSiteUrl, "{client id}", "{client secret}", spAppToken);
      -
      -// create an SPRest object configured to use our context
      -// this is used in place of the global sp object
      -const userSP = new SPRest().configure(await ctx.getUserConfig(), spSiteUrl);
      -const addinSP = new SPRest().configure(await ctx.getAddInOnlyConfig(), spSiteUrl);
      -
      -// make a request on behalf of the user
      -const user = await userSP.web.currentUser();
      -console.log(`Hello ${user.Title}`);
      -
      -// make an add-in only request
      -const app = await addinSP.web.currentUser();
      -console.log(`Add-in principal: ${app.Title}`);
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/nodejs/proxy/index.html b/docs/v3/v2/nodejs/proxy/index.html deleted file mode 100644 index 1184999ee..000000000 --- a/docs/v3/v2/nodejs/proxy/index.html +++ /dev/null @@ -1,2238 +0,0 @@ - - - - - - - - - - - - - - - - - - proxy - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/nodejs/proxy

      -

      In some cases when deploying on node you may need to use a proxy as governed by corporate policy, or perhaps you want to examine the traffic using a tool such as Fiddler.

      -

      setProxyUrl

      -

      Basic Usage

      -

      You need to import the setProxyUrl function from @pnp/nodejs library and call it with your proxy url. Once done an https-proxy-agent will be used with each request. This works across all clients within the @pnp/nodejs library.

      -
      import { SPFetchClient, SPOAuthEnv, setProxyUrl } from "@pnp/nodejs";
      -
      -sp.setup({
      -    sp: {
      -        fetchClientFactory: () => {
      -
      -            // call the set proxy url function and it will be used for all requests regardless of client
      -            setProxyUrl("{your proxy url}");
      -            return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO);
      -        },
      -    },
      -});
      -
      -

      Use with Fiddler

      -

      To get Fiddler to work you may need to set an environment variable. This should only be done for testing!

      -
      import { SPFetchClient, SPOAuthEnv, setProxyUrl } from "@pnp/nodejs";
      -
      -sp.setup({
      -    sp: {
      -        fetchClientFactory: () => {
      -
      -            // ignore certificate errors: ONLY FOR TESTING!!
      -            process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
      -
      -            // this is my fiddler url locally
      -            setProxyUrl("http://127.0.0.1:8888");
      -            return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO);
      -        },
      -    },
      -});
      -
      -

      setProxyAgent

      -

      Added in 2.0.11

      -

      You need to import the setProxyAgent function from @pnp/nodejs library and call it with your proxy url. You can supply any valid proxy and it will be used.

      -
      import { SPFetchClient, SPOAuthEnv, setProxyAgent } from "@pnp/nodejs";
      -
      -sp.setup({
      -    sp: {
      -        fetchClientFactory: () => {
      -
      -            const myAgent = new MyAgentOfSomeType({});
      -
      -            // call the set proxy agent function and it will be used for all requests regardless of client
      -            setProxyAgent(myAgent);
      -            return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO);
      -        },
      -    },
      -});
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/nodejs/sp-extensions/index.html b/docs/v3/v2/nodejs/sp-extensions/index.html deleted file mode 100644 index ba352ffd1..000000000 --- a/docs/v3/v2/nodejs/sp-extensions/index.html +++ /dev/null @@ -1,2362 +0,0 @@ - - - - - - - - - - - - - - - - - - sp Extensions - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/nodejs - sp extensions

      -

      By importing anything from the @pnp/nodejs library you automatically get nodejs specific extension methods added into the sp fluent api. This article describes them.

      -
      -

      These examples use the *-commonjs version of the libraries as they target node, you can read more about the differences.

      -
      -

      IFile.getStream

      -

      Allows you to read a response body as a nodejs PassThrough stream.

      -
      // by importing the the library the node specific extensions are automatically applied
      -import { SPFetchClient, SPNS } from "@pnp/nodejs-commonjs";
      -import { sp } from "@pnp/sp-commonjs";
      -
      -sp.setup({
      -    sp: {
      -        fetchClientFactory: () => {
      -            return new SPFetchClient("{url}", "{id}", "{secret}");
      -        },
      -    },
      -});
      -
      -// get the stream
      -const streamResult: SPNS.IResponseBodyStream = await sp.web.getFileByServerRelativeUrl("/sites/dev/file.txt").getStream();
      -
      -// see if we have a known length
      -console.log(streamResult.knownLength);
      -
      -// read the stream
      -// this is a very basic example - you can do tons more with streams in node
      -const txt = await new Promise<string>((resolve) => {
      -    let data = "";
      -    stream.body.on("data", (chunk) => data += chunk);
      -    stream.body.on("end", () => resolve(data));
      -});
      -
      -

      IFiles.addChunked

      -

      Added in 2.1.0

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders/web";
      -import "@pnp/sp/folders/list";
      -import "@pnp/sp/files/web";
      -import "@pnp/sp/files/folder";
      -import * as fs from "fs";
      -
      -
      -const stream = fs.createReadStream("{file path}");
      -const files = sp.web.defaultDocumentLibrary.rootFolder.files;
      -
      -await files.addChunked(name, stream, null, true, 10);
      -
      -

      IFile.setStreamContentChunked

      -

      Added in 2.1.0

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders/web";
      -import "@pnp/sp/folders/list";
      -import "@pnp/sp/files/web";
      -import "@pnp/sp/files/folder";
      -import * as fs from "fs";
      -
      -const stream = fs.createReadStream("{file path}");
      -const file = sp.web.defaultDocumentLibrary.rootFolder.files..getByName("file-name.txt");
      -
      -await file.setStreamContentChunked(stream);
      -
      -

      Explicit import

      -

      If you don't need to import anything from the library, but would like to include the extensions just import the library as shown.

      -
      // ES Modules:  import "@pnp/nodejs";
      -import "@pnp/nodejs-commonjs";
      -
      -// get the stream
      -const streamResult = await sp.web.getFileByServerRelativeUrl("/sites/dev/file.txt").getStream();
      -
      -

      Accessing SP Extension Namespace

      -

      There are classes and interfaces included in extension modules, which you can access through a namespace, "SPNS".

      -
      import { SPNS } from "@pnp/nodejs-commonjs";
      -
      -const parser = new SPNS.StreamParser();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/nodejs/sp-fetch-client/index.html b/docs/v3/v2/nodejs/sp-fetch-client/index.html deleted file mode 100644 index 4a58274fb..000000000 --- a/docs/v3/v2/nodejs/sp-fetch-client/index.html +++ /dev/null @@ -1,2294 +0,0 @@ - - - - - - - - - - - - - - - - - - SPFetchClient - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/nodejs/spfetchclient

      -

      The SPFetchClient is used to authentication to SharePoint as a provider hosted add-in using a client and secret in nodejs. Remember it is not a good practice to expose client ids and secrets on the client and use of this class is intended for nodejs exclusively.

      -

      See: How to register a legacy SharePoint application

      -
      import { SPFetchClient } from "@pnp/nodejs";
      -import { sp } from "@pnp/sp/presets/all";
      -
      -sp.setup({
      -    sp: {
      -        fetchClientFactory: () => {
      -            return new SPFetchClient("{site url}", "{client id}", "{client secret}");
      -        },
      -    },
      -});
      -
      -// execute a library request as normal
      -const w = await sp.web();
      -
      -console.log(JSON.stringify(w, null, 4));
      -
      -

      Set Authentication Environment

      -

      For some areas such as Germany, China, and US Gov clouds you need to specify a different authentication url to the service. This is done by specifying the correct SPOAuthEnv enumeration to the SPFetchClient constructor. The options are listed below. If you are not sure which option to specify the default is likely OK.

      -
        -
      • SPO : (default) for all *.sharepoint.com urls
      • -
      • China: for China hosted cloud
      • -
      • Germany: for Germany local cloud
      • -
      • USDef: USA Defense cloud
      • -
      • USGov: USA Government cloud
      • -
      -
      import { sp } from "@pnp/sp/presets/all";
      -import { SPFetchClient, SPOAuthEnv } from "@pnp/nodejs";
      -
      -sp.setup({
      -    sp: {
      -        fetchClientFactory: () => {
      -            return new SPFetchClient("{site url}", "{client id}", "{client secret}", SPOAuthEnv.China);
      -        },
      -    },
      -});
      -
      -

      Set Realm

      -

      In some cases automatically resolving the realm may not work. In this case you can set the realm parameter in the SPFetchClient constructor. You can determine the correct value for the realm by navigating to https://{site name}-admin.sharepoint.com/_layouts/15/TA_AllAppPrincipals.aspx and copying the GUID value that appears after the "@" - this is the realm id.

      -
      import { sp } from "@pnp/sp/presets/all";
      -import { SPFetchClient, SPOAuthEnv } from "@pnp/nodejs";
      -
      -sp.setup({
      -    sp: {
      -        fetchClientFactory: () => {
      -            return new SPFetchClient("{site url}", "{client id}", "{client secret}", SPOAuthEnv.SPO, "{realm}");
      -        },
      -    },
      -});
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/npm-scripts/index.html b/docs/v3/v2/npm-scripts/index.html deleted file mode 100644 index 6946e972d..000000000 --- a/docs/v3/v2/npm-scripts/index.html +++ /dev/null @@ -1,2521 +0,0 @@ - - - - - - - - - - - - - - - - - - Npm Scripts - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      Supported NPM Scripts

      -

      As you likely are aware you can embed scripts within package.json. Using this capability coupled with the knowledge that pretty much all of the tools we use now support code files (.js/.ts) as configuration we have removed gulp from our tooling and now execute our various actions via scripts. This is not a knock on gulp, it remains a great tool, rather an opportunity for us to remove some dependencies.

      -

      This article outlines the current scripts we've implemented and how to use them, with available options and examples.

      -

      Start

      -

      Executes the serve command

      -
      npm start
      -
      -

      Serve

      -

      Starts a debugging server serving a bundled script with ./debug/serve/main.ts as the entry point. This allows you to run tests and debug code running within the context of a webpage rather than node.

      -
      npm run serve
      -
      -

      Test

      -

      Runs the tests and coverage for the library.

      -
      -

      Starting with 2.3.0 ONLY MSAL auth is supported for running the tests. More details on setting up MSAL for node.

      -
      -

      Options

      -

      There are several options you can provide to the test command. All of these need to be separated using a "--" double hyphen so they are passed to the spawned sub-commands.

      -

      Test a Single Package

      -
      -

      --package or -p

      -
      -

      This option will only run the tests associated with the package you specify. The values are the folder names within the ./packages directory.

      -
      # run only sp tests
      -npm test -- -p sp
      -
      -# run only logging tests
      -npm test -- -package logging
      -
      -

      Run a Single Test File

      -
      -

      --single or --s

      -
      -

      You can also run a specific file with a package. This option must be used with the single package option as you are essentially specifying the folder and file. This option uses either the flags.

      -
      # run only sp web tests
      -npm test -- -p sp -s web
      -
      -# run only graph groups tests
      -npm test -- -package graph -single groups
      -
      -

      Specify a Site

      -
      -

      --site

      -
      -

      By default every time you run the tests a new sub-site is created below the site specified in your settings file. You can choose to reuse a site for testing, which saves time when re-running a set of tests frequently. Testing content is not deleted after tests, so if you need to inspect the created content from testing you may wish to forgo this option.

      -

      This option can be used with any or none of the other testing options.

      -
      # run only sp web tests with a certain site
      -npm test -- -p sp -s web --site https://some.site.com/sites/dev
      -
      -

      Cleanup

      -
      -

      --cleanup

      -
      -

      If you include this flag the testing web will be deleted once tests are complete. Useful for local testing where you do not need to inspect the web once the tests are complete. Works with any of the other options, be careful when specifying a web using --site as it will be deleted.

      -
      # clean up our testing site
      -npm test -- --cleanup
      -
      -

      Logging

      -
      -

      --logging

      -
      -

      If you include this flag a console logger will be subscribed and the log level will be set to Info. This will provide console output for all the requests being made during testing. This flag is compatible with all other flags - however unless you are trying to debug a specific test this will produce a lot of chatty output.

      -
      # enable logging during testing
      -npm test -- --logging
      -
      -

      spVerbose

      -

      Added in 2.0.13

      -
      -

      --spverbose

      -
      -

      This flag will enable "verbose" OData mode for SharePoint tests. This flag is compatible with other flags.

      -
      npm test -- --spverbose
      -
      -

      build

      -

      Invokes the pnpbuild cli to transpile the TypeScript into JavaScript. All behavior is controlled via the tsconfig.json in the root of the project and sub folders as needed.

      -
      npm run build
      -
      -

      package

      -

      Invokes the pnpbuild cli to create the package directories under the dist folder. This will allow you to see exactly what will end up in the npm packages once they are published.

      -
      npm run package
      -
      -

      lint

      -

      Runs the linter.

      -
      npm run lint
      -
      -

      clean

      -

      Removes any generated folders from the working directory.

      -
      npm run clean
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/odata/caching/index.html b/docs/v3/v2/odata/caching/index.html deleted file mode 100644 index 8dd7143c4..000000000 --- a/docs/v3/v2/odata/caching/index.html +++ /dev/null @@ -1,2465 +0,0 @@ - - - - - - - - - - - - - - - - - - caching - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/queryable/caching

      -

      Often times data doesn't change that quickly, especially in the case of rolling up corporate news or upcoming events. These types of things can be cached for minutes if not hours. To help make caching easy you just need to insert the usingCaching method in your chain. This only applies to get requests. The usingCaching method can be used with the inBatch method as well to cache the results of batched requests.

      -

      The below examples uses the @pnp/sp library as the example - but this works equally well for any library making use of the @pnp/queryable base classes, such as @pnp/graph.

      -

      Basic example

      -

      You can use the method without any additional configuration. We have made some default choices for you and will discuss ways to override them later. The code below will get items from a list, first checking the cache for the value. You can also use it with OData operators such as top and orderBy. The usingCaching() method should always be the last method in the chain before the get() (OR if you are using batching these methods can be transposed, more details below).

      -
      import { sp } from "@pnp/sp";
      -
      -const r = await sp.web.lists.getByTitle("Tasks").items.usingCaching()();
      -console.log(r);
      -
      -const r2 = await sp.web.lists.getByTitle("Tasks").items.top(5).orderBy("Modified").usingCaching()();
      -console.log(r2);
      -
      -

      Globally Configure Cache Settings

      -

      If you would not like to use the default values, but don't want to clutter your code by setting the caching values on each request you can configure custom options globally. These will be applied to all calls to usingCaching() throughout your application.

      -
      import { sp } from "@pnp/sp";
      -
      -sp.setup({
      -    defaultCachingStore: "session", // or "local"
      -    defaultCachingTimeoutSeconds: 30,
      -    globalCacheDisable: false // or true to disable caching in case of debugging/testing
      -});
      -
      -const r = await sp.web.lists.getByTitle("Tasks").items.top(5).orderBy("Modified").usingCaching()();
      -console.log(r);
      -
      -

      Per Call Configuration

      -

      If you prefer more verbose code or have a need to manage the cache settings on a per request basis you can include individual caching settings for each request. These settings are passed to the usingCaching method call and are defined in the following interface. If you want to use the per-request options you must include the key.

      -
      export interface ICachingOptions {
      -    expiration?: Date;
      -    storeName?: "session" | "local";
      -    key: string;
      -}
      -
      -
      import { sp } from "@pnp/sp";
      -import { dateAdd } from "@pnp/core";
      -
      -const r = await sp.web.lists.getByTitle("Tasks").items.top(5).orderBy("Modified").usingCaching({
      -    expiration: dateAdd(new Date(), "minute", 20),
      -    key: "My Key",
      -    storeName: "local"
      -})();
      -console.log(r);
      -
      -

      Using Batching with Caching

      -

      You can use batching and caching together, but remember caching is only applied to get requests. When you use them together the methods can be transposed, the below example is valid.

      -
      import { sp } from "@pnp/sp";
      -
      -let batch = sp.createBatch();
      -
      -sp.web.lists.inBatch(batch).usingCaching()().then(r => {
      -    console.log(r)
      -});
      -
      -sp.web.lists.getByTitle("Tasks").items.usingCaching().inBatch(batch)().then(r => {
      -    console.log(r)
      -});
      -
      -batch.execute().then(() => console.log("All done!"));
      -
      -

      Implement Custom Caching

      -

      You may desire to use a different caching strategy than the one we implemented within the library. The easiest way to achieve this is to wrap the request in your custom caching functionality using the unresolved promise as needed. Here we show how to implement the Stale While Revalidate pattern as discussed here.

      -

      Implement caching helper method

      -

      We create a map to act as our cache storage and a function to wrap the request caching logic

      -
      const map = new Map<string, any>();
      -
      -async function staleWhileRevalidate<T>(key: string, p: Promise<T>): Promise<T> {
      -
      -    if (map.has(key)) {
      -
      -        // In Cache
      -        p.then(u => {
      -            // Update Cache once we have a result
      -            map.set(key, u);
      -        });
      -
      -        // Return from Cache
      -        return map.get(key);
      -    }
      -
      -    // Not In Cache so we need to wait for the value
      -    const r = await p;
      -
      -    // Set Cache
      -    map.set(key, r);
      -
      -    // Return from Promise
      -    return r;
      -}
      -
      -

      Usage

      -
      -

      Don't call usingCaching just apply the helper method

      -
      -
      // this one will wait for the request to finish
      -const r1 = await staleWhileRevalidate("test1", sp.web.select("Title", "Description")());
      -
      -console.log(JSON.stringify(r1, null, 2));
      -
      -// this one will return the result from cache and then update the cache in the background
      -const r2 = await staleWhileRevalidate("test1", sp.web.select("Title", "Description")());
      -
      -console.log(JSON.stringify(r2, null, 2));
      -
      -

      Wrapper Function

      -

      You can wrap this call into a single function you can reuse within your application each time you need the web data for example. You can update the select and interface to match your needs as well.

      -
      interface WebData {
      -    Title: string;
      -    Description: string;
      -}
      -
      -function getWebData(): Promise<WebData> {
      -
      -    return staleWhileRevalidate("test1", sp.web.select("Title", "Description")());
      -}
      -
      -
      -// this one will wait for the request to finish
      -const r1 = await getWebData();
      -
      -console.log(JSON.stringify(r1, null, 2));
      -
      -// this one will return the result from cache and then update the cache in the background
      -const r2 = await getWebData();
      -
      -console.log(JSON.stringify(r2, null, 2));
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/odata/core/index.html b/docs/v3/v2/odata/core/index.html deleted file mode 100644 index 0083c8923..000000000 --- a/docs/v3/v2/odata/core/index.html +++ /dev/null @@ -1,2322 +0,0 @@ - - - - - - - - - - - - - - - - - - core - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/queryable/core

      -

      This module contains shared interfaces and abstract classes used within the @pnp/queryable package and those items that inherit from it.

      -

      ProcessHttpClientResponseException

      -

      The exception thrown when a response is returned and cannot be processed.

      -

      interface ODataParser

      -

      Base interface used to describe a class that that will parse incoming responses. It takes a single type parameter representing the type of the -value to be returned. It has two methods, one is optional:

      -
        -
      • parse(r: Response): Promise - main method use to parse a response and return a Promise resolving to an object of type T
      • -
      • hydrate?: (d: any) => T - optional method used when getting an object from the cache if it requires calling a constructor
      • -
      -

      ODataParserBase

      -

      The base class used by all parsers in the @pnp libraries. It is optional to use when creating your own custom parsers, but does contain several helper methods.

      -

      Create a custom parser from ODataParserBase

      -

      You can always create custom parsers for your projects, however it is likely you will not require this step as the default parsers should work for most cases.

      -
      class MyParser extends ODataParserBase<any> {
      -
      -    // we need to override the parse method to do our custom stuff
      -    public parse(r: Response): Promise<T> {
      -
      -        // we wrap everything in a promise
      -        return new Promise((resolve, reject) => {
      -
      -            // lets use the default error handling which returns true for no error
      -            // and will call reject with an error if one exists
      -            if (this.handleError(r, reject)) {
      -
      -                // now we add our custom parsing here
      -                r.text().then(txt => {
      -                    // here we call a made up function to parse the result
      -                    // this is where we would do our parsing as required
      -                    myCustomerUnencode(txt).then(v => {
      -                        resolve(v);
      -                    });
      -                });
      -            }
      -        });
      -    }
      -}
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/odata/debug/index.html b/docs/v3/v2/odata/debug/index.html deleted file mode 100644 index 5cfcbd1d1..000000000 --- a/docs/v3/v2/odata/debug/index.html +++ /dev/null @@ -1,2358 +0,0 @@ - - - - - - - - - - - - - - - - - - Debugging Proxy Objects - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      Debugging Proxy Objects

      -

      Because all queryables are now represented as Proxy objects you can't immediately see the properties/method of the object or the data stored about the request. In certain debugging scenarios it can help to get visibility into the object that is wrapped by the proxy. To enable this we provide a set of extensions to help.

      -

      The debug extensions are added by including the import "@pnp/queryable/debug"; statement in your project. It should be removed for production. This module provides several methods to help with debugging Queryable Proxy objects.

      -

      Unwrap

      -

      The __unwrap() method returns the concrete Queryable instance wrapped by the Proxy. You can then examine this object in various ways or dump it to the console for debugging.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/queryable/debug";
      -
      -// unwrap the underlying concrete queryable instance
      -const unwrapped = sp.web.__unwrap();
      -
      -console.log(JSON.stringify(unwrapped, null, 2));
      -
      -
      -

      Note: It is not supported to unwrap objects and then use them. It may work in some cases, but this behavior may change as what is contained with the Proxy is an implementation detail and should not be relied upon. Without the Proxy wrapper we make no guarantees.

      -
      -

      Data

      -

      All of the information related to a queryable's request is contained within the "data" property. If you need to grab that information you can use the __data property.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/queryable/debug";
      -
      -// get the underlying queryable's data
      -const data = sp.web.__data;
      -
      -console.log(JSON.stringify(data, null, 2));
      -
      -

      JSON

      -

      You can also get a representation of the wrapped instance in JSON format consisting of all its own properties and values.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/queryable/debug";
      -
      -// get the underlying queryable's as JSON
      -const data = sp.web.__json();
      -
      -console.log(JSON.stringify(data, null, 2));
      -
      -

      Deep Trace

      -

      Deep tracing is the ability to write every property and method access to the log. This produces VERY verbose output but can be helpful in situations where you need to trace how things are called and when within the Proxy. You enable deep tracing using the __enableDeepTrace method and disable using __disableDeepTrace.

      -
      import { Logger, ConsoleListener } from "@pnp/logging";
      -import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/queryable/debug";
      -
      -Logger.subscribe(new ConsoleListener());
      -
      -// grab an instance to enable deep trace
      -const web = sp.web;
      -
      -// enable deep trace on the instance
      -web.__enableDeepTrace();
      -
      -const y = await web.lists();
      -
      -// disable deep trace
      -web.__disableDeepTrace();
      -
      -

      The example above produces the following output:

      -
      Message: get ::> lists
      -Message: get ::> lists
      -Message: get ::> toUrl
      -Message: get ::> toUrl
      -Message: get ::> data
      -Message: get ::> data
      -Message: get ::> _data
      -Message: get ::> query
      -Message: get ::> query
      -Message: get ::> data
      -Message: get ::> data
      -Message: get ::> _data
      -Message: get ::> _data
      -Message: get ::> data
      -Message: get ::> data
      -Message: get ::> _data
      -Message: get ::> _data
      -Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122352) Beginning GET request (_api/web/lists) Data: {}
      -Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122352) Beginning GET request (_api/web/lists) Data: {}
      -Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122354) Sending request.
      -Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122354) Sending request.
      -Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124099) Completing GET request. Data: {}
      -Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124099) Completing GET request. Data: {}
      -Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124102) Returning result from pipeline. Set logging to verbose to see data. Data: {}
      -Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124102) Returning result from pipeline. Set logging to verbose to see data. Data: {}
      -Message: get ::> __disableDeepTrace
      -Message: get ::> __disableDeepTrace
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/odata/extensions/index.html b/docs/v3/v2/odata/extensions/index.html deleted file mode 100644 index c250324dc..000000000 --- a/docs/v3/v2/odata/extensions/index.html +++ /dev/null @@ -1,2581 +0,0 @@ - - - - - - - - - - - - - - - - - - Extending an OData library - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      Extensions

      -

      introduced in 2.0.0

      -

      Extending is the concept of overriding or adding functionality into an object or environment without altering the underlying class instances. This can be useful for debugging, testing, or injecting custom functionality. Extensions work with any invocable. You can control any behavior of the library with extensions.

      -
      -

      Extensions do not work in ie11 compatibility mode. This is by design.

      -
      -

      Types of Extensions

      -

      There are three types of Extensions available as well as three methods for registration. You can register any type of extension with any of the registration options.

      -

      Function Extensions

      -

      The first type is a simple function with a signature:

      -
      (op: "apply" | "get" | "has" | "set", target: T, ...rest: any[]): void
      -
      -

      This function is passed the current operation as the first argument, currently one of "apply", "get", "has", or "set". The second argument is the target instance upon which the operation is being invoked. The remaining parameters vary by the operation being performed, but will match their respective ProxyHandler method signatures.

      -

      Named Extensions

      -

      Named extensions are designed to add or replace a single property or method, though you can register multiple using the same object. These extensions are defined by using an object which has the property/methods you want to override described. Registering named extensions globally will override that operation to all invokables.

      -
      import { extendFactory } from "@pnp/queryable";
      -import { sp, List, Lists, IWeb, ILists, List, IList, Web } from "@pnp/sp/presets/all";
      -import { escapeQueryStrValue } from "@pnp/sp/utils/escapeQueryStrValue";
      -
      -// create a plain object with the props and methods we want to add/change
      -const myExtensions = {
      -    // override the lists property
      -    get lists(this: IWeb): ILists {
      -        // we will always order our lists by title and select just the Title for ALL calls (just as an example)
      -        return Lists(this).orderBy("Title").select("Title");
      -    },
      -    // override the getByTitle method
      -    getByTitle: function (this: ILists, title: string): IList {
      -        // in our example our list has moved, so we rewrite the request on the fly
      -        if (title === "List1") {
      -            return List(this, `getByTitle('List2')`);
      -        } else {
      -            // you can't at this point call the "base" method as you will end up in loop within the proxy
      -            // so you need to ensure you patch/include any original functionality you need
      -            return List(this, `getByTitle('${escapeQueryStrValue(title)}')`);
      -        }
      -    },
      -};
      -
      -// register all the named Extensions
      -extendFactory(Web, myExtensions);
      -
      -// this will use our extension to ensure the lists are ordered
      -const lists = await sp.web.lists();
      -
      -console.log(JSON.stringify(lists, null, 2));
      -
      -// we will get the items from List1 but within the extension it is rewritten as List2
      -const items = await sp.web.lists.getByTitle("List1").items();
      -
      -console.log(JSON.stringify(items.length, null, 2));
      -
      -

      ProxyHandler Extensions

      -

      You can also register a partial ProxyHandler implementation as an extension. You can implement one or more of the ProxyHandler methods as needed. Here we implement the same override of getByTitle globally. This is the most complicated method of creating an extension and assumes an understanding of how ProxyHandlers work.

      -
      import { extendFactory } from "@pnp/queryable";
      -import { sp, Lists, IWeb, ILists, Web } from "@pnp/sp/presets/all";
      -import { escapeQueryStrValue } from "@pnp/sp/utils/escapeSingleQuote";
      -
      -const myExtensions = {
      -    get: (target, p: string | number | symbol, _receiver: any) => {
      -        switch (p) {
      -            case "getByTitle":
      -                return (title: string) => {
      -
      -                    // in our example our list has moved, so we rewrite the request on the fly
      -                    if (title === "LookupList") {
      -                        return List(target, `getByTitle('OrderByList')`);
      -                    } else {
      -                        // you can't at this point call the "base" method as you will end up in loop within the proxy
      -                        // so you need to ensure you patch/include any original functionality you need
      -                        return List(target, `getByTitle('${escapeQueryStrValue(title)}')`);
      -                    }
      -                };
      -        }
      -    },
      -};
      -
      -extendFactory(Web, myExtensions);
      -
      -const lists = sp.web.lists;
      -const items = await lists.getByTitle("LookupList").items();
      -
      -console.log(JSON.stringify(items.length, null, 2));
      -
      -

      Registering Extensions

      -

      You can register Extensions either globally, on an invocable factory, or on a per-object basis, and you can register a single extension or an array of Extensions.

      -

      Global Registration

      -

      Globally registering an extension allows you to inject functionality into every invocable that is instantiated within your application. It is important to remember that processing extensions happens on ALL property access and method invocation operations - so global extensions should be used sparingly.

      -
      import { extendGlobal } from "@pnp/queryable";
      -
      -// we can add a logging method to very verbosely track what things are called in our application
      -extendGlobal((op: string, _target: any, ...rest: any[]): void => {
      -        switch (op) {
      -            case "apply":
      -                Logger.write(`${op} ::> ()`, LogLevel.Info);
      -                break;
      -                case "has":
      -            case "get":
      -            case "set":
      -                Logger.write(`${op} ::> ${rest[0]}`, LogLevel.Info);
      -                break;
      -            default:
      -                Logger.write(`unknown ${op}`, LogLevel.Info);
      -        }
      -});
      -
      -

      Factory Registration

      -

      The pattern you will likely find most useful is the ability to extend an invocable factory. This will apply your extensions to all instances created with that factory, meaning all IWebs or ILists will have the extension methods. The example below shows how to add a property to IWeb as well as a method to IList.

      -
      import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import { IWeb, Web } from "@pnp/sp/webs";
      -import { ILists, Lists } from "@pnp/sp/lists";
      -import { extendFactory } from "@pnp/queryable";
      -import { sp } from "@pnp/sp";
      -
      -// sets up the types correctly when importing across your application
      -declare module "@pnp/sp/webs/types" {
      -
      -    // we need to extend the interface
      -    interface IWeb {
      -        orderedLists: ILists;
      -    }
      -}
      -
      -// sets up the types correctly when importing across your application
      -declare module "@pnp/sp/lists/types" {
      -
      -    // we need to extend the interface
      -    interface ILists {
      -        getOrderedListsQuery: (this: ILists) => ILists;
      -    }
      -}
      -
      -extendFactory(Web, {
      -    // add an ordered lists property
      -    get orderedLists(this: IWeb): ILists {
      -        return this.lists.getOrderedListsQuery();
      -    },
      -});
      -
      -extendFactory(Lists, {
      -    // add an ordered lists property
      -    getOrderedListsQuery(this: ILists): ILists {
      -        return this.top(10).orderBy("Title").select("Title");
      -    },
      -});
      -
      -// regardless of how we access the web and lists collections our extensions remain with all new instance based on
      -const web = Web("https://tenant.sharepoint.com/sites/dev/");
      -const lists1 = await web.orderedLists();
      -console.log(JSON.stringify(lists1, null, 2));
      -
      -const lists2 = await Web("https://tenant.sharepoint.com/sites/dev/").orderedLists();
      -console.log(JSON.stringify(lists2, null, 2));
      -
      -const lists3 = await sp.web.orderedLists();
      -console.log(JSON.stringify(lists3, null, 2));
      -
      -

      Instance Registration

      -

      You can also register Extensions on a single object instance, which is often the preferred approach as it will have less of a performance impact across your whole application. This is useful for debugging, overriding methods/properties, or controlling the behavior of specific object instances.

      -
      -

      Extensions are not transferred to child objects in a fluent chain, be sure you are extending the instance you think you are.

      -
      -

      Here we show the same override operation of getByTitle on the lists collection, but safely only overriding the single instance.

      -
      import { extendObj } from "@pnp/queryable";
      -import { sp, List, ILists } from "@pnp/sp/presets/all";
      -
      -const myExtensions = {
      -    getByTitle: function (this: ILists, title: string) {
      -        // in our example our list has moved, so we rewrite the request on the fly
      -        if (title === "List1") {
      -            return List(this, "getByTitle('List2')");
      -        } else {
      -            // you can't at this point call the "base" method as you will end up in loop within the proxy
      -            // so you need to ensure you patch/include any original functionality you need
      -            return List(this, `getByTitle('${escapeQueryStrValue(title)}')`);
      -        }
      -    },
      -};
      -
      -const lists =  extendObj(sp.web.lists, myExtensions);
      -const items = await lists.getByTitle("LookupList").items();
      -
      -console.log(JSON.stringify(items.length, null, 2));
      -
      -

      Enable & Disable Extensions and Clear Global Extensions

      -

      Extensions are automatically enabled when you set an extension through any of the above outlined methods. You can disable and enable extensions on demand if needed.

      -
      import { enableExtensions, disableExtensions, clearGlobalExtensions } from "@pnp/queryable";
      -
      -// disable Extensions
      -disableExtensions();
      -
      -// enable Extensions
      -enableExtensions();
      -
      -// clear all the globally registered extensions
      -clearGlobalExtensions();
      -
      -

      Order of Operations

      -

      It is important to understand the order in which extensions are executed and when a value is returned. Instance extensions* are always called first, followed by global Extensions - in both cases they are called in the order they were registered. This allows you to perhaps have some global functionality while maintaining the ability to override it again at the instance level. IF an extension returns a value other than undefined that value is returned and no other extensions are processed.

      -
      -

      *extensions applied via an extended factory are considered instance extensions

      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/odata/index.html b/docs/v3/v2/odata/index.html deleted file mode 100644 index 705e73326..000000000 --- a/docs/v3/v2/odata/index.html +++ /dev/null @@ -1,2259 +0,0 @@ - - - - - - - - - - - - - - - - - - odata - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/queryable

      -

      npm version

      -

      This modules contains the abstract core classes used to process odata requests. They can also be used to build your own odata -library should you wish to. By sharing the core functionality across libraries we can provide a consistent API as well as ensure -the core code is solid and well tested, with any updates benefitting all inheriting libraries.

      -

      Getting Started

      -

      Install the library and required dependencies

      -

      npm install @pnp/logging @pnp/core @pnp/queryable --save

      -

      Library Topics

      - - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/odata/odata-batch/index.html b/docs/v3/v2/odata/odata-batch/index.html deleted file mode 100644 index 334e7ac4d..000000000 --- a/docs/v3/v2/odata/odata-batch/index.html +++ /dev/null @@ -1,2250 +0,0 @@ - - - - - - - - - - - - - - - - - - OData Batching - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/queryable/odatabatch

      -

      This module contains an abstract class used as a base when inheriting libraries support batching.

      -

      ODataBatchRequestInfo

      -

      This interface defines what each batch needs to know about each request. It is generic in that any library can provide the information but will -be responsible for processing that info by implementing the abstract executeImpl method.

      -

      ODataBatch

      -

      Base class for building batching support for a library inheriting from @pnp/queryable. You can see implementations of this abstract class in the @pnp/sp -and @pnp/graph modules.

      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/odata/parsers/index.html b/docs/v3/v2/odata/parsers/index.html deleted file mode 100644 index 76fc533e6..000000000 --- a/docs/v3/v2/odata/parsers/index.html +++ /dev/null @@ -1,2355 +0,0 @@ - - - - - - - - - - - - - - - - - - Parsers - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/queryable/parsers

      -

      This modules contains a set of generic parsers. These can be used or extended as needed, though it is likely in most cases the default parser will be all you need.

      -

      ODataDefaultParser

      -

      The simplest parser used to transform a Response into its JSON representation. The default parser will handle errors in a consistent manner throwing an HttpRequestError instance. This class extends Error and adds the response, status, and statusText properties. The response object is unread. You can use this custom error as shown below to gather more information about what went wrong in the request.

      -
      import { sp } from "@pnp/sp";
      -import { JSONParser } from "@pnp/queryable";
      -
      -try {
      -
      -    const parser = new JSONParser();
      -
      -    // this always throws a 404 error
      -    await sp.web.getList("doesn't exist").get(parser);
      -
      -} catch (e) {
      -
      -    // we can check for the property "isHttpRequestError" to see if this is an instance of our class
      -    // this gets by all the many limitations of subclassing Error and type detection in JavaScript
      -    if (e.hasOwnProperty("isHttpRequestError")) {
      -
      -        console.log("e is HttpRequestError");
      -
      -        // now we can access the various properties and make use of the response object.
      -        // at this point the body is unread
      -        console.log(`status: ${e.status}`);
      -        console.log(`statusText: ${e.statusText}`);
      -
      -        const json = await e.response.clone().json();
      -        console.log(JSON.stringify(json));
      -        const text = await e.response.clone().text();
      -        console.log(text);
      -        const headers = e.response.headers;
      -    }
      -
      -    console.error(e);
      -}
      -
      -

      TextParser

      -

      Specialized parser used to parse the response using the .text() method with no other processing. Used primarily for files.

      -

      BlobParser

      -

      Specialized parser used to parse the response using the .blob() method with no other processing. Used primarily for files.

      -

      JSONParser

      -

      Specialized parser used to parse the response using the .json() method with no other processing. Used primarily for files.

      -

      BufferParser

      -

      Specialized parser used to parse the response using the .arrayBuffer() [node] for .buffer() [browser] method with no other processing. Used primarily for files.

      -

      LambdaParser

      -

      Allows you to pass in any handler function you want, called if the request does not result in an error that transforms the raw, unread request into the result type.

      -
      import { LambdaParser } from "@pnp/queryable";
      -import { sp } from "@pnp/sp";
      -
      -// here a simple parser duplicating the functionality of the JSONParser
      -const parser = new LambdaParser((r: Response) => r.json());
      -
      -const webDataJson = await sp.web.get(parser);
      -
      -console.log(webDataJson);
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/odata/pipeline/index.html b/docs/v3/v2/odata/pipeline/index.html deleted file mode 100644 index 2f8094dde..000000000 --- a/docs/v3/v2/odata/pipeline/index.html +++ /dev/null @@ -1,2297 +0,0 @@ - - - - - - - - - - - - - - - - - - Pipeline - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/queryable/pipeline

      -

      All of the odata requests processed by @pnp/queryable pass through an extensible request pipeline. Each request is executed in a specific request context defined by the RequestContext<T> interface with the type parameter representing the type ultimately returned at the end a successful processing through the pipeline. Unless you are writing a pipeline method it is unlikely you will ever interact directly with the request pipeline.

      -

      interface RequestContext<T>

      -

      The interface that defines the context within which all requests are executed. Note that the pipeline methods to be executed are part of the context. This allows full control over the methods called during a request, and allows for the insertion of any custom methods required.

      -
      interface RequestContext<T> {
      -    batch: ODataBatch;
      -    batchDependency: () => void;
      -    cachingOptions: ICachingOptions;
      -    hasResult?: boolean;
      -    isBatched: boolean;
      -    isCached: boolean;
      -    options: FetchOptions;
      -    parser: ODataParser<T>;
      -    pipeline: Array<(c: RequestContext<T>) => Promise<RequestContext<T>>>;
      -    requestAbsoluteUrl: string;
      -    requestId: string;
      -    result?: T;
      -    verb: string;
      -    clientFactory: () => RequestClient;
      -}
      -
      -

      requestPipelineMethod decorator

      -

      The requestPipelineMethod decorator is used to tag a pipeline method and add functionality to bypass processing if a result is already present in the pipeline. If you would like your method to always run regardless of the existence of a result you can pass true to ensure it will always run. Each pipeline method takes a single argument of the current RequestContext and returns a promise resolving to the RequestContext updated as needed.

      -
      @requestPipelineMethod(true)
      -public static myPipelineMethod<T>(context: RequestContext<T>): Promise<RequestContext<T>> {
      -
      -    return new Promise<RequestContext<T>>(resolve => {
      -
      -        // do something
      -
      -        resolve(context);
      -    });
      -}
      -
      -

      Default Pipeline

      -
        -
      1. logs the start of the request
      2. -
      3. checks the cache for a value based on the context's cache settings
      4. -
      5. sends the request if no value from found in the cache
      6. -
      7. logs the end of the request
      8. -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/odata/queryable/index.html b/docs/v3/v2/odata/queryable/index.html deleted file mode 100644 index 6778cab75..000000000 --- a/docs/v3/v2/odata/queryable/index.html +++ /dev/null @@ -1,2455 +0,0 @@ - - - - - - - - - - - - - - - - - - Queryable - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/queryable/queryable

      -

      The Queryable class is the base class for all of the libraries building fluent request apis.

      -

      abstract class ODataQueryable<BatchType extends ODataBatch>

      -

      This class takes a single type parameter representing the type of the batch implementation object. If your api will not support batching you can create a dummy class here and simply not use the batching calls.

      -

      Properties

      -

      query

      -

      Provides access to the query string builder for this url

      -

      Public Methods

      -

      concat

      -

      Directly concatenates the supplied string to the current url, not normalizing "/" chars

      -

      configure

      -

      Sets custom options for current object and all derived objects accessible via chaining

      -
      import { ConfigOptions } from "@pnp/queryable";
      -import { sp } from "@pnp/sp";
      -
      -const headers: ConfigOptions = {
      -    Accept: 'application/json;odata=nometadata'
      -};
      -
      -// here we use configure to set the headers value for all child requests of the list instance
      -const list = sp.web.lists.getByTitle("List1").configure({ headers });
      -
      -// this will use the values set in configure
      -list.items().then(items => console.log(JSON.stringify(items, null, 2));
      -
      -

      For reference the ConfigOptions interface is shown below:

      -
      export interface ConfigOptions {
      -    headers?: string[][] | { [key: string]: string } | Headers;
      -    mode?: "navigate" | "same-origin" | "no-cors" | "cors";
      -    credentials?: "omit" | "same-origin" | "include";
      -    cache?: "default" | "no-store" | "reload" | "no-cache" | "force-cache" | "only-if-cached";
      -}
      -
      -

      configureFrom

      -

      Sets custom options from another queryable instance's options. Identical to configure except the options are derived from the supplied instance.

      -

      usingCaching

      -

      Enables caching for this request. See caching for more details.

      -
      import { sp } from "@pnp/sp"
      -
      -sp.web.usingCaching()().then(...);
      -
      -

      inBatch

      -

      Adds this query to the supplied batch

      -

      toUrl

      -

      Gets the current url

      -

      abstract toUrlAndQuery()

      -

      When implemented by an inheriting class will build the full url with appropriate query string used to make the actual request

      -

      get

      -

      Execute the current request. Takes an optional type parameter allowing for the typing of the value or the user of parsers that will create specific object instances.

      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/packages/index.html b/docs/v3/v2/packages/index.html deleted file mode 100644 index f22b360a2..000000000 --- a/docs/v3/v2/packages/index.html +++ /dev/null @@ -1,2251 +0,0 @@ - - - - - - - - - - - - - - - - - - Packages - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      Packages

      -

      The following packages comprise the Patterns and Practices client side libraries. All of the packages are published as a set and depend on their peers within the @pnp scope.

      -

      The latest published version is npm version.

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      @pnp/
      adaljsclientProvides an adaljs wrapper suitable for use with PnPjs
      commonProvides shared functionality across all pnp libraries
      config-storeProvides a way to manage configuration within your application
      graphProvides a fluent api for working with Microsoft Graph
      loggingLight-weight, subscribable logging framework
      msaljsclientProvides an msal wrapper suitable for use with PnPjs
      nodejsProvides functionality enabling the @pnp libraries within nodejs
      odataProvides shared odata functionality and base classes
      spProvides a fluent api for working with SharePoint REST
      sp-addinhelpersProvides functionality for working within SharePoint add-ins
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/pnpjs/index.html b/docs/v3/v2/pnpjs/index.html deleted file mode 100644 index f0c0222c4..000000000 --- a/docs/v3/v2/pnpjs/index.html +++ /dev/null @@ -1,2281 +0,0 @@ - - - - - - - - - - - - - - - - - - pnpjs - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      PnPjs

      -

      This package is a rollup package of all the other libraries for scenarios where you would prefer to access all of the code from a single file. Examples would be importing a single file into a script editor webpart or using the library in other ways that benefit from a single file. You will not be able to take advantage of selective imports using this bundle.

      -
      -

      Our recommendation is to import the packages directly into your project, or to create a custom bundle. This package is mostly provided to help folks with backward-compatibility needs.

      -
      -

      Script Editor Webpart

      -

      The below is an example of using the pnp.js bundle within a Script Editor webpart. This script editor example is provided for folks on older version of SharePoint - when possible your first choice is SharePoint Framework.

      -

      You will need to grab the pnp.js bundle file from the dist folder of the pnpjs package and upload it to a location where you can reference it from without your script editor webparts.

      -
      -

      *This is included as a reference for backward compatibility. The script editor webpart is no longer available in SharePoint online. In addition, see our General Statement on Polyfills and IE11

      -
      -
      <script src="https://mytenant.sharepoint.com/sites/dev/Shared%20Documents/pnp2bundle/pnp.js"></script>
      -<!-- Optional to include the IE11 polyfill package -->
      -<script src="https://unpkg.com/@pnp/polyfill-ie11"></script>
      -<script>
      -
      -document.onreadystatechange = async function() {
      -
      -    if(document.readyState === "complete") {
      -
      -        // because this is a UMD bundle there is a global root object named "pnp"
      -        const a = await pnp.sp.web.lists();
      -        document.getElementById("pnpexample").innerHTML = JSON.stringify(a);
      -    }
      -}
      -</script>
      -<div id="pnpexample"></div>
      -
      -

      Access Library Features

      -

      Within the bundle all of the classes and methods are exported at the root object, with the exports from sp and graph libraries contained with NS variables to avoid naming conflicts. So if you need to access say the "Web" factory you can do so:

      -
      const web = pnp.SPNS.Web("https://something.sharepoint.com");
      -const lists = await web.lists();
      -
      -
      pnp.GraphNS.*
      -
      -

      Individual libraries can also be accessed for their exports:

      -
      pnp.Logger.subscribe(new pnp.ConsoleListener());
      -pnp.log.write("hello");
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/search/search_index.json b/docs/v3/v2/search/search_index.json deleted file mode 100644 index fb2111733..000000000 --- a/docs/v3/v2/search/search_index.json +++ /dev/null @@ -1 +0,0 @@ -{"config":{"lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"PnPjs is a collection of fluent libraries for consuming SharePoint, Graph, and Office 365 REST APIs in a type-safe way. You can use it within SharePoint Framework, Nodejs, or any JavaScript project. This an open source initiative and we encourage contributions and constructive feedback from the community. Animation of the library in use, note intellisense help in building your queries General Guidance \u00b6 These articles provide general guidance for working with the libraries. If you are migrating from v1 please review the transition guide . Getting Started Authentication Get Started Contributing npm scripts Polyfills Packages \u00b6 Patterns and Practices client side libraries (PnPjs) are comprised of the packages listed below. All of the packages are published as a set and depend on their peers within the @pnp scope. The latest published version is . @pnp/ adaljsclient Provides an adaljs wrapper suitable for use with PnPjs common Provides shared functionality across all pnp libraries config-store Provides a way to manage configuration within your application graph Provides a fluent api for working with Microsoft Graph logging Light-weight, subscribable logging framework msaljsclient Provides an msal wrapper suitable for use with PnPjs nodejs Provides functionality enabling the @pnp libraries within nodejs odata Provides shared odata functionality and base classes sp Provides a fluent api for working with SharePoint REST sp-addinhelpers Provides functionality for working within SharePoint add-ins Authentication \u00b6 We have a new section dedicated to helping you figure out the best way to handle authentication in your application, check it out! Issues, Questions, Ideas \u00b6 Please log an issue using our template as a guide. This will let us track your request and ensure we respond. We appreciate any constructive feedback, questions, ideas, or bug reports with our thanks for giving back to the project. Changelog \u00b6 Please review the CHANGELOG for release details on all library changes. Code of Conduct \u00b6 This project has adopted the Microsoft Open Source Code of Conduct . For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments. \"Sharing is Caring\" \u00b6 Please use http://aka.ms/sppnp for the latest updates around the whole SharePoint Patterns and Practices (PnP) program . Disclaimer \u00b6 THIS CODE IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.","title":"Home"},{"location":"#general-guidance","text":"These articles provide general guidance for working with the libraries. If you are migrating from v1 please review the transition guide . Getting Started Authentication Get Started Contributing npm scripts Polyfills","title":"General Guidance"},{"location":"#packages","text":"Patterns and Practices client side libraries (PnPjs) are comprised of the packages listed below. All of the packages are published as a set and depend on their peers within the @pnp scope. The latest published version is . @pnp/ adaljsclient Provides an adaljs wrapper suitable for use with PnPjs common Provides shared functionality across all pnp libraries config-store Provides a way to manage configuration within your application graph Provides a fluent api for working with Microsoft Graph logging Light-weight, subscribable logging framework msaljsclient Provides an msal wrapper suitable for use with PnPjs nodejs Provides functionality enabling the @pnp libraries within nodejs odata Provides shared odata functionality and base classes sp Provides a fluent api for working with SharePoint REST sp-addinhelpers Provides functionality for working within SharePoint add-ins","title":"Packages"},{"location":"#authentication","text":"We have a new section dedicated to helping you figure out the best way to handle authentication in your application, check it out!","title":"Authentication"},{"location":"#issues-questions-ideas","text":"Please log an issue using our template as a guide. This will let us track your request and ensure we respond. We appreciate any constructive feedback, questions, ideas, or bug reports with our thanks for giving back to the project.","title":"Issues, Questions, Ideas"},{"location":"#changelog","text":"Please review the CHANGELOG for release details on all library changes.","title":"Changelog"},{"location":"#code-of-conduct","text":"This project has adopted the Microsoft Open Source Code of Conduct . For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.","title":"Code of Conduct"},{"location":"#sharing-is-caring","text":"Please use http://aka.ms/sppnp for the latest updates around the whole SharePoint Patterns and Practices (PnP) program .","title":"\"Sharing is Caring\""},{"location":"#disclaimer","text":"THIS CODE IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.","title":"Disclaimer"},{"location":"SPFx-on-premises/","text":"Workaround for on-premises SPFx TypeScript Version (SharePoint 2016 or 2019) \u00b6 Note this article applies to version 1.4.1 SharePoint Framework projects targeting on-premises only. When using the Yeoman generator to create a SharePoint Framework 1.4.1 project targeting on-premises it installs TypeScript version 2.2.2 (SP2016) or 2.4.2/2.4.1 (SP2019). Unfortunately this library relies on 3.6.4 or later due to extensive use of default values for generic type parameters in the libraries. To work around this limitation you can follow the steps in this article. npm i npm i -g rimraf # used to remove the node_modules folder (much better/faster) Ensure that the @pnp/sp package is already installed npm i @pnp/sp Remove the package-lock.json file & node_modules rimraf node_modules folder and execute npm install Open package-lock.json from the root folder Search for \"typescript\" or similar with version 2.4.1 (SP2019) 2.2.2 (SP2016) Replace \"2.4.1\" or \"2.2.2\" with \"3.6.4\" Search for the next \"typescript\" occurrence and replace the block with: JSON \"typescript\": { \"version\": \"3.6.4\", \"resolved\": \"https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz\", \"integrity\": \"sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==\", \"dev\": true } Remove node_modules folder rimraf node_modules Run npm install Alternative using npm-force-resolutions \u00b6 Install resolutions package and TypeScript providing considered version explicitly: bash npm i -D npm-force-resolutions typescript@3.6.4 Add a resolution for TypeScript and preinstall script into package.json to a corresponding code blocks: JSON { \"scripts\": { \"preinstall\": \"npx npm-force-resolutions\" }, \"resolutions\": { \"typescript\": \"3.6.4\" } } Run npm install to trigger preinstall script and bumping TypeScript version into package-lock.json Run npm run build , should produce no errors Installing additional dependencies should be safe then.","title":"SPFx On-Premises"},{"location":"SPFx-on-premises/#workaround-for-on-premises-spfx-typescript-version-sharepoint-2016-or-2019","text":"Note this article applies to version 1.4.1 SharePoint Framework projects targeting on-premises only. When using the Yeoman generator to create a SharePoint Framework 1.4.1 project targeting on-premises it installs TypeScript version 2.2.2 (SP2016) or 2.4.2/2.4.1 (SP2019). Unfortunately this library relies on 3.6.4 or later due to extensive use of default values for generic type parameters in the libraries. To work around this limitation you can follow the steps in this article. npm i npm i -g rimraf # used to remove the node_modules folder (much better/faster) Ensure that the @pnp/sp package is already installed npm i @pnp/sp Remove the package-lock.json file & node_modules rimraf node_modules folder and execute npm install Open package-lock.json from the root folder Search for \"typescript\" or similar with version 2.4.1 (SP2019) 2.2.2 (SP2016) Replace \"2.4.1\" or \"2.2.2\" with \"3.6.4\" Search for the next \"typescript\" occurrence and replace the block with: JSON \"typescript\": { \"version\": \"3.6.4\", \"resolved\": \"https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz\", \"integrity\": \"sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==\", \"dev\": true } Remove node_modules folder rimraf node_modules Run npm install","title":"Workaround for on-premises SPFx TypeScript Version (SharePoint 2016 or 2019)"},{"location":"SPFx-on-premises/#alternative-using-npm-force-resolutions","text":"Install resolutions package and TypeScript providing considered version explicitly: bash npm i -D npm-force-resolutions typescript@3.6.4 Add a resolution for TypeScript and preinstall script into package.json to a corresponding code blocks: JSON { \"scripts\": { \"preinstall\": \"npx npm-force-resolutions\" }, \"resolutions\": { \"typescript\": \"3.6.4\" } } Run npm install to trigger preinstall script and bumping TypeScript version into package-lock.json Run npm run build , should produce no errors Installing additional dependencies should be safe then.","title":"Alternative using npm-force-resolutions"},{"location":"getting-started/","text":"Getting Started \u00b6 These libraries are geared towards folks working with TypeScript but will work equally well for JavaScript projects. To get started you need to install the libraries you need via npm. Many of the packages have a peer dependency to other packages with the @pnp namespace meaning you may need to install more than one package. All packages are released together eliminating version confusion - all packages will depend on packages with the same version number. If you need to support older browsers please review the article on polyfills for required functionality. Install \u00b6 First you will need to install those libraries you want to use in your application. Here we will install the most frequently used packages. This step applies to any environment or project. npm install @pnp/sp @pnp/graph --save Next we can import and use the functionality within our application. Below is a very simple example, please see the individual package documentation for more details and examples. import { getRandomString } from \"@pnp/core\"; (function() { // get and log a random string console.log(getRandomString(20)); })() Getting Started with SharePoint Framework \u00b6 The @pnp/sp and @pnp/graph libraries are designed to work seamlessly within SharePoint Framework projects with a small amount of upfront configuration. If you are running in 2016 or 2019 on-premises please read this note on a workaround for the included TypeScript version. If you are targeting SharePoint online you do not need to take any additional steps. Establish Context \u00b6 Because SharePoint Framework provides a local context to each component we need to set that context within the library. This allows us to determine request urls as well as use the SPFx HttpGraphClient within @pnp/graph. There are two ways to provide the SPFx context to the library. Either through the setup method imported from @pnp/core or using the setup method on either the @pnp/sp or @pnp/graph main export. All three are shown below and are equivalent, meaning if you are already importing the sp variable from @pnp/sp or the graph variable from @pnp/graph you should use their setup method to reduce imports. The setup is always done in the onInit method to ensure it runs before your other life-cycle code. You can also set any other settings at this time. Using @pnp/core setup \u00b6 import { setup as pnpSetup } from \"@pnp/core\"; // ... protected onInit(): Promise { return super.onInit().then(_ => { // other init code may be present pnpSetup({ spfxContext: this.context }); }); } // ... Using @pnp/sp setup \u00b6 import { sp } from \"@pnp/sp/presets/all\"; // ... protected onInit(): Promise { return super.onInit().then(_ => { // other init code may be present sp.setup({ spfxContext: this.context }); }); } // ... Sp setup also supports passing just the SPFx context object directly as this is the most common case import { sp } from \"@pnp/sp/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present sp.setup(this.context); } // ... Using @pnp/graph setup \u00b6 import { graph } from \"@pnp/graph/presets/all\"; // ... protected onInit(): Promise { return super.onInit().then(_ => { // other init code may be present graph.setup({ spfxContext: this.context }); }); } // ... Establish context within an SPFx service \u00b6 Because you do not have full access to the context object within a service you need to setup things a little differently. If you do not need AAD tokens you can leave that part out and specify just the pageContext. import { ServiceKey, ServiceScope } from \"@microsoft/sp-core-library\"; import { PageContext } from \"@microsoft/sp-page-context\"; import { AadTokenProviderFactory } from \"@microsoft/sp-http\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; export interface ISampleService { getLists(): Promise; } export class SampleService { public static readonly serviceKey: ServiceKey = ServiceKey.create('SPFx:SampleService', SampleService); constructor(serviceScope: ServiceScope) { serviceScope.whenFinished(() => { const pageContext = serviceScope.consume(PageContext.serviceKey); const tokenProviderFactory = serviceScope.consume(AadTokenProviderFactory.serviceKey); // we need to \"spoof\" the context object with the parts we need for PnPjs sp.setup({ spfxContext: { aadTokenProviderFactory: tokenProviderFactory, pageContext: pageContext, } }); // This approach also works if you do not require AAD tokens // you don't need to do both // sp.setup({ // sp : { // baseUrl : pageContext.web.absoluteUrl // } // }); }); } public getLists(): Promise { return sp.web.lists(); } } Connect to SharePoint from Node \u00b6 Please see the main article on how we support node versions that require commonjs modules. npm i @pnp/sp-commonjs @pnp/nodejs-commonjs This will install the logging, common, odata, sp, and nodejs packages. You can read more about what each package does starting on the packages page. Once these are installed you need to import them into your project, to communicate with SharePoint from node we'll need the following imports: import { sp } from \"@pnp/sp-commonjs\"; import { SPFetchClient } from \"@pnp/nodejs-commonjs\"; Once you have imported the necessary resources you can update your code to setup the node fetch client as well as make a call to SharePoint. // configure your node options (only once in your application) sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\"); }, }, }); // make a call to SharePoint and log it in the console sp.web.select(\"Title\", \"Description\")().then(w => { console.log(JSON.stringify(w, null, 4)); }); Connect to Microsoft Graph From Node \u00b6 Similar to the above you can also make calls to the Graph api from node using the libraries. Again we start with installing the required resources. You can see ./debug/launch/graph.ts for a live example. npm i @pnp/graph-commonjs @pnp/nodejs-commonjs Now we need to import what we'll need to call graph import { graph } from \"@pnp/graph-commonjs\"; import { AdalFetchClient } from \"@pnp/nodejs-commonjs\"; Now we can make our graph calls after setting up the Adal client. Note you'll need to setup an AzureAD App registration with the necessary permissions. graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{mytenant}.onmicrosoft.com\", \"{application id}\", \"{application secret}\"); }, }, }); // make a call to Graph and get all the groups graph.groups().then(g => { console.log(JSON.stringify(g, null, 4)); }); Getting Started outside SharePoint Framework \u00b6 In some cases you may be working in a way such that we cannot determine the base url for the web. In this scenario you have two options. Set baseUrl through setup \u00b6 Here we are setting the baseUrl via the sp.setup method. We are also setting the headers to use verbose mode, something you may have to do when working against unpatched versions of SharePoint 2013 as discussed here . This is optional for 2016 or SharePoint Online. The library does not support setting the headers to use nometadata as we rely on the metadata in the response to do some of the more complicated functions. Some of the pure data calls will probably work but it is not a supported configuration. import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { headers: { Accept: \"application/json;odata=verbose\", }, baseUrl: \"{Absolute SharePoint Web URL}\" }, }); const w = await sp.web(); Create Web instances directly \u00b6 Using this method you create the web directly with the url you want to use as the base. import { Web } from \"@pnp/sp/presets/all\"; const web = Web(\"{Absolute SharePoint Web URL}\"); const w = await web(); Next Steps \u00b6 Be sure to review the article describing all of the available settings across the libraries.","title":"Getting Started"},{"location":"getting-started/#getting-started","text":"These libraries are geared towards folks working with TypeScript but will work equally well for JavaScript projects. To get started you need to install the libraries you need via npm. Many of the packages have a peer dependency to other packages with the @pnp namespace meaning you may need to install more than one package. All packages are released together eliminating version confusion - all packages will depend on packages with the same version number. If you need to support older browsers please review the article on polyfills for required functionality.","title":"Getting Started"},{"location":"getting-started/#install","text":"First you will need to install those libraries you want to use in your application. Here we will install the most frequently used packages. This step applies to any environment or project. npm install @pnp/sp @pnp/graph --save Next we can import and use the functionality within our application. Below is a very simple example, please see the individual package documentation for more details and examples. import { getRandomString } from \"@pnp/core\"; (function() { // get and log a random string console.log(getRandomString(20)); })()","title":"Install"},{"location":"getting-started/#getting-started-with-sharepoint-framework","text":"The @pnp/sp and @pnp/graph libraries are designed to work seamlessly within SharePoint Framework projects with a small amount of upfront configuration. If you are running in 2016 or 2019 on-premises please read this note on a workaround for the included TypeScript version. If you are targeting SharePoint online you do not need to take any additional steps.","title":"Getting Started with SharePoint Framework"},{"location":"getting-started/#establish-context","text":"Because SharePoint Framework provides a local context to each component we need to set that context within the library. This allows us to determine request urls as well as use the SPFx HttpGraphClient within @pnp/graph. There are two ways to provide the SPFx context to the library. Either through the setup method imported from @pnp/core or using the setup method on either the @pnp/sp or @pnp/graph main export. All three are shown below and are equivalent, meaning if you are already importing the sp variable from @pnp/sp or the graph variable from @pnp/graph you should use their setup method to reduce imports. The setup is always done in the onInit method to ensure it runs before your other life-cycle code. You can also set any other settings at this time.","title":"Establish Context"},{"location":"getting-started/#using-pnpcore-setup","text":"import { setup as pnpSetup } from \"@pnp/core\"; // ... protected onInit(): Promise { return super.onInit().then(_ => { // other init code may be present pnpSetup({ spfxContext: this.context }); }); } // ...","title":"Using @pnp/core setup"},{"location":"getting-started/#using-pnpsp-setup","text":"import { sp } from \"@pnp/sp/presets/all\"; // ... protected onInit(): Promise { return super.onInit().then(_ => { // other init code may be present sp.setup({ spfxContext: this.context }); }); } // ... Sp setup also supports passing just the SPFx context object directly as this is the most common case import { sp } from \"@pnp/sp/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present sp.setup(this.context); } // ...","title":"Using @pnp/sp setup"},{"location":"getting-started/#using-pnpgraph-setup","text":"import { graph } from \"@pnp/graph/presets/all\"; // ... protected onInit(): Promise { return super.onInit().then(_ => { // other init code may be present graph.setup({ spfxContext: this.context }); }); } // ...","title":"Using @pnp/graph setup"},{"location":"getting-started/#establish-context-within-an-spfx-service","text":"Because you do not have full access to the context object within a service you need to setup things a little differently. If you do not need AAD tokens you can leave that part out and specify just the pageContext. import { ServiceKey, ServiceScope } from \"@microsoft/sp-core-library\"; import { PageContext } from \"@microsoft/sp-page-context\"; import { AadTokenProviderFactory } from \"@microsoft/sp-http\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; export interface ISampleService { getLists(): Promise; } export class SampleService { public static readonly serviceKey: ServiceKey = ServiceKey.create('SPFx:SampleService', SampleService); constructor(serviceScope: ServiceScope) { serviceScope.whenFinished(() => { const pageContext = serviceScope.consume(PageContext.serviceKey); const tokenProviderFactory = serviceScope.consume(AadTokenProviderFactory.serviceKey); // we need to \"spoof\" the context object with the parts we need for PnPjs sp.setup({ spfxContext: { aadTokenProviderFactory: tokenProviderFactory, pageContext: pageContext, } }); // This approach also works if you do not require AAD tokens // you don't need to do both // sp.setup({ // sp : { // baseUrl : pageContext.web.absoluteUrl // } // }); }); } public getLists(): Promise { return sp.web.lists(); } }","title":"Establish context within an SPFx service"},{"location":"getting-started/#connect-to-sharepoint-from-node","text":"Please see the main article on how we support node versions that require commonjs modules. npm i @pnp/sp-commonjs @pnp/nodejs-commonjs This will install the logging, common, odata, sp, and nodejs packages. You can read more about what each package does starting on the packages page. Once these are installed you need to import them into your project, to communicate with SharePoint from node we'll need the following imports: import { sp } from \"@pnp/sp-commonjs\"; import { SPFetchClient } from \"@pnp/nodejs-commonjs\"; Once you have imported the necessary resources you can update your code to setup the node fetch client as well as make a call to SharePoint. // configure your node options (only once in your application) sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\"); }, }, }); // make a call to SharePoint and log it in the console sp.web.select(\"Title\", \"Description\")().then(w => { console.log(JSON.stringify(w, null, 4)); });","title":"Connect to SharePoint from Node"},{"location":"getting-started/#connect-to-microsoft-graph-from-node","text":"Similar to the above you can also make calls to the Graph api from node using the libraries. Again we start with installing the required resources. You can see ./debug/launch/graph.ts for a live example. npm i @pnp/graph-commonjs @pnp/nodejs-commonjs Now we need to import what we'll need to call graph import { graph } from \"@pnp/graph-commonjs\"; import { AdalFetchClient } from \"@pnp/nodejs-commonjs\"; Now we can make our graph calls after setting up the Adal client. Note you'll need to setup an AzureAD App registration with the necessary permissions. graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{mytenant}.onmicrosoft.com\", \"{application id}\", \"{application secret}\"); }, }, }); // make a call to Graph and get all the groups graph.groups().then(g => { console.log(JSON.stringify(g, null, 4)); });","title":"Connect to Microsoft Graph From Node"},{"location":"getting-started/#getting-started-outside-sharepoint-framework","text":"In some cases you may be working in a way such that we cannot determine the base url for the web. In this scenario you have two options.","title":"Getting Started outside SharePoint Framework"},{"location":"getting-started/#set-baseurl-through-setup","text":"Here we are setting the baseUrl via the sp.setup method. We are also setting the headers to use verbose mode, something you may have to do when working against unpatched versions of SharePoint 2013 as discussed here . This is optional for 2016 or SharePoint Online. The library does not support setting the headers to use nometadata as we rely on the metadata in the response to do some of the more complicated functions. Some of the pure data calls will probably work but it is not a supported configuration. import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { headers: { Accept: \"application/json;odata=verbose\", }, baseUrl: \"{Absolute SharePoint Web URL}\" }, }); const w = await sp.web();","title":"Set baseUrl through setup"},{"location":"getting-started/#create-web-instances-directly","text":"Using this method you create the web directly with the url you want to use as the base. import { Web } from \"@pnp/sp/presets/all\"; const web = Web(\"{Absolute SharePoint Web URL}\"); const w = await web();","title":"Create Web instances directly"},{"location":"getting-started/#next-steps","text":"Be sure to review the article describing all of the available settings across the libraries.","title":"Next Steps"},{"location":"nodejs-support/","text":"Working in Nodejs \u00b6 As outlined on the getting started page you can easily use the library with Nodejs, but there are some key differences you need to consider. But first a little history, you can skip this part if you just want to see how things work but we felt some folks might be interested. To make selective imports work we need to support es module syntax for client-side environments such as SPFx development. All versions of Nodejs that are currently LTS do not support es modules without flags (as of when this was written). We thought we had a scheme to handle this following the available guidance but ultimately it didn't work across all node versions and we unpublished 2.0.1. CommonJS Libraries \u00b6 Because of the difficulties of working with es modules in node we recommend using our mirror packages providing commonjs modules. These can be installed by using the package name and appending -commonjs, such as: npm install @pnp/sp-commonjs @pnp/nodejs-commonjs These packages are built from the same source and released at the same time so all updates are included with each release. The only difference is that for the sp-commonjs and graph-commonjs packages we target the \"all\" preset as the entry point. This makes things a little easier in node where bundle sizes aren't an issue. You can see this in the nodejs-app sample . Here is that sample explained fully: Install Libraries \u00b6 We want to make a simple request to SharePoint so we need to first install the modules we need: npm install @pnp/sp-commonjs @pnp/nodejs-commonjs --save We will also install TypeScript: npm install typescript --save-dev index.ts \u00b6 We will create an index.ts file and add the following code. You will need to update the site url, client id, and client secret to your values. This should be done using a settings file or something like Azure KeyVault for production, but for this example it is good enough. // our imports come from the -commonjs libs import { SPFetchClient } from \"@pnp/nodejs-commonjs\"; import { sp } from \"@pnp/sp-commonjs\"; // we call setup to use the node client sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{ site url }\", \"{ client id }\", \"{ client secret }\"); }, }, }); async function makeRequest() { // make a request to get the web's details const w = await sp.web(); console.log(JSON.stringify(w, null, 2)); } // get past no await at root of app makeRequest(); Don't forget you will need to register an app to get the client id and secret. Add a tsconfig.json \u00b6 Not strictly necessary but very useful to include a tsconfig.json to control how tsc transpiles your code to JavaScript { \"compilerOptions\": { \"module\": \"commonjs\", \"target\": \"esnext\", \"moduleResolution\": \"node\", \"declaration\": true, \"outDir\": \"dist\", \"skipLibCheck\": true, \"sourceMap\": true, \"lib\": [ \"dom\", \"esnext\" ] }, \"files\": [ \"./index.ts\" ] } Add an script to package.json \u00b6 We add the \"start\" script to the default package.json { \"name\": \"nodejs-app\", \"version\": \"1.0.0\", \"description\": \"Sample nodejs app using PnPjs\", \"main\": \"index.js\", \"scripts\": { \"start\": \"tsc -p . && node dist/index.js\", \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\" }, \"author\": \"\", \"license\": \"MIT\", \"dependencies\": { \"@pnp/nodejs-commonjs\": \"^2.0.2-5\", \"@pnp/sp-commonjs\": \"^2.0.2-5\" }, \"devDependencies\": { \"typescript\": \"^3.7.5\" } } Run It \u00b6 You can now run your program using: npm start","title":"Working in Nodejs"},{"location":"nodejs-support/#working-in-nodejs","text":"As outlined on the getting started page you can easily use the library with Nodejs, but there are some key differences you need to consider. But first a little history, you can skip this part if you just want to see how things work but we felt some folks might be interested. To make selective imports work we need to support es module syntax for client-side environments such as SPFx development. All versions of Nodejs that are currently LTS do not support es modules without flags (as of when this was written). We thought we had a scheme to handle this following the available guidance but ultimately it didn't work across all node versions and we unpublished 2.0.1.","title":"Working in Nodejs"},{"location":"nodejs-support/#commonjs-libraries","text":"Because of the difficulties of working with es modules in node we recommend using our mirror packages providing commonjs modules. These can be installed by using the package name and appending -commonjs, such as: npm install @pnp/sp-commonjs @pnp/nodejs-commonjs These packages are built from the same source and released at the same time so all updates are included with each release. The only difference is that for the sp-commonjs and graph-commonjs packages we target the \"all\" preset as the entry point. This makes things a little easier in node where bundle sizes aren't an issue. You can see this in the nodejs-app sample . Here is that sample explained fully:","title":"CommonJS Libraries"},{"location":"nodejs-support/#install-libraries","text":"We want to make a simple request to SharePoint so we need to first install the modules we need: npm install @pnp/sp-commonjs @pnp/nodejs-commonjs --save We will also install TypeScript: npm install typescript --save-dev","title":"Install Libraries"},{"location":"nodejs-support/#indexts","text":"We will create an index.ts file and add the following code. You will need to update the site url, client id, and client secret to your values. This should be done using a settings file or something like Azure KeyVault for production, but for this example it is good enough. // our imports come from the -commonjs libs import { SPFetchClient } from \"@pnp/nodejs-commonjs\"; import { sp } from \"@pnp/sp-commonjs\"; // we call setup to use the node client sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{ site url }\", \"{ client id }\", \"{ client secret }\"); }, }, }); async function makeRequest() { // make a request to get the web's details const w = await sp.web(); console.log(JSON.stringify(w, null, 2)); } // get past no await at root of app makeRequest(); Don't forget you will need to register an app to get the client id and secret.","title":"index.ts"},{"location":"nodejs-support/#add-a-tsconfigjson","text":"Not strictly necessary but very useful to include a tsconfig.json to control how tsc transpiles your code to JavaScript { \"compilerOptions\": { \"module\": \"commonjs\", \"target\": \"esnext\", \"moduleResolution\": \"node\", \"declaration\": true, \"outDir\": \"dist\", \"skipLibCheck\": true, \"sourceMap\": true, \"lib\": [ \"dom\", \"esnext\" ] }, \"files\": [ \"./index.ts\" ] }","title":"Add a tsconfig.json"},{"location":"nodejs-support/#add-an-script-to-packagejson","text":"We add the \"start\" script to the default package.json { \"name\": \"nodejs-app\", \"version\": \"1.0.0\", \"description\": \"Sample nodejs app using PnPjs\", \"main\": \"index.js\", \"scripts\": { \"start\": \"tsc -p . && node dist/index.js\", \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\" }, \"author\": \"\", \"license\": \"MIT\", \"dependencies\": { \"@pnp/nodejs-commonjs\": \"^2.0.2-5\", \"@pnp/sp-commonjs\": \"^2.0.2-5\" }, \"devDependencies\": { \"typescript\": \"^3.7.5\" } }","title":"Add an script to package.json"},{"location":"nodejs-support/#run-it","text":"You can now run your program using: npm start","title":"Run It"},{"location":"npm-scripts/","text":"Supported NPM Scripts \u00b6 As you likely are aware you can embed scripts within package.json. Using this capability coupled with the knowledge that pretty much all of the tools we use now support code files (.js/.ts) as configuration we have removed gulp from our tooling and now execute our various actions via scripts. This is not a knock on gulp, it remains a great tool, rather an opportunity for us to remove some dependencies. This article outlines the current scripts we've implemented and how to use them, with available options and examples. Start \u00b6 Executes the serve command npm start Serve \u00b6 Starts a debugging server serving a bundled script with ./debug/serve/main.ts as the entry point. This allows you to run tests and debug code running within the context of a webpage rather than node. npm run serve Test \u00b6 Runs the tests and coverage for the library. Starting with 2.3.0 ONLY MSAL auth is supported for running the tests. More details on setting up MSAL for node. Options \u00b6 There are several options you can provide to the test command. All of these need to be separated using a \"--\" double hyphen so they are passed to the spawned sub-commands. Test a Single Package \u00b6 --package or -p This option will only run the tests associated with the package you specify. The values are the folder names within the ./packages directory. # run only sp tests npm test -- -p sp # run only logging tests npm test -- -package logging Run a Single Test File \u00b6 --single or --s You can also run a specific file with a package. This option must be used with the single package option as you are essentially specifying the folder and file. This option uses either the flags. # run only sp web tests npm test -- -p sp -s web # run only graph groups tests npm test -- -package graph -single groups Specify a Site \u00b6 --site By default every time you run the tests a new sub-site is created below the site specified in your settings file . You can choose to reuse a site for testing, which saves time when re-running a set of tests frequently. Testing content is not deleted after tests, so if you need to inspect the created content from testing you may wish to forgo this option. This option can be used with any or none of the other testing options. # run only sp web tests with a certain site npm test -- -p sp -s web --site https://some.site.com/sites/dev Cleanup \u00b6 --cleanup If you include this flag the testing web will be deleted once tests are complete. Useful for local testing where you do not need to inspect the web once the tests are complete. Works with any of the other options, be careful when specifying a web using --site as it will be deleted. # clean up our testing site npm test -- --cleanup Logging \u00b6 --logging If you include this flag a console logger will be subscribed and the log level will be set to Info. This will provide console output for all the requests being made during testing. This flag is compatible with all other flags - however unless you are trying to debug a specific test this will produce a lot of chatty output. # enable logging during testing npm test -- --logging spVerbose \u00b6 Added in 2.0.13 --spverbose This flag will enable \"verbose\" OData mode for SharePoint tests. This flag is compatible with other flags. npm test -- --spverbose build \u00b6 Invokes the pnpbuild cli to transpile the TypeScript into JavaScript. All behavior is controlled via the tsconfig.json in the root of the project and sub folders as needed. npm run build package \u00b6 Invokes the pnpbuild cli to create the package directories under the dist folder. This will allow you to see exactly what will end up in the npm packages once they are published. npm run package lint \u00b6 Runs the linter. npm run lint clean \u00b6 Removes any generated folders from the working directory. npm run clean","title":"Npm Scripts"},{"location":"npm-scripts/#supported-npm-scripts","text":"As you likely are aware you can embed scripts within package.json. Using this capability coupled with the knowledge that pretty much all of the tools we use now support code files (.js/.ts) as configuration we have removed gulp from our tooling and now execute our various actions via scripts. This is not a knock on gulp, it remains a great tool, rather an opportunity for us to remove some dependencies. This article outlines the current scripts we've implemented and how to use them, with available options and examples.","title":"Supported NPM Scripts"},{"location":"npm-scripts/#start","text":"Executes the serve command npm start","title":"Start"},{"location":"npm-scripts/#serve","text":"Starts a debugging server serving a bundled script with ./debug/serve/main.ts as the entry point. This allows you to run tests and debug code running within the context of a webpage rather than node. npm run serve","title":"Serve"},{"location":"npm-scripts/#test","text":"Runs the tests and coverage for the library. Starting with 2.3.0 ONLY MSAL auth is supported for running the tests. More details on setting up MSAL for node.","title":"Test"},{"location":"npm-scripts/#options","text":"There are several options you can provide to the test command. All of these need to be separated using a \"--\" double hyphen so they are passed to the spawned sub-commands.","title":"Options"},{"location":"npm-scripts/#test-a-single-package","text":"--package or -p This option will only run the tests associated with the package you specify. The values are the folder names within the ./packages directory. # run only sp tests npm test -- -p sp # run only logging tests npm test -- -package logging","title":"Test a Single Package"},{"location":"npm-scripts/#run-a-single-test-file","text":"--single or --s You can also run a specific file with a package. This option must be used with the single package option as you are essentially specifying the folder and file. This option uses either the flags. # run only sp web tests npm test -- -p sp -s web # run only graph groups tests npm test -- -package graph -single groups","title":"Run a Single Test File"},{"location":"npm-scripts/#specify-a-site","text":"--site By default every time you run the tests a new sub-site is created below the site specified in your settings file . You can choose to reuse a site for testing, which saves time when re-running a set of tests frequently. Testing content is not deleted after tests, so if you need to inspect the created content from testing you may wish to forgo this option. This option can be used with any or none of the other testing options. # run only sp web tests with a certain site npm test -- -p sp -s web --site https://some.site.com/sites/dev","title":"Specify a Site"},{"location":"npm-scripts/#cleanup","text":"--cleanup If you include this flag the testing web will be deleted once tests are complete. Useful for local testing where you do not need to inspect the web once the tests are complete. Works with any of the other options, be careful when specifying a web using --site as it will be deleted. # clean up our testing site npm test -- --cleanup","title":"Cleanup"},{"location":"npm-scripts/#logging","text":"--logging If you include this flag a console logger will be subscribed and the log level will be set to Info. This will provide console output for all the requests being made during testing. This flag is compatible with all other flags - however unless you are trying to debug a specific test this will produce a lot of chatty output. # enable logging during testing npm test -- --logging","title":"Logging"},{"location":"npm-scripts/#spverbose","text":"Added in 2.0.13 --spverbose This flag will enable \"verbose\" OData mode for SharePoint tests. This flag is compatible with other flags. npm test -- --spverbose","title":"spVerbose"},{"location":"npm-scripts/#build","text":"Invokes the pnpbuild cli to transpile the TypeScript into JavaScript. All behavior is controlled via the tsconfig.json in the root of the project and sub folders as needed. npm run build","title":"build"},{"location":"npm-scripts/#package","text":"Invokes the pnpbuild cli to create the package directories under the dist folder. This will allow you to see exactly what will end up in the npm packages once they are published. npm run package","title":"package"},{"location":"npm-scripts/#lint","text":"Runs the linter. npm run lint","title":"lint"},{"location":"npm-scripts/#clean","text":"Removes any generated folders from the working directory. npm run clean","title":"clean"},{"location":"packages/","text":"Packages \u00b6 The following packages comprise the Patterns and Practices client side libraries. All of the packages are published as a set and depend on their peers within the @pnp scope. The latest published version is . @pnp/ adaljsclient Provides an adaljs wrapper suitable for use with PnPjs common Provides shared functionality across all pnp libraries config-store Provides a way to manage configuration within your application graph Provides a fluent api for working with Microsoft Graph logging Light-weight, subscribable logging framework msaljsclient Provides an msal wrapper suitable for use with PnPjs nodejs Provides functionality enabling the @pnp libraries within nodejs odata Provides shared odata functionality and base classes sp Provides a fluent api for working with SharePoint REST sp-addinhelpers Provides functionality for working within SharePoint add-ins","title":"Packages"},{"location":"packages/#packages","text":"The following packages comprise the Patterns and Practices client side libraries. All of the packages are published as a set and depend on their peers within the @pnp scope. The latest published version is . @pnp/ adaljsclient Provides an adaljs wrapper suitable for use with PnPjs common Provides shared functionality across all pnp libraries config-store Provides a way to manage configuration within your application graph Provides a fluent api for working with Microsoft Graph logging Light-weight, subscribable logging framework msaljsclient Provides an msal wrapper suitable for use with PnPjs nodejs Provides functionality enabling the @pnp libraries within nodejs odata Provides shared odata functionality and base classes sp Provides a fluent api for working with SharePoint REST sp-addinhelpers Provides functionality for working within SharePoint add-ins","title":"Packages"},{"location":"transition-guide/","text":"Transition Guide \u00b6 We have worked to make moving from @pnp library 1. to 2. as painless as possible, however there are some changes to how things work. The below guide we have provided an overview of what it takes to transition between the libraries. If we missed something, please let us know in the issues list so we can update the guide. Thanks! Installing @pnp libraries \u00b6 In version 1.* the libraries were setup as peer dependencies of each other requiring you to install each of them separately. We continue to believe this correctly describes the relationship, but recognize that basically nothing in the world accounts for peer dependencies. So we have updated the libraries to be dependencies. This makes it easier to install into your projects as you only need to install a single library: npm i --save @pnp/sp Selective Imports \u00b6 Another big change in v2 is the ability to selectively import the pieces you need from the libraries. This allows you to have smaller bundles and works well with tree-shaking. It does require you to have more import statements, which can potentially be a bit confusing at first. The selective imports apply to the sp and graph libraries. To help explain let's take the example of the Web object. In v1 Web includes a reference to pretty much everything else in the entire sp library. Meaning that if you use web (and you pretty much have to) you hold a ref to all the other pieces (like Fields, Lists, ContentTypes) even if you aren't using them. Because of that tree shaking can't do anything to reduce the bundle size because it \"thinks\" you are using them simply because they have been imported. To solve this in v2 the Web object no longer contains references to anything, it is a bare object with a few methods. If you look at the source you will see that, for example, there is no longer a \"lists\" property. These properties and methods are now added through selectively importing the functionality you need: Selectively Import Web lists functionality \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // this imports the functionality for lists associated only with web import \"@pnp/sp/lists/web\"; const r = await sp.web.lists(); import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // this imports all the functionality for lists import \"@pnp/sp/lists\"; const r = await sp.web.lists(); Each of the docs pages shows the selective import paths for each sub-module (lists, items, etc.). Presets \u00b6 In addition to the ability to selectively import functionality you can import presets. This allows you to import an entire set of functionality in a single line. At launch the sp library will support two presets \"all\" and \"core\" with the graph library supporting \"all\". Using the \"all\" preset will match the functionality of v1. This can save you time in transitioning your projects so you can update to selective imports later. For new projects we recommend using the selective imports from day 1. To update your V1 projects to V2 you can replace all instances of \"@pnp/sp\" with \"@pnp/sp/presets/all\" and things should work as before (though some class names or other things may have changed, please review the change log and the rest of this guide). // V1 way of doing things: import { sp, ClientSideWebpart, ClientSideWebpartPropertyTypes, } from \"@pnp/sp\"; // V2 way with selective imports import { sp } from \"@pnp/sp\"; import { ClientSideWebpart, ClientSideWebpartPropertyTypes } from \"@pnp/sp/clientside-pages\"; // V2 way with preset \"all\" import { sp, ClientSideWebpart, ClientSideWebpartPropertyTypes } from \"@pnp/sp/presets/all\"; Invokable Objects \u00b6 Another new feature is the addition of invokable objects. Previously where you used \"get()\" to invoke a request you can now leave it off. We have left the .get method in place so everyone's code wasn't broken immediately upon transitioning. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // old way (still works) const r1 = sp.web(); // invokable const r2 = sp.web(); The benefit is that objects can now support default actions that are not \"get\" but might be \"post\". And you save typing a few extra characters. This still work the same as with select or any of the other odata methods: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // invokable const r = sp.web.select(\"Title\", \"Url\")(); Factory Functions & Interfaces \u00b6 Another change in the library is in the structure of exports. We are no longer exporting the objects themselves, rather we are only exposing factory functions and interfaces. This allows us to decouple what developers use from our internal implementation. For folks using the fluent chain starting with sp you shouldn't need to update your code. If you are using any of the v1 classes directly you should just need to remove the \"new\" keyword and update the import path. The factory functions signature matches the constructor signature of the v1 objects. // v1 import { Web } from \"@pnp/sp\"; const web: Web = new Web(\"some absolute url\"); const r1 = web(); // v2 import { Web, IWeb } from \"@pnp/sp/webs\"; const web: IWeb = Web(\"some absolute url\"); const r2 = web(); Extension Methods \u00b6 Another new capability in v2 is the ability to extend objects and factories. This allows you to easily add methods or properties on a per-object basis. Please see the full article on extension methods describing this great new capability. CDN publishing \u00b6 Starting with v2 we will no longer create bundles for each of the packages. Historically these are not commonly used, don't work perfectly for everyone (there are a lot of ways to bundle things), and another piece we need to maintain. Instead we encourage folks to create their own bundles , optimized for their particular scenario. This will result in smaller overall bundle size and allow folks to bundle things to match their scenario. Please review the article on creating your custom bundles to see how to tailor bundles to your needs. The PnPjs bundle will remain, though it is designed only for backwards compatibility and we strongly recommend creating your own bundles, or directly importing the libraries into your projects using selective imports. Drop client-svc and sp-taxonomy libraries \u00b6 These libraries were created to allow folks to access and manage SharePoint taxonomy and manage metadata. Given that there is upcoming support for taxonomy via a supported REST API we will drop these two libraries. If working with taxonomy remains a core requirement of your application and we do not yet have support for the new apis, please remain on v1 for the time being. As of 2.0.6 we support reading the modern taxonomy API. Docs here","title":"Transition Guide"},{"location":"transition-guide/#transition-guide","text":"We have worked to make moving from @pnp library 1. to 2. as painless as possible, however there are some changes to how things work. The below guide we have provided an overview of what it takes to transition between the libraries. If we missed something, please let us know in the issues list so we can update the guide. Thanks!","title":"Transition Guide"},{"location":"transition-guide/#installing-pnp-libraries","text":"In version 1.* the libraries were setup as peer dependencies of each other requiring you to install each of them separately. We continue to believe this correctly describes the relationship, but recognize that basically nothing in the world accounts for peer dependencies. So we have updated the libraries to be dependencies. This makes it easier to install into your projects as you only need to install a single library: npm i --save @pnp/sp","title":"Installing @pnp libraries"},{"location":"transition-guide/#selective-imports","text":"Another big change in v2 is the ability to selectively import the pieces you need from the libraries. This allows you to have smaller bundles and works well with tree-shaking. It does require you to have more import statements, which can potentially be a bit confusing at first. The selective imports apply to the sp and graph libraries. To help explain let's take the example of the Web object. In v1 Web includes a reference to pretty much everything else in the entire sp library. Meaning that if you use web (and you pretty much have to) you hold a ref to all the other pieces (like Fields, Lists, ContentTypes) even if you aren't using them. Because of that tree shaking can't do anything to reduce the bundle size because it \"thinks\" you are using them simply because they have been imported. To solve this in v2 the Web object no longer contains references to anything, it is a bare object with a few methods. If you look at the source you will see that, for example, there is no longer a \"lists\" property. These properties and methods are now added through selectively importing the functionality you need:","title":"Selective Imports"},{"location":"transition-guide/#selectively-import-web-lists-functionality","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // this imports the functionality for lists associated only with web import \"@pnp/sp/lists/web\"; const r = await sp.web.lists(); import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // this imports all the functionality for lists import \"@pnp/sp/lists\"; const r = await sp.web.lists(); Each of the docs pages shows the selective import paths for each sub-module (lists, items, etc.).","title":"Selectively Import Web lists functionality"},{"location":"transition-guide/#presets","text":"In addition to the ability to selectively import functionality you can import presets. This allows you to import an entire set of functionality in a single line. At launch the sp library will support two presets \"all\" and \"core\" with the graph library supporting \"all\". Using the \"all\" preset will match the functionality of v1. This can save you time in transitioning your projects so you can update to selective imports later. For new projects we recommend using the selective imports from day 1. To update your V1 projects to V2 you can replace all instances of \"@pnp/sp\" with \"@pnp/sp/presets/all\" and things should work as before (though some class names or other things may have changed, please review the change log and the rest of this guide). // V1 way of doing things: import { sp, ClientSideWebpart, ClientSideWebpartPropertyTypes, } from \"@pnp/sp\"; // V2 way with selective imports import { sp } from \"@pnp/sp\"; import { ClientSideWebpart, ClientSideWebpartPropertyTypes } from \"@pnp/sp/clientside-pages\"; // V2 way with preset \"all\" import { sp, ClientSideWebpart, ClientSideWebpartPropertyTypes } from \"@pnp/sp/presets/all\";","title":"Presets"},{"location":"transition-guide/#invokable-objects","text":"Another new feature is the addition of invokable objects. Previously where you used \"get()\" to invoke a request you can now leave it off. We have left the .get method in place so everyone's code wasn't broken immediately upon transitioning. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // old way (still works) const r1 = sp.web(); // invokable const r2 = sp.web(); The benefit is that objects can now support default actions that are not \"get\" but might be \"post\". And you save typing a few extra characters. This still work the same as with select or any of the other odata methods: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // invokable const r = sp.web.select(\"Title\", \"Url\")();","title":"Invokable Objects"},{"location":"transition-guide/#factory-functions-interfaces","text":"Another change in the library is in the structure of exports. We are no longer exporting the objects themselves, rather we are only exposing factory functions and interfaces. This allows us to decouple what developers use from our internal implementation. For folks using the fluent chain starting with sp you shouldn't need to update your code. If you are using any of the v1 classes directly you should just need to remove the \"new\" keyword and update the import path. The factory functions signature matches the constructor signature of the v1 objects. // v1 import { Web } from \"@pnp/sp\"; const web: Web = new Web(\"some absolute url\"); const r1 = web(); // v2 import { Web, IWeb } from \"@pnp/sp/webs\"; const web: IWeb = Web(\"some absolute url\"); const r2 = web();","title":"Factory Functions & Interfaces"},{"location":"transition-guide/#extension-methods","text":"Another new capability in v2 is the ability to extend objects and factories. This allows you to easily add methods or properties on a per-object basis. Please see the full article on extension methods describing this great new capability.","title":"Extension Methods"},{"location":"transition-guide/#cdn-publishing","text":"Starting with v2 we will no longer create bundles for each of the packages. Historically these are not commonly used, don't work perfectly for everyone (there are a lot of ways to bundle things), and another piece we need to maintain. Instead we encourage folks to create their own bundles , optimized for their particular scenario. This will result in smaller overall bundle size and allow folks to bundle things to match their scenario. Please review the article on creating your custom bundles to see how to tailor bundles to your needs. The PnPjs bundle will remain, though it is designed only for backwards compatibility and we strongly recommend creating your own bundles, or directly importing the libraries into your projects using selective imports.","title":"CDN publishing"},{"location":"transition-guide/#drop-client-svc-and-sp-taxonomy-libraries","text":"These libraries were created to allow folks to access and manage SharePoint taxonomy and manage metadata. Given that there is upcoming support for taxonomy via a supported REST API we will drop these two libraries. If working with taxonomy remains a core requirement of your application and we do not yet have support for the new apis, please remain on v1 for the time being. As of 2.0.6 we support reading the modern taxonomy API. Docs here","title":"Drop client-svc and sp-taxonomy libraries"},{"location":"authentication/","text":"Authentication \u00b6 One of the more challenging aspects of web development is ensuring you are properly authenticated to access the resources you need. This section is designed to guide you through connecting to the resources you need using the appropriate methods. There are two places the PnPjs libraries can be used to connect to various services client (browser) or server . Utility Scenarios \u00b6 BearerTokenFetchClient LambdaFetchClient Client Scenarios \u00b6 SharePoint Framework Connect As: Current User User + AAD App via MSAL User + AAD App via ADAL Connect To: SharePoint as: Current User User + AAD App via MSAL Graph as: Current User User + AAD App via MSAL Both as: Current User User + AAD App via MSAL Single Page Application User + AAD App via MSAL Server Scenarios \u00b6 NodeJS SharePoint App Registration (App-Only) ADAL (App-Only) MSAL (App-Only) - coming soon","title":"Getting Started"},{"location":"authentication/#authentication","text":"One of the more challenging aspects of web development is ensuring you are properly authenticated to access the resources you need. This section is designed to guide you through connecting to the resources you need using the appropriate methods. There are two places the PnPjs libraries can be used to connect to various services client (browser) or server .","title":"Authentication"},{"location":"authentication/#utility-scenarios","text":"BearerTokenFetchClient LambdaFetchClient","title":"Utility Scenarios"},{"location":"authentication/#client-scenarios","text":"SharePoint Framework Connect As: Current User User + AAD App via MSAL User + AAD App via ADAL Connect To: SharePoint as: Current User User + AAD App via MSAL Graph as: Current User User + AAD App via MSAL Both as: Current User User + AAD App via MSAL Single Page Application User + AAD App via MSAL","title":"Client Scenarios"},{"location":"authentication/#server-scenarios","text":"NodeJS SharePoint App Registration (App-Only) ADAL (App-Only) MSAL (App-Only) - coming soon","title":"Server Scenarios"},{"location":"authentication/adaljsclient/","text":"@pnp/core/adalclient \u00b6 This module contains the AdalClient class which can be used to authenticate to any AzureAD secured resource. It is designed to work seamlessly with SharePoint Framework's permissions. Where possible it is recommended to use the MSAL client . Getting Started \u00b6 Install the library and required dependencies npm install @pnp/adaljsclient --save Setup and Use inside SharePoint Framework \u00b6 Using the SharePoint Framework is the preferred way to make use of the AdalClient as we can use the AADTokenProvider to efficiently get tokens on your behalf. You can also read more about how this process works and the necessary SPFx configurations in the SharePoint Framework 1.6 release notes . This method will only work for SharePoint Framework >= 1.6. For earlier versions of SharePoint Framework you can still use the AdalClient as outlined below using the constructor to specify the values for an AAD Application you have setup. Calling the graph api \u00b6 By providing the context in the onInit we can create the adal client from known information. import { graph } from \"@pnp/graph\"; import { getRandomString } from \"@pnp/core\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present graph.setup(this.context); }); } public render(): void { // here we are creating a team with a random name, required Group ReadWrite All permissions const teamName = `ATeam.${getRandomString(4)}`; this.domElement.innerHTML = `Hello, I am creating a team named \"${teamName}\" for you...`; graph.teams.create(teamName, \"This is a description\").then(t => { this.domElement.innerHTML += \"done!\"; }).catch(e => { this.domElement.innerHTML = `Oops, I ran into a problem...${JSON.stringify(e, null, 4)}`; }); } Calling the SharePoint API \u00b6 This example shows how to use the ADALClient with the @pnp/sp library to call an API secured with AAD from within SharePoint Framework. import { SPFxAdalClient } from \"@pnp/core\"; import { sp } from \"@pnp/sp/presets/all\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present sp.setup({ spfxContext: this.context, sp: { fetchClientFactory: () => new SPFxAdalClient(this.context), }, }); }); } public render(): void { sp.web().then(t => { this.domElement.innerHTML = JSON.stringify(t); }).catch(e => { this.domElement.innerHTML = JSON.stringify(e); }); } Calling the any API \u00b6 You can also use the AdalClient to execute AAD authenticated requests to any API which is properly configured to accept the incoming tokens. This approach will only work within SharePoint Framework >= 1.6. Here we call the SharePoint REST API without the sp library as an example. import { FetchOptions } from \"@pnp/core\"; import { AdalClient } from \"@pnp/adaljsclient\"; import { ODataDefaultParser } from \"@pnp/queryable\"; // ... public render(): void { // create an ADAL Client const client = AdalClient.fromSPFxContext(this.context); // setup the request options const opts: FetchOptions = { method: \"GET\", headers: { \"Accept\": \"application/json\", }, }; // execute the request client.fetch(\"https://{tenant}.sharepoint.com/_api/web\", opts).then(response => { // create a parser to convert the response into JSON. // You can create your own, at this point you have a fetch Response to work with const parser = new ODataDefaultParser(); parser.parse(response).then(json => { this.domElement.innerHTML = JSON.stringify(json); }); }).catch(e => { this.domElement.innerHTML = JSON.stringify(e); }); } Manually Configure \u00b6 This example shows setting up and using the AdalClient to make queries using information you have setup. You can review this article for more information on setting up and securing any application using AzureAD. Setup and Use with Microsoft Graph \u00b6 This sample uses a custom AzureAd app you have created and granted the appropriate permissions. import { AdalClient } from \"@pnp/adaljsclient\"; import { graph } from \"@pnp/graph\"; // configure the graph client // parameters are: // client id - the id of the application you created in azure ad // tenant - can be id or URL (shown) // redirect url - absolute url of a page to which your application and Azure AD app allows replies graph.setup({ graph: { fetchClientFactory: () => { return new AdalClient( \"00000000-0000-0000-0000-000000000000\", \"{tenant}.onmicrosoft.com\", \"https://myapp/singlesignon.aspx\"); }, }, }); try { // call the graph API const groups = await graph.groups(); console.log(JSON.stringify(groups, null, 4)); } catch (e) { console.error(e); } Nodejs Applications \u00b6 We have a dedicated node client in @pnp/nodejs.","title":"ADAL Client"},{"location":"authentication/adaljsclient/#pnpcoreadalclient","text":"This module contains the AdalClient class which can be used to authenticate to any AzureAD secured resource. It is designed to work seamlessly with SharePoint Framework's permissions. Where possible it is recommended to use the MSAL client .","title":"@pnp/core/adalclient"},{"location":"authentication/adaljsclient/#getting-started","text":"Install the library and required dependencies npm install @pnp/adaljsclient --save","title":"Getting Started"},{"location":"authentication/adaljsclient/#setup-and-use-inside-sharepoint-framework","text":"Using the SharePoint Framework is the preferred way to make use of the AdalClient as we can use the AADTokenProvider to efficiently get tokens on your behalf. You can also read more about how this process works and the necessary SPFx configurations in the SharePoint Framework 1.6 release notes . This method will only work for SharePoint Framework >= 1.6. For earlier versions of SharePoint Framework you can still use the AdalClient as outlined below using the constructor to specify the values for an AAD Application you have setup.","title":"Setup and Use inside SharePoint Framework"},{"location":"authentication/adaljsclient/#calling-the-graph-api","text":"By providing the context in the onInit we can create the adal client from known information. import { graph } from \"@pnp/graph\"; import { getRandomString } from \"@pnp/core\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present graph.setup(this.context); }); } public render(): void { // here we are creating a team with a random name, required Group ReadWrite All permissions const teamName = `ATeam.${getRandomString(4)}`; this.domElement.innerHTML = `Hello, I am creating a team named \"${teamName}\" for you...`; graph.teams.create(teamName, \"This is a description\").then(t => { this.domElement.innerHTML += \"done!\"; }).catch(e => { this.domElement.innerHTML = `Oops, I ran into a problem...${JSON.stringify(e, null, 4)}`; }); }","title":"Calling the graph api"},{"location":"authentication/adaljsclient/#calling-the-sharepoint-api","text":"This example shows how to use the ADALClient with the @pnp/sp library to call an API secured with AAD from within SharePoint Framework. import { SPFxAdalClient } from \"@pnp/core\"; import { sp } from \"@pnp/sp/presets/all\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present sp.setup({ spfxContext: this.context, sp: { fetchClientFactory: () => new SPFxAdalClient(this.context), }, }); }); } public render(): void { sp.web().then(t => { this.domElement.innerHTML = JSON.stringify(t); }).catch(e => { this.domElement.innerHTML = JSON.stringify(e); }); }","title":"Calling the SharePoint API"},{"location":"authentication/adaljsclient/#calling-the-any-api","text":"You can also use the AdalClient to execute AAD authenticated requests to any API which is properly configured to accept the incoming tokens. This approach will only work within SharePoint Framework >= 1.6. Here we call the SharePoint REST API without the sp library as an example. import { FetchOptions } from \"@pnp/core\"; import { AdalClient } from \"@pnp/adaljsclient\"; import { ODataDefaultParser } from \"@pnp/queryable\"; // ... public render(): void { // create an ADAL Client const client = AdalClient.fromSPFxContext(this.context); // setup the request options const opts: FetchOptions = { method: \"GET\", headers: { \"Accept\": \"application/json\", }, }; // execute the request client.fetch(\"https://{tenant}.sharepoint.com/_api/web\", opts).then(response => { // create a parser to convert the response into JSON. // You can create your own, at this point you have a fetch Response to work with const parser = new ODataDefaultParser(); parser.parse(response).then(json => { this.domElement.innerHTML = JSON.stringify(json); }); }).catch(e => { this.domElement.innerHTML = JSON.stringify(e); }); }","title":"Calling the any API"},{"location":"authentication/adaljsclient/#manually-configure","text":"This example shows setting up and using the AdalClient to make queries using information you have setup. You can review this article for more information on setting up and securing any application using AzureAD.","title":"Manually Configure"},{"location":"authentication/adaljsclient/#setup-and-use-with-microsoft-graph","text":"This sample uses a custom AzureAd app you have created and granted the appropriate permissions. import { AdalClient } from \"@pnp/adaljsclient\"; import { graph } from \"@pnp/graph\"; // configure the graph client // parameters are: // client id - the id of the application you created in azure ad // tenant - can be id or URL (shown) // redirect url - absolute url of a page to which your application and Azure AD app allows replies graph.setup({ graph: { fetchClientFactory: () => { return new AdalClient( \"00000000-0000-0000-0000-000000000000\", \"{tenant}.onmicrosoft.com\", \"https://myapp/singlesignon.aspx\"); }, }, }); try { // call the graph API const groups = await graph.groups(); console.log(JSON.stringify(groups, null, 4)); } catch (e) { console.error(e); }","title":"Setup and Use with Microsoft Graph"},{"location":"authentication/adaljsclient/#nodejs-applications","text":"We have a dedicated node client in @pnp/nodejs.","title":"Nodejs Applications"},{"location":"authentication/bearertokenclient/","text":"@pnp/core/BearerTokenFetchClient \u00b6 The BearerTokenFetchClient takes a single parameter representing an access token and uses it to make the requests. The disadvantage to this approach is not knowing to where the request will be sent, which in some cases is fine. An alternative is the LambdaFetchClient Static \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import { BearerTokenFetchClient } from \"@pnp/core\"; import { myTokenFactory } from \"./my-auth.js\"; graph.setup({ graph: { fetchClientFactory: () => { // note this method is not async, so your logic here cannot await. // Please see the LambdaFetchClient if you have a need for async support. const token = myTokenFactory(); return new BearerTokenFetchClient(token); }, }, });","title":"Bearer Token Client"},{"location":"authentication/bearertokenclient/#pnpcorebearertokenfetchclient","text":"The BearerTokenFetchClient takes a single parameter representing an access token and uses it to make the requests. The disadvantage to this approach is not knowing to where the request will be sent, which in some cases is fine. An alternative is the LambdaFetchClient","title":"@pnp/core/BearerTokenFetchClient"},{"location":"authentication/bearertokenclient/#static","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import { BearerTokenFetchClient } from \"@pnp/core\"; import { myTokenFactory } from \"./my-auth.js\"; graph.setup({ graph: { fetchClientFactory: () => { // note this method is not async, so your logic here cannot await. // Please see the LambdaFetchClient if you have a need for async support. const token = myTokenFactory(); return new BearerTokenFetchClient(token); }, }, });","title":"Static"},{"location":"authentication/client-spa/","text":"Authentication in Single Page Application \u00b6 If you are writing a single page application deployed outside SharePoint it is recommended to use the MSAL client. You can find further details on the settings in the MSAL docs . You will need to ensure that you grant the permissions required to the application you are trying to use. import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/common\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"{your redirect uri}\", }, cache: { cacheLocation: \"sessionStorage\", }, }, [\"email\", \"Files.Read.All\", \"User.Read.All\"]), }, }); const data = await graph.me();","title":"SPA Auth"},{"location":"authentication/client-spa/#authentication-in-single-page-application","text":"If you are writing a single page application deployed outside SharePoint it is recommended to use the MSAL client. You can find further details on the settings in the MSAL docs . You will need to ensure that you grant the permissions required to the application you are trying to use. import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/common\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"{your redirect uri}\", }, cache: { cacheLocation: \"sessionStorage\", }, }, [\"email\", \"Files.Read.All\", \"User.Read.All\"]), }, }); const data = await graph.me();","title":"Authentication in Single Page Application"},{"location":"authentication/client-spfx/","text":"Authentication in SharePoint Framework \u00b6 Auth as Current User \u00b6 PnPjs is designed to work as easily as possible within the SharePoint Framework so the authentication setup is very simple for the base case. Supply the current SharePoint Framework context to the library. This works for both SharePoint authentication and Graph authentication using the current user. Graph permissions are controlled by the permissions granted to the SharePoint shared application within your tenant. The below example is taken from a SharePoint Framework webpart. Connect to SharePoint as Current User \u00b6 import { sp } from \"@pnp/sp/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present sp.setup(this.context); } // ... Connect to Graph as Current User \u00b6 Permissions for this graph connection are controlled by the Shared SharePoint Application. You can target other applications using the MSAL Client . import { graph } from \"@pnp/graph/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present // this will use the ADAL client behind the scenes with no additional configuration work graph.setup(this.context); } // ... MSAL Client \u00b6 You might want/need to use a client configured to use your own AAD application and not the shared SharePoint application. You can do so using the MSAL client . Here we show this using graph, this works the same with any of the setup strategies . Please see the MSAL library docs for more details on what values to supply in the configuration. Note: you must install the @pnp/msaljsclient client package before using it import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/common\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"{your redirect uri}\", }, cache: { cacheLocation: \"sessionStorage\", }, }, [\"email\", \"Files.Read.All\", \"User.Read.All\"]), }, }); } // ... ADAL Client \u00b6 You can use the ADAL client from within SPFx, though it is recommended to transition to the MSAL client. Note: you must install the @pnp/adaljsclient client package before using it import { AdalClient } from \"@pnp/adaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present graph.setup({ graph: { fetchClientFactory: () => { return new AdalClient( \"00000000-0000-0000-0000-000000000000\", \"{tenant}.onmicrosoft.com\", \"\"); }, }); } // ...","title":"SPFx Auth"},{"location":"authentication/client-spfx/#authentication-in-sharepoint-framework","text":"","title":"Authentication in SharePoint Framework"},{"location":"authentication/client-spfx/#auth-as-current-user","text":"PnPjs is designed to work as easily as possible within the SharePoint Framework so the authentication setup is very simple for the base case. Supply the current SharePoint Framework context to the library. This works for both SharePoint authentication and Graph authentication using the current user. Graph permissions are controlled by the permissions granted to the SharePoint shared application within your tenant. The below example is taken from a SharePoint Framework webpart.","title":"Auth as Current User"},{"location":"authentication/client-spfx/#connect-to-sharepoint-as-current-user","text":"import { sp } from \"@pnp/sp/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present sp.setup(this.context); } // ...","title":"Connect to SharePoint as Current User"},{"location":"authentication/client-spfx/#connect-to-graph-as-current-user","text":"Permissions for this graph connection are controlled by the Shared SharePoint Application. You can target other applications using the MSAL Client . import { graph } from \"@pnp/graph/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present // this will use the ADAL client behind the scenes with no additional configuration work graph.setup(this.context); } // ...","title":"Connect to Graph as Current User"},{"location":"authentication/client-spfx/#msal-client","text":"You might want/need to use a client configured to use your own AAD application and not the shared SharePoint application. You can do so using the MSAL client . Here we show this using graph, this works the same with any of the setup strategies . Please see the MSAL library docs for more details on what values to supply in the configuration. Note: you must install the @pnp/msaljsclient client package before using it import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/common\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"{your redirect uri}\", }, cache: { cacheLocation: \"sessionStorage\", }, }, [\"email\", \"Files.Read.All\", \"User.Read.All\"]), }, }); } // ...","title":"MSAL Client"},{"location":"authentication/client-spfx/#adal-client","text":"You can use the ADAL client from within SPFx, though it is recommended to transition to the MSAL client. Note: you must install the @pnp/adaljsclient client package before using it import { AdalClient } from \"@pnp/adaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present graph.setup({ graph: { fetchClientFactory: () => { return new AdalClient( \"00000000-0000-0000-0000-000000000000\", \"{tenant}.onmicrosoft.com\", \"\"); }, }); } // ...","title":"ADAL Client"},{"location":"authentication/lambdaclient/","text":"@pnp/core/LambdaFetchClient \u00b6 The LambdaFetchClient class allows you to provide an async function that returns an access token using any logic/supporting libraries you need. This provides total freedom to define how you do authentication, so long as it results in a usable Bearer token to call the target resource. The advantage to the LambdaFetchClient is that you get the url for each request, meaning your logic can account for where the request is headed. The token function should be as efficient as possible as it's logic must complete before each request will be sent. Signature \u00b6 The LambdaFetchClient accepts a single argument of type ILambdaTokenFactoryParams. // signature of method, the return string is the access token (parms: ILambdaTokenFactoryParams) => Promise // ILambdaTokenFactoryParams export interface ILambdaTokenFactoryParams { /** * Url to which the request for which we are requesting a token will be sent */ url: string; /** * Any options supplied for the request */ options: IFetchOptions; } @azure/msal-browser example \u00b6 This example shows how to use @azure/msal-browser along with LambdaFetchClient to achieve signin. msal-browser has many possible configurations which are described within their documentation. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import { LambdaFetchClient } from \"@pnp/core\"; import { PublicClientApplication, Configuration } from \"@azure/msal-browser\"; const config: Configuration = { auth: { clientId: \"{client id}\", authority: \"https://login.microsoftonline.com/common/\" } } // create a single application, could also create this within the lambda client, but it would create a new applicaiton per request const msal = new PublicClientApplication(config); // create a new instance of the lambda fetch client const client = new LambdaFetchClient(async () => { const request = { scopes: [\"User.Read.All\"], }; const response = await msal.loginPopup(request); // lamba returns the access token return response.accessToken; }); // setup graph with the client graph.setup({ graph: { fetchClientFactory: () => client, }, }); // execute the request to graph which will use the client defined above const result = await graph.users();","title":"Lambda Token Client"},{"location":"authentication/lambdaclient/#pnpcorelambdafetchclient","text":"The LambdaFetchClient class allows you to provide an async function that returns an access token using any logic/supporting libraries you need. This provides total freedom to define how you do authentication, so long as it results in a usable Bearer token to call the target resource. The advantage to the LambdaFetchClient is that you get the url for each request, meaning your logic can account for where the request is headed. The token function should be as efficient as possible as it's logic must complete before each request will be sent.","title":"@pnp/core/LambdaFetchClient"},{"location":"authentication/lambdaclient/#signature","text":"The LambdaFetchClient accepts a single argument of type ILambdaTokenFactoryParams. // signature of method, the return string is the access token (parms: ILambdaTokenFactoryParams) => Promise // ILambdaTokenFactoryParams export interface ILambdaTokenFactoryParams { /** * Url to which the request for which we are requesting a token will be sent */ url: string; /** * Any options supplied for the request */ options: IFetchOptions; }","title":"Signature"},{"location":"authentication/lambdaclient/#azuremsal-browser-example","text":"This example shows how to use @azure/msal-browser along with LambdaFetchClient to achieve signin. msal-browser has many possible configurations which are described within their documentation. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import { LambdaFetchClient } from \"@pnp/core\"; import { PublicClientApplication, Configuration } from \"@azure/msal-browser\"; const config: Configuration = { auth: { clientId: \"{client id}\", authority: \"https://login.microsoftonline.com/common/\" } } // create a single application, could also create this within the lambda client, but it would create a new applicaiton per request const msal = new PublicClientApplication(config); // create a new instance of the lambda fetch client const client = new LambdaFetchClient(async () => { const request = { scopes: [\"User.Read.All\"], }; const response = await msal.loginPopup(request); // lamba returns the access token return response.accessToken; }); // setup graph with the client graph.setup({ graph: { fetchClientFactory: () => client, }, }); // execute the request to graph which will use the client defined above const result = await graph.users();","title":"@azure/msal-browser example"},{"location":"authentication/msaljsclient/","text":"msaljsclient - MSAL Client for PnPjs \u00b6 The MSAL client is a thin wrapper around the MSAL library adapting it for use with PnPjs's request pipeline. Install \u00b6 You need to install the MSAL client before using it. This is in addition to installing the other PnPjs libraries you require. npm install @pnp/msaljsclient --save Configure \u00b6 The PnP client is a very thin wrapper around the MSAL library and you can supply any of the arguments supported. These are described in the MSAL docs . The basic configuration values you need (at least from our testing) are client id, authority, and redirectUri. The other options are settable but not required. This article is not intended to be an exhaustive discussion of all the MSAL configuration possibilities, please see the official docs to understand all of the available options. The second parameter when configuring the PnP client is the list of scope you are seeking to use. These must be configured and properly granted within AAD and you can request one or more scopes as needed for the current scenario. Use in SPFx \u00b6 Calling SharePoint via MSAL \u00b6 When calling the SharePoint REST API we must use only a special scope \"https://{tenant}.sharepoint.com/.default\" import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/mytentant.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://mytentant.sharepoint.com/sites/dev/SitePages/test.aspx\", }, }, [\"https://mytentant.sharepoint.com/.default\"]), }, }); const r = await sp.web(); Calling Graph via MSAL \u00b6 When calling the graph API you must specify the scopes you need and ensure they are configured in AAD import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/tenant.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://tenant.sharepoint.com/sites/dev/SitePages/test.aspx\", }, }, [\"Group.Read.All\"]), }, }); const r = await graph.groups(); Use in Single Page Applications \u00b6 You can also use the PnPjs MSAL client within your SPA applications. Please review the various settings to ensure you are configuring MSAL as needed for your application import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/tenant.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://myapp.com/login.aspx\", }, }, [\"Group.Read.All\"]), }, }); const r = await graph.groups(); Get a Token \u00b6 You can also use the client to get a token if you need a token for use outside the PnPjs libraries import { MsalClient } from \"@pnp/msaljsclient\"; // note we do not provide scopes here as the second parameter. We certainly could and will get a token // based on those scopes by making a call to getToken() without a param. const client = new MsalClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant}.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://{tenant}.sharepoint.com/sites/dev/SitePages/webpacktest.aspx\", }, }); const token = await client.getToken([\"Group.Read.All\"]); const token2 = await client.getToken([\"Files.Read\"]);","title":"MSAL Client"},{"location":"authentication/msaljsclient/#msaljsclient-msal-client-for-pnpjs","text":"The MSAL client is a thin wrapper around the MSAL library adapting it for use with PnPjs's request pipeline.","title":"msaljsclient - MSAL Client for PnPjs"},{"location":"authentication/msaljsclient/#install","text":"You need to install the MSAL client before using it. This is in addition to installing the other PnPjs libraries you require. npm install @pnp/msaljsclient --save","title":"Install"},{"location":"authentication/msaljsclient/#configure","text":"The PnP client is a very thin wrapper around the MSAL library and you can supply any of the arguments supported. These are described in the MSAL docs . The basic configuration values you need (at least from our testing) are client id, authority, and redirectUri. The other options are settable but not required. This article is not intended to be an exhaustive discussion of all the MSAL configuration possibilities, please see the official docs to understand all of the available options. The second parameter when configuring the PnP client is the list of scope you are seeking to use. These must be configured and properly granted within AAD and you can request one or more scopes as needed for the current scenario.","title":"Configure"},{"location":"authentication/msaljsclient/#use-in-spfx","text":"","title":"Use in SPFx"},{"location":"authentication/msaljsclient/#calling-sharepoint-via-msal","text":"When calling the SharePoint REST API we must use only a special scope \"https://{tenant}.sharepoint.com/.default\" import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/mytentant.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://mytentant.sharepoint.com/sites/dev/SitePages/test.aspx\", }, }, [\"https://mytentant.sharepoint.com/.default\"]), }, }); const r = await sp.web();","title":"Calling SharePoint via MSAL"},{"location":"authentication/msaljsclient/#calling-graph-via-msal","text":"When calling the graph API you must specify the scopes you need and ensure they are configured in AAD import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/tenant.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://tenant.sharepoint.com/sites/dev/SitePages/test.aspx\", }, }, [\"Group.Read.All\"]), }, }); const r = await graph.groups();","title":"Calling Graph via MSAL"},{"location":"authentication/msaljsclient/#use-in-single-page-applications","text":"You can also use the PnPjs MSAL client within your SPA applications. Please review the various settings to ensure you are configuring MSAL as needed for your application import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/tenant.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://myapp.com/login.aspx\", }, }, [\"Group.Read.All\"]), }, }); const r = await graph.groups();","title":"Use in Single Page Applications"},{"location":"authentication/msaljsclient/#get-a-token","text":"You can also use the client to get a token if you need a token for use outside the PnPjs libraries import { MsalClient } from \"@pnp/msaljsclient\"; // note we do not provide scopes here as the second parameter. We certainly could and will get a token // based on those scopes by making a call to getToken() without a param. const client = new MsalClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant}.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://{tenant}.sharepoint.com/sites/dev/SitePages/webpacktest.aspx\", }, }); const token = await client.getToken([\"Group.Read.All\"]); const token2 = await client.getToken([\"Files.Read\"]);","title":"Get a Token"},{"location":"authentication/server-nodejs/","text":"Authentication in Nodejs \u00b6 SharePoint App Registration \u00b6 Due to a recent change in how SPO is configured NEW tenants will have ACS authentication disabled by default. You can read more details in this article . For testing we recommend using MSAL Certificate Auth . Within the PnPjs testing framework we make use of SharePoint App Registration. This uses the SPFetchClient client from the nodejs package. This client works based on the legacy SharePoint App Registration model making use of a client and secret granted permissions through AppInv.aspx. This method works and at the time of writing has no published end date. See: details on how to register a legacy SharePoint application . import { SPFetchClient } from \"@pnp/nodejs\"; import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\"); }, }, }); // execute a library request as normal const w = await sp.web(); MSAL \u00b6 Added in 2.0.11 You can now use the @azure/msal-node client with PnPjs using MsalFetchClient. You must configure an AAD application with the appropriate permissions for your application. At the time this article was written the msal-node package is not yet GA. Call Graph \u00b6 You can call the Microsoft Graph API with a client id and secret or certificate (see SharePoint example for cert auth) import { graph } from \"@pnp/graph/presets/all\"; // configure your node options graph.setup({ graph: { fetchClientFactory: () => { return new MsalFetchClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant id or common}/\", clientId: \"{guid}\", clientSecret: \"{client secret}\", } }); }, }, }); const userInfo = await graph.users(); Call SharePoint \u00b6 To call the SharePoint APIs via MSAL you are required to use certificate authentication with your application. Fully covering certificates is outside the scope of these docs, but the following commands were used with openssl to create testing certs for the sample code below. mkdir \\temp cd \\temp openssl req -x509 -newkey rsa:2048 -keyout keytmp.pem -out cert.pem -days 365 -passout pass:HereIsMySuperPass -subj '/C=US/ST=Washington/L=Seattle' openssl rsa -in keytmp.pem -out key.pem -passin pass:HereIsMySuperPass Using the above code you end up with three files, \"cert.pem\", \"key.pem\", and \"keytmp.pem\". The \"cert.pem\" file is uploaded to your AAD application registration. The \"key.pem\" is read as the private key for the configuration. You need to set the baseUrl property when using the MsalFetchClient import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { readFileSync } from \"fs\"; // read in our private key const buffer = readFileSync(\"c:/temp/key.pem\"); // configure node options sp.setup({ sp: { baseUrl: \"https://{my tenant}.sharepoint.com/sites/dev/\", fetchClientFactory: () => { return new MsalFetchClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant id or common}/\", clientCertificate: { thumbprint: \"{certificate thumbprint, displayed in AAD}\", privateKey: buffer.toString(), }, clientId: \"{client id}\", } }, [\"https://{my tenant}.sharepoint.com/.default\"]); // you must set the scope for SharePoint access }, }, }); const w = await sp.web(); ADAL \u00b6 The AdalFetchClient class depends on the adal-node package to authenticate against Azure AD. The example below outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected. See: More details on the node client import { AdalFetchClient } from \"@pnp/nodejs\"; import { graph } from \"@pnp/graph/presets/all\"; // setup the client using graph setup function graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{tenant}\", \"{app id}\", \"{app secret}\"); }, }, }); // execute a library request as normal const g = await graph.groups(); console.log(JSON.stringify(g, null, 4));","title":"NodeJS Auth"},{"location":"authentication/server-nodejs/#authentication-in-nodejs","text":"","title":"Authentication in Nodejs"},{"location":"authentication/server-nodejs/#sharepoint-app-registration","text":"Due to a recent change in how SPO is configured NEW tenants will have ACS authentication disabled by default. You can read more details in this article . For testing we recommend using MSAL Certificate Auth . Within the PnPjs testing framework we make use of SharePoint App Registration. This uses the SPFetchClient client from the nodejs package. This client works based on the legacy SharePoint App Registration model making use of a client and secret granted permissions through AppInv.aspx. This method works and at the time of writing has no published end date. See: details on how to register a legacy SharePoint application . import { SPFetchClient } from \"@pnp/nodejs\"; import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\"); }, }, }); // execute a library request as normal const w = await sp.web();","title":"SharePoint App Registration"},{"location":"authentication/server-nodejs/#msal","text":"Added in 2.0.11 You can now use the @azure/msal-node client with PnPjs using MsalFetchClient. You must configure an AAD application with the appropriate permissions for your application. At the time this article was written the msal-node package is not yet GA.","title":"MSAL"},{"location":"authentication/server-nodejs/#call-graph","text":"You can call the Microsoft Graph API with a client id and secret or certificate (see SharePoint example for cert auth) import { graph } from \"@pnp/graph/presets/all\"; // configure your node options graph.setup({ graph: { fetchClientFactory: () => { return new MsalFetchClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant id or common}/\", clientId: \"{guid}\", clientSecret: \"{client secret}\", } }); }, }, }); const userInfo = await graph.users();","title":"Call Graph"},{"location":"authentication/server-nodejs/#call-sharepoint","text":"To call the SharePoint APIs via MSAL you are required to use certificate authentication with your application. Fully covering certificates is outside the scope of these docs, but the following commands were used with openssl to create testing certs for the sample code below. mkdir \\temp cd \\temp openssl req -x509 -newkey rsa:2048 -keyout keytmp.pem -out cert.pem -days 365 -passout pass:HereIsMySuperPass -subj '/C=US/ST=Washington/L=Seattle' openssl rsa -in keytmp.pem -out key.pem -passin pass:HereIsMySuperPass Using the above code you end up with three files, \"cert.pem\", \"key.pem\", and \"keytmp.pem\". The \"cert.pem\" file is uploaded to your AAD application registration. The \"key.pem\" is read as the private key for the configuration. You need to set the baseUrl property when using the MsalFetchClient import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { readFileSync } from \"fs\"; // read in our private key const buffer = readFileSync(\"c:/temp/key.pem\"); // configure node options sp.setup({ sp: { baseUrl: \"https://{my tenant}.sharepoint.com/sites/dev/\", fetchClientFactory: () => { return new MsalFetchClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant id or common}/\", clientCertificate: { thumbprint: \"{certificate thumbprint, displayed in AAD}\", privateKey: buffer.toString(), }, clientId: \"{client id}\", } }, [\"https://{my tenant}.sharepoint.com/.default\"]); // you must set the scope for SharePoint access }, }, }); const w = await sp.web();","title":"Call SharePoint"},{"location":"authentication/server-nodejs/#adal","text":"The AdalFetchClient class depends on the adal-node package to authenticate against Azure AD. The example below outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected. See: More details on the node client import { AdalFetchClient } from \"@pnp/nodejs\"; import { graph } from \"@pnp/graph/presets/all\"; // setup the client using graph setup function graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{tenant}\", \"{app id}\", \"{app secret}\"); }, }, }); // execute a library request as normal const g = await graph.groups(); console.log(JSON.stringify(g, null, 4));","title":"ADAL"},{"location":"authentication/sp-app-registration/","text":"Legacy SharePoint App Registration \u00b6 This section outlines how to register for a client id and secret for use in the above code. Due to a recent change in how SPO is configured NEW tenants will have ACS authentication disabled by default. You can read more details in this article . For testing we recommend using MSAL Certificate Authentication . Register An Add-In \u00b6 Before you can begin running tests you need to register a low-trust add-in with SharePoint. This is primarily designed for Office 365, but can work on-premises if you configure your farm accordingly . Navigation to {site url}/_layouts/appregnew.aspx Click \"Generate\" for both the Client Id and Secret values Give you add-in a title, this can be anything but will let you locate it in the list of add-in permissions Provide a fake value for app domain and redirect uri Click \"Create\" Copy the returned block of text containing the client id and secret as well as app name for your records and later in this article. Grant Your Add-In Permissions \u00b6 Now that we have created an add-in registration we need to tell SharePoint what permissions it can use. Due to an update in SharePoint Online you now have to register add-ins with certain permissions in the admin site . Navigate to {admin site url}/_layouts/appinv.aspx Paste your client id from the above section into the App Id box and click \"Lookup\" You should see the information populated into the form from the last section, if not ensure you have the correct id value Paste the below XML into the permissions request xml box and hit \"Create\" You should get a confirmation message. Note that the above XML will grant full tenant control. This is OK for testing, but you should grant only those permissions necessary for your application in production.","title":"SP App Reg"},{"location":"authentication/sp-app-registration/#legacy-sharepoint-app-registration","text":"This section outlines how to register for a client id and secret for use in the above code. Due to a recent change in how SPO is configured NEW tenants will have ACS authentication disabled by default. You can read more details in this article . For testing we recommend using MSAL Certificate Authentication .","title":"Legacy SharePoint App Registration"},{"location":"authentication/sp-app-registration/#register-an-add-in","text":"Before you can begin running tests you need to register a low-trust add-in with SharePoint. This is primarily designed for Office 365, but can work on-premises if you configure your farm accordingly . Navigation to {site url}/_layouts/appregnew.aspx Click \"Generate\" for both the Client Id and Secret values Give you add-in a title, this can be anything but will let you locate it in the list of add-in permissions Provide a fake value for app domain and redirect uri Click \"Create\" Copy the returned block of text containing the client id and secret as well as app name for your records and later in this article.","title":"Register An Add-In"},{"location":"authentication/sp-app-registration/#grant-your-add-in-permissions","text":"Now that we have created an add-in registration we need to tell SharePoint what permissions it can use. Due to an update in SharePoint Online you now have to register add-ins with certain permissions in the admin site . Navigate to {admin site url}/_layouts/appinv.aspx Paste your client id from the above section into the App Id box and click \"Lookup\" You should see the information populated into the form from the last section, if not ensure you have the correct id value Paste the below XML into the permissions request xml box and hit \"Create\" You should get a confirmation message. Note that the above XML will grant full tenant control. This is OK for testing, but you should grant only those permissions necessary for your application in production.","title":"Grant Your Add-In Permissions"},{"location":"common/","text":"@pnp/core \u00b6 The common modules provides a set of utilities classes and reusable building blocks used throughout the @pnp modules. They can be used within your applications as well. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/core --save Import and use functionality, see details on modules below. import { getGUID } from \"@pnp/core\"; console.log(getGUID()); Exports \u00b6 collections libconfig netutil storage util Custom HttpClient","title":"common"},{"location":"common/#pnpcore","text":"The common modules provides a set of utilities classes and reusable building blocks used throughout the @pnp modules. They can be used within your applications as well.","title":"@pnp/core"},{"location":"common/#getting-started","text":"Install the library and required dependencies npm install @pnp/core --save Import and use functionality, see details on modules below. import { getGUID } from \"@pnp/core\"; console.log(getGUID());","title":"Getting Started"},{"location":"common/#exports","text":"collections libconfig netutil storage util Custom HttpClient","title":"Exports"},{"location":"common/collections/","text":"@pnp/core/collections \u00b6 The collections module provides typings and classes related to working with dictionaries. TypedHash \u00b6 Interface used to described an object with string keys corresponding to values of type T export interface TypedHash { [key: string]: T; } objectToMap \u00b6 Converts a plain object to a Map instance const map = objectToMap({ a: \"b\", c: \"d\"}); mergeMaps \u00b6 Merges two or more maps, overwriting values with the same key. Last value in wins. const m1 = new Map(); const m2 = new Map(); const m3 = new Map(); const m4 = new Map(); const m = mergeMaps(m1, m2, m3, m4);","title":"collections"},{"location":"common/collections/#pnpcorecollections","text":"The collections module provides typings and classes related to working with dictionaries.","title":"@pnp/core/collections"},{"location":"common/collections/#typedhash","text":"Interface used to described an object with string keys corresponding to values of type T export interface TypedHash { [key: string]: T; }","title":"TypedHash"},{"location":"common/collections/#objecttomap","text":"Converts a plain object to a Map instance const map = objectToMap({ a: \"b\", c: \"d\"});","title":"objectToMap"},{"location":"common/collections/#mergemaps","text":"Merges two or more maps, overwriting values with the same key. Last value in wins. const m1 = new Map(); const m2 = new Map(); const m3 = new Map(); const m4 = new Map(); const m = mergeMaps(m1, m2, m3, m4);","title":"mergeMaps"},{"location":"common/custom-httpclientimpl/","text":"Custom HttpClientImpl \u00b6 This should be considered an advanced topic and creating a custom HttpClientImpl is not something you will likely need to do. Also, we don't offer support beyond this article for writing your own implementation. It is possible you may need complete control over the sending and receiving of requests. Before you get started read and understand the fetch specification as you are essentially writing a custom fetch implementation. The first step (second if you read the fetch spec as mentioned just above) is to understand the interface you need to implement, HttpClientImpl. export interface HttpClientImpl { fetch(url: string, options: FetchOptions): Promise; } There is a single method \"fetch\" which takes a url string and a set of options. These options can be just about anything but are constrained within the library to the FetchOptions interface. export interface FetchOptions { method?: string; headers?: HeadersInit | { [index: string]: string }; body?: BodyInit; mode?: string | RequestMode; credentials?: string | RequestCredentials; cache?: string | RequestCache; } So you will need to handle any of those options along with the provided url when sending your request. The library will expect your implementation to return a Promise that resolves to a Response defined by the fetch specification - which you've already read \ud83d\udc4d. Using Your Custom HttpClientImpl \u00b6 Once you have written your implementation using it on your requests is done by setting it in the global library configuration: import { setup } from \"@pnp/core\"; import { sp, Web } from \"@pnp/sp\"; import { MyAwesomeClient } from \"./awesomeclient\"; sp.setup({ sp: { fetchClientFactory: () => { return new MyAwesomeClient(); } } }); let w = new Web(\"{site url}\"); // this request will use your client. const result = await w.select(\"Title\")(); console.log(result); Subclassing is Better \u00b6 You can of course inherit from one of the implementations available within the @pnp scope if you just need to say add a header or need to do something to every request sent. Perhaps some advanced logging. This approach will save you from needing to fully write a fetch implementation. A FINAL NOTE \u00b6 Whatever you do, do not write a client that uses a client id and secret and exposes them on the client side. Client Id and Secret should only ever be used on a server, never exposed to clients as anyone with those values has the full permissions granted to that id and secret.","title":"Custom HttpClientImpl"},{"location":"common/custom-httpclientimpl/#custom-httpclientimpl","text":"This should be considered an advanced topic and creating a custom HttpClientImpl is not something you will likely need to do. Also, we don't offer support beyond this article for writing your own implementation. It is possible you may need complete control over the sending and receiving of requests. Before you get started read and understand the fetch specification as you are essentially writing a custom fetch implementation. The first step (second if you read the fetch spec as mentioned just above) is to understand the interface you need to implement, HttpClientImpl. export interface HttpClientImpl { fetch(url: string, options: FetchOptions): Promise; } There is a single method \"fetch\" which takes a url string and a set of options. These options can be just about anything but are constrained within the library to the FetchOptions interface. export interface FetchOptions { method?: string; headers?: HeadersInit | { [index: string]: string }; body?: BodyInit; mode?: string | RequestMode; credentials?: string | RequestCredentials; cache?: string | RequestCache; } So you will need to handle any of those options along with the provided url when sending your request. The library will expect your implementation to return a Promise that resolves to a Response defined by the fetch specification - which you've already read \ud83d\udc4d.","title":"Custom HttpClientImpl"},{"location":"common/custom-httpclientimpl/#using-your-custom-httpclientimpl","text":"Once you have written your implementation using it on your requests is done by setting it in the global library configuration: import { setup } from \"@pnp/core\"; import { sp, Web } from \"@pnp/sp\"; import { MyAwesomeClient } from \"./awesomeclient\"; sp.setup({ sp: { fetchClientFactory: () => { return new MyAwesomeClient(); } } }); let w = new Web(\"{site url}\"); // this request will use your client. const result = await w.select(\"Title\")(); console.log(result);","title":"Using Your Custom HttpClientImpl"},{"location":"common/custom-httpclientimpl/#subclassing-is-better","text":"You can of course inherit from one of the implementations available within the @pnp scope if you just need to say add a header or need to do something to every request sent. Perhaps some advanced logging. This approach will save you from needing to fully write a fetch implementation.","title":"Subclassing is Better"},{"location":"common/custom-httpclientimpl/#a-final-note","text":"Whatever you do, do not write a client that uses a client id and secret and exposes them on the client side. Client Id and Secret should only ever be used on a server, never exposed to clients as anyone with those values has the full permissions granted to that id and secret.","title":"A FINAL NOTE"},{"location":"common/libconfig/","text":"@pnp/core/libconfig \u00b6 Contains the shared classes and interfaces used to configure the libraries. These bases classes are expanded on in dependent libraries with the core configuration defined here. This module exposes an instance of the RuntimeConfigImpl class: RuntimeConfig. This configuration object can be referenced and contains the global configuration shared across the libraries. You can also extend the configuration for use within your own applications. ILibraryConfiguration Interface \u00b6 Defines the shared configurable values used across the library as shown below. Each of these has a default value as shown below export interface ILibraryConfiguration { /** * Allows caching to be global disabled, default: false */ globalCacheDisable?: boolean; /** * Defines the default store used by the usingCaching method, default: session */ defaultCachingStore?: \"session\" | \"local\"; /** * Defines the default timeout in seconds used by the usingCaching method, default 30 */ defaultCachingTimeoutSeconds?: number; /** * If true a timeout expired items will be removed from the cache in intervals determined by cacheTimeoutInterval */ enableCacheExpiration?: boolean; /** * Determines the interval in milliseconds at which the cache is checked to see if items have expired (min: 100) */ cacheExpirationIntervalMilliseconds?: number; /** * Used to supply the current context from an SPFx webpart to the library */ spfxContext?: any; } RuntimeConfigImpl \u00b6 The class which implements the runtime configuration management as well as sets the default values used within the library. At its heart lies a Dictionary used to track the configuration values. The keys will match the values in the interface or plain object passed to the extend method. assign \u00b6 The assign method is used to add configuration to the global configuration instance. You can pass it any plain object with string keys and those values will be added. Any existing values will be overwritten based on the keys. Last value in wins. For a more detailed scenario of using the RuntimeConfig instance in your own application please see the section below \"Using RuntimeConfig within your application\". Note there are no methods to remove/clear the global config as it should be considered fairly static as frequent updates may have unpredictable side effects as it is a global shared object. Generally it should be set at the start of your application. import { RuntimeConfig } from \"@pnp/core\"; // add your custom keys to the global configuration // note you can use object hashes as values RuntimeConfig.assign({ \"myKey1\": \"value 1\", \"myKey2\": { \"subKey\": \"sub value 1\", \"subKey2\": \"sub value 2\", }, }); // read your custom values const v = RuntimeConfig.get(\"myKey1\"); // \"value 1\" Using RuntimeConfig within your Application \u00b6 If you have a set of properties you will access very frequently it may be desirable to implement your own configuration object and expose those values as properties. To do so you will need to create an interface for your configuration (optional) and a wrapper class for RuntimeConfig to expose your properties import { ILibraryConfiguration, RuntimeConfig, ITypedHash } from \"@pnp/core\"; // first we create our own interface by extending LibraryConfiguration. This allows your class to accept all the values with correct type checking. Note, because // TypeScript allows you to extend from multiple interfaces you can build a complex configuration definition from many sub definitions. // create the interface of your properties // by creating this separately you allows others to compose your parts into their own config interface MyConfigurationPart { // you can create a grouped definition and access your settings as an object // keys can be optional or required as defined by your interface my?: { prop1?: string; prop2?: string; } // and/or define multiple top level properties (beware key collision) // it is good practice to use a unique prefix myProp1: string; myProp2: number; } // now create a combined interface interface MyConfiguration extends ILibraryConfiguration, MyConfigurationPart { } // now create a wrapper object and expose your properties class MyRuntimeConfigImpl { // exposing a nested property public get prop1(): ITypedHash { const myPart = RuntimeConfig.get(\"my\"); if (myPart !== null && typeof myPart !== \"undefined\" && typeof myPart.prop1 !== \"undefined\") { return myPart.prop1; } return {}; } // exposing a root level property public get myProp1(): string | null { let myProp1 = RuntimeConfig.get(\"myProp1\"); if (myProp1 === null) { myProp1 = \"some default value\"; } return myProp1; } setup(config: MyConfiguration): void { RuntimeConfig.assign(config); } } // create a single static instance of your impl class export let MyRuntimeConfig = new MyRuntimeConfigImpl(); Now in other files you can use and set your configuration with a typed interface and properties import { MyRuntimeConfig } from \"{location of module}\"; MyRuntimeConfig.setup({ my: { prop1: \"hello\", }, }); const value = MyRuntimeConfig.myProp1; // \"hello\"","title":"libconfig"},{"location":"common/libconfig/#pnpcorelibconfig","text":"Contains the shared classes and interfaces used to configure the libraries. These bases classes are expanded on in dependent libraries with the core configuration defined here. This module exposes an instance of the RuntimeConfigImpl class: RuntimeConfig. This configuration object can be referenced and contains the global configuration shared across the libraries. You can also extend the configuration for use within your own applications.","title":"@pnp/core/libconfig"},{"location":"common/libconfig/#ilibraryconfiguration-interface","text":"Defines the shared configurable values used across the library as shown below. Each of these has a default value as shown below export interface ILibraryConfiguration { /** * Allows caching to be global disabled, default: false */ globalCacheDisable?: boolean; /** * Defines the default store used by the usingCaching method, default: session */ defaultCachingStore?: \"session\" | \"local\"; /** * Defines the default timeout in seconds used by the usingCaching method, default 30 */ defaultCachingTimeoutSeconds?: number; /** * If true a timeout expired items will be removed from the cache in intervals determined by cacheTimeoutInterval */ enableCacheExpiration?: boolean; /** * Determines the interval in milliseconds at which the cache is checked to see if items have expired (min: 100) */ cacheExpirationIntervalMilliseconds?: number; /** * Used to supply the current context from an SPFx webpart to the library */ spfxContext?: any; }","title":"ILibraryConfiguration Interface"},{"location":"common/libconfig/#runtimeconfigimpl","text":"The class which implements the runtime configuration management as well as sets the default values used within the library. At its heart lies a Dictionary used to track the configuration values. The keys will match the values in the interface or plain object passed to the extend method.","title":"RuntimeConfigImpl"},{"location":"common/libconfig/#assign","text":"The assign method is used to add configuration to the global configuration instance. You can pass it any plain object with string keys and those values will be added. Any existing values will be overwritten based on the keys. Last value in wins. For a more detailed scenario of using the RuntimeConfig instance in your own application please see the section below \"Using RuntimeConfig within your application\". Note there are no methods to remove/clear the global config as it should be considered fairly static as frequent updates may have unpredictable side effects as it is a global shared object. Generally it should be set at the start of your application. import { RuntimeConfig } from \"@pnp/core\"; // add your custom keys to the global configuration // note you can use object hashes as values RuntimeConfig.assign({ \"myKey1\": \"value 1\", \"myKey2\": { \"subKey\": \"sub value 1\", \"subKey2\": \"sub value 2\", }, }); // read your custom values const v = RuntimeConfig.get(\"myKey1\"); // \"value 1\"","title":"assign"},{"location":"common/libconfig/#using-runtimeconfig-within-your-application","text":"If you have a set of properties you will access very frequently it may be desirable to implement your own configuration object and expose those values as properties. To do so you will need to create an interface for your configuration (optional) and a wrapper class for RuntimeConfig to expose your properties import { ILibraryConfiguration, RuntimeConfig, ITypedHash } from \"@pnp/core\"; // first we create our own interface by extending LibraryConfiguration. This allows your class to accept all the values with correct type checking. Note, because // TypeScript allows you to extend from multiple interfaces you can build a complex configuration definition from many sub definitions. // create the interface of your properties // by creating this separately you allows others to compose your parts into their own config interface MyConfigurationPart { // you can create a grouped definition and access your settings as an object // keys can be optional or required as defined by your interface my?: { prop1?: string; prop2?: string; } // and/or define multiple top level properties (beware key collision) // it is good practice to use a unique prefix myProp1: string; myProp2: number; } // now create a combined interface interface MyConfiguration extends ILibraryConfiguration, MyConfigurationPart { } // now create a wrapper object and expose your properties class MyRuntimeConfigImpl { // exposing a nested property public get prop1(): ITypedHash { const myPart = RuntimeConfig.get(\"my\"); if (myPart !== null && typeof myPart !== \"undefined\" && typeof myPart.prop1 !== \"undefined\") { return myPart.prop1; } return {}; } // exposing a root level property public get myProp1(): string | null { let myProp1 = RuntimeConfig.get(\"myProp1\"); if (myProp1 === null) { myProp1 = \"some default value\"; } return myProp1; } setup(config: MyConfiguration): void { RuntimeConfig.assign(config); } } // create a single static instance of your impl class export let MyRuntimeConfig = new MyRuntimeConfigImpl(); Now in other files you can use and set your configuration with a typed interface and properties import { MyRuntimeConfig } from \"{location of module}\"; MyRuntimeConfig.setup({ my: { prop1: \"hello\", }, }); const value = MyRuntimeConfig.myProp1; // \"hello\"","title":"Using RuntimeConfig within your Application"},{"location":"common/netutil/","text":"@pnp/core/net \u00b6 This module contains a set of classes and interfaces used to characterize shared http interactions and configuration of the libraries. Some of the interfaces are described below (many have no use outside the library) as well as several classes. Interfaces \u00b6 HttpClientImpl \u00b6 Defines an implementation of an Http Client within the context of @pnp. This being a class with a a single method \"fetch\" takes a URL and options. It returns a Promise . Used primarily with the shared request pipeline to define the client used to make the actual request. You can write your own custom implementation if needed. RequestClient \u00b6 An abstraction that contains specific methods related to each of the primary request methods get, post, patch, delete as well as fetch and fetchRaw. The difference between fetch and fetchRaw is that a client may include additional logic or processing in fetch, where fetchRaw should be a direct call to the underlying HttpClientImpl fetch method. Classes \u00b6 This module export two classes of note, FetchClient and BearerTokenFetchClient. Both implement HttpClientImpl. FetchClient \u00b6 Basic implementation that calls the global (window) fetch method with no additional processing. import { FetchClient } from \"@pnp/core\"; const client = new FetchClient(); client.fetch(\"{url}\", {}); BearerTokenFetchClient \u00b6 A simple implementation that takes a provided authentication token and adds the Authentication Bearer header to the request. No other processing is done and the token is treated as a static string. import { BearerTokenFetchClient } from \"@pnp/core\"; const client = new BearerTokenFetchClient(\"{authentication token}\"); client.fetch(\"{url}\", {});","title":"netutil"},{"location":"common/netutil/#pnpcorenet","text":"This module contains a set of classes and interfaces used to characterize shared http interactions and configuration of the libraries. Some of the interfaces are described below (many have no use outside the library) as well as several classes.","title":"@pnp/core/net"},{"location":"common/netutil/#interfaces","text":"","title":"Interfaces"},{"location":"common/netutil/#httpclientimpl","text":"Defines an implementation of an Http Client within the context of @pnp. This being a class with a a single method \"fetch\" takes a URL and options. It returns a Promise . Used primarily with the shared request pipeline to define the client used to make the actual request. You can write your own custom implementation if needed.","title":"HttpClientImpl"},{"location":"common/netutil/#requestclient","text":"An abstraction that contains specific methods related to each of the primary request methods get, post, patch, delete as well as fetch and fetchRaw. The difference between fetch and fetchRaw is that a client may include additional logic or processing in fetch, where fetchRaw should be a direct call to the underlying HttpClientImpl fetch method.","title":"RequestClient"},{"location":"common/netutil/#classes","text":"This module export two classes of note, FetchClient and BearerTokenFetchClient. Both implement HttpClientImpl.","title":"Classes"},{"location":"common/netutil/#fetchclient","text":"Basic implementation that calls the global (window) fetch method with no additional processing. import { FetchClient } from \"@pnp/core\"; const client = new FetchClient(); client.fetch(\"{url}\", {});","title":"FetchClient"},{"location":"common/netutil/#bearertokenfetchclient","text":"A simple implementation that takes a provided authentication token and adds the Authentication Bearer header to the request. No other processing is done and the token is treated as a static string. import { BearerTokenFetchClient } from \"@pnp/core\"; const client = new BearerTokenFetchClient(\"{authentication token}\"); client.fetch(\"{url}\", {});","title":"BearerTokenFetchClient"},{"location":"common/storage/","text":"@pnp/core/storage \u00b6 This module provides a thin wrapper over the browser storage options, local and session. If neither option is available it shims storage with a non-persistent in memory polyfill. Optionally through configuration you can activate expiration. Sample usage is shown below. PnPClientStorage \u00b6 The main export of this module, contains properties representing local and session storage. import { PnPClientStorage } from \"@pnp/core\"; const storage = new PnPClientStorage(); const myvalue = storage.local.get(\"mykey\"); PnPClientStorageWrapper \u00b6 Each of the storage locations (session and local) are wrapped with this helper class. You can use it directly, but generally it would be used from an instance of PnPClientStorage as shown below. These examples all use local storage, the operations are identical for session storage. import { PnPClientStorage } from \"@pnp/core\"; const storage = new PnPClientStorage(); // get a value from storage const value = storage.local.get(\"mykey\"); // put a value into storage storage.local.put(\"mykey2\", \"my value\"); // put a value into storage with an expiration storage.local.put(\"mykey2\", \"my value\", new Date()); // put a simple object into storage // because JSON.stringify is used to package the object we do NOT do a deep rehydration of stored objects storage.local.put(\"mykey3\", { key: \"value\", key2: \"value2\", }); // remove a value from storage storage.local.delete(\"mykey3\"); // get an item or add it if it does not exist // returns a promise in case you need time to get the value for storage // optionally takes a third parameter specifying the expiration storage.local.getOrPut(\"mykey4\", () => { return Promise.resolve(\"value\"); }); // delete expired items storage.local.deleteExpired(); Cache Expiration \u00b6 The ability remove of expired items based on a configured timeout can help if the cache is filling up. This can be accomplished in two ways. The first is to explicitly call the new deleteExpired method on the cache you wish to clear. A suggested usage is to add this into your page init code as clearing expired items once per page load is likely sufficient. import { PnPClientStorage } from \"@pnp/core\"; const storage = new PnPClientStorage(); // session storage storage.session.deleteExpired(); // local storage storage.local.deleteExpired(); // this returns a promise, so you can perform some activity after the expired items are removed: storage.local.deleteExpired().then(_ => { // init my application }); The second method is to enable automated cache expiration through global config. Setting the enableCacheExpiration property to true will enable the timer. Optionally you can set the interval at which the cache is checked via the cacheExpirationIntervalMilliseconds property, by default 750 milliseconds is used. We enforce a minimum of 300 milliseconds as this functionality is enabled via setTimeout and there is little value in having an excessive number of cache checks. This method is more appropriate for a single page application where the page is infrequently reloaded and many cached operations are performed. There is no advantage to enabling cache expiration unless you are experiencing cache storage space pressure in a long running page - and you may see a performance hit due to the use of setTimeout. import { setup } from \"@pnp/core\"; setup({ enableCacheExpiration: true, cacheExpirationIntervalMilliseconds: 1000, // optional });","title":"storage"},{"location":"common/storage/#pnpcorestorage","text":"This module provides a thin wrapper over the browser storage options, local and session. If neither option is available it shims storage with a non-persistent in memory polyfill. Optionally through configuration you can activate expiration. Sample usage is shown below.","title":"@pnp/core/storage"},{"location":"common/storage/#pnpclientstorage","text":"The main export of this module, contains properties representing local and session storage. import { PnPClientStorage } from \"@pnp/core\"; const storage = new PnPClientStorage(); const myvalue = storage.local.get(\"mykey\");","title":"PnPClientStorage"},{"location":"common/storage/#pnpclientstoragewrapper","text":"Each of the storage locations (session and local) are wrapped with this helper class. You can use it directly, but generally it would be used from an instance of PnPClientStorage as shown below. These examples all use local storage, the operations are identical for session storage. import { PnPClientStorage } from \"@pnp/core\"; const storage = new PnPClientStorage(); // get a value from storage const value = storage.local.get(\"mykey\"); // put a value into storage storage.local.put(\"mykey2\", \"my value\"); // put a value into storage with an expiration storage.local.put(\"mykey2\", \"my value\", new Date()); // put a simple object into storage // because JSON.stringify is used to package the object we do NOT do a deep rehydration of stored objects storage.local.put(\"mykey3\", { key: \"value\", key2: \"value2\", }); // remove a value from storage storage.local.delete(\"mykey3\"); // get an item or add it if it does not exist // returns a promise in case you need time to get the value for storage // optionally takes a third parameter specifying the expiration storage.local.getOrPut(\"mykey4\", () => { return Promise.resolve(\"value\"); }); // delete expired items storage.local.deleteExpired();","title":"PnPClientStorageWrapper"},{"location":"common/storage/#cache-expiration","text":"The ability remove of expired items based on a configured timeout can help if the cache is filling up. This can be accomplished in two ways. The first is to explicitly call the new deleteExpired method on the cache you wish to clear. A suggested usage is to add this into your page init code as clearing expired items once per page load is likely sufficient. import { PnPClientStorage } from \"@pnp/core\"; const storage = new PnPClientStorage(); // session storage storage.session.deleteExpired(); // local storage storage.local.deleteExpired(); // this returns a promise, so you can perform some activity after the expired items are removed: storage.local.deleteExpired().then(_ => { // init my application }); The second method is to enable automated cache expiration through global config. Setting the enableCacheExpiration property to true will enable the timer. Optionally you can set the interval at which the cache is checked via the cacheExpirationIntervalMilliseconds property, by default 750 milliseconds is used. We enforce a minimum of 300 milliseconds as this functionality is enabled via setTimeout and there is little value in having an excessive number of cache checks. This method is more appropriate for a single page application where the page is infrequently reloaded and many cached operations are performed. There is no advantage to enabling cache expiration unless you are experiencing cache storage space pressure in a long running page - and you may see a performance hit due to the use of setTimeout. import { setup } from \"@pnp/core\"; setup({ enableCacheExpiration: true, cacheExpirationIntervalMilliseconds: 1000, // optional });","title":"Cache Expiration"},{"location":"common/util/","text":"@pnp/core/util \u00b6 This module contains utility methods that you can import individually from the common library. import { getRandomString, } from \"@pnp/core\"; // use from individually imported method console.log(getRandomString(10)); assign \u00b6 Merges a source object's own enumerable properties into a single target object. Similar to Object.assign, but allows control of overwriting of existing properties. import { assign } from \"@pnp/core\"; let obj1 = { prop: 1, prop2: 2, }; const obj2 = { prop: 4, prop3: 9, }; const example1 = assign(obj1, obj2); // example1 = { prop: 4, prop2: 2, prop3: 9 } //noOverwrite = true stops overwriting existing properties const example2 = assign(obj1, obj2, true); // example2 = { prop: 1, prop2: 2, prop3: 9 } combine \u00b6 Combines any number of paths, normalizing the slashes as required import { combine } from \"@pnp/core\"; // \"https://microsoft.com/something/more\" const paths = combine(\"https://microsoft.com\", \"something\", \"more\"); // \"also/works/with/relative\" const paths2 = combine(\"/also/\", \"/works\", \"with/\", \"/relative\\\\\"); dateAdd \u00b6 Manipulates a date, please see the Stack Overflow discussion from where this method was taken. import { dateAdd } from \"@pnp/core\"; const testDate = new Date(); dateAdd(testDate,'minute',10); getCtxCallback \u00b6 Gets a callback function which will maintain context across async calls. import { getCtxCallback } from \"@pnp/core\"; const contextThis = { myProp: 6, }; function theFunction() { // \"this\" within this function will be the context object supplied // in this case the variable contextThis, so myProp will exist return this.myProp; } const callback = getCtxCallback(contextThis, theFunction); callback(); // returns 6 // You can also supply additional parameters if needed function theFunction2(g: number) { // \"this\" within this function will be the context object supplied // in this case the variable contextThis, so myProp will exist return this.myProp + g; } const callback2 = getCtxCallback(contextThis, theFunction2, 4); callback2(); // returns 10 (6 + 4) getGUID \u00b6 Creates a random guid, please see the Stack Overflow discussion from where this method was taken. import { getGUID } from \"@pnp/core\"; const newGUID = getGUID(); getRandomString \u00b6 Gets a random string consisting of the number of characters requested. import { getRandomString } from \"@pnp/core\"; const randomString = getRandomString(10); hOP \u00b6 Shortcut for Object.hasOwnProperty. Determines if an object has a specified property. import { HttpRequestError } from \"@pnp/queryable\"; import { hOP } from \"@pnp/core\"; export async function handleError(e: Error | HttpRequestError): Promise { //Checks to see if the error object has a property called isHttpRequestError. Returns a bool. if (hOP(e, \"isHttpRequestError\")) { // Handle this type or error } else { // not an HttpRequestError so we do something else } } isArray \u00b6 Determines if a supplied variable represents an array. import { isArray } from \"@pnp/core\"; let x:String[] = [1,2,3]]; if (isArray(x)){ console.log(\"I am an array\"); }else{ console.log(\"I am not an array\"); } isFunc \u00b6 Determines if a supplied variable represents a function. import { isFunc } from \"@pnp/core\"; public testFunction() { console.log(\"test function\"); return } if (isFunc(testFunction)){ console.log(\"this is a function\"); testFunction(); } isUrlAbsolute \u00b6 Determines if a supplied url is absolute and returns true; otherwise returns false. import { isUrlAbsolute } from \"@pnp/core\"; const webPath = 'https://{tenant}.sharepoint.com/sites/dev/'; if (isUrlAbsolute(webPath)){ console.log(\"URL is absolute\"); }else{ console.log(\"URL is not absolute\"); } objectDefinedNotNull \u00b6 Determines if an object is defined and not null. import { objectDefinedNotNull } from \"@pnp/core\"; let obj = { prop: 1 }; if (objectDefinedNotNull(obj)){ console.log(\"Not null\"); }else{ console.log(\"Null\"); } stringIsNullOrEmpty \u00b6 Determines if a supplied string is null or empty. import { stringIsNullOrEmpty } from \"@pnp/core\"; let x:String = \"hello\"; if (stringIsNullOrEmpty(x)){ console.log(\"Null or empty\"); }else{ console.log(\"Not null or empty\"); } Removed \u00b6 Some methods that were no longer used internally by the @pnp libraries have been removed. You can find the source for those methods below for use in your projects should you require. /** * Loads a stylesheet into the current page * * @param path The url to the stylesheet * @param avoidCache If true a value will be appended as a query string to avoid browser caching issues */ public static loadStylesheet(path: string, avoidCache: boolean): void { if (avoidCache) { path += \"?\" + encodeURIComponent((new Date()).getTime().toString()); } const head = document.getElementsByTagName(\"head\"); if (head.length > 0) { const e = document.createElement(\"link\"); head[0].appendChild(e); e.setAttribute(\"type\", \"text/css\"); e.setAttribute(\"rel\", \"stylesheet\"); e.setAttribute(\"href\", path); } } /** * Tests if a url param exists * * @param name The name of the url parameter to check */ public static urlParamExists(name: string): boolean { name = name.replace(/[\\[]/, \"\\\\[\").replace(/[\\]]/, \"\\\\]\"); const regex = new RegExp(\"[\\\\?&]\" + name + \"=([^&#]*)\"); return regex.test(location.search); } /** * Gets a url param value by name * * @param name The name of the parameter for which we want the value */ public static getUrlParamByName(name: string): string { name = name.replace(/[\\[]/, \"\\\\[\").replace(/[\\]]/, \"\\\\]\"); const regex = new RegExp(\"[\\\\?&]\" + name + \"=([^&#]*)\"); const results = regex.exec(location.search); return results == null ? \"\" : decodeURIComponent(results[1].replace(/\\+/g, \" \")); } /** * Gets a url param by name and attempts to parse a bool value * * @param name The name of the parameter for which we want the boolean value */ public static getUrlParamBoolByName(name: string): boolean { const p = this.getUrlParamByName(name); const isFalse = (p === \"\" || /false|0/i.test(p)); return !isFalse; } /** * Inserts the string s into the string target as the index specified by index * * @param target The string into which we will insert s * @param index The location in target to insert s (zero based) * @param s The string to insert into target at position index */ public static stringInsert(target: string, index: number, s: string): string { if (index > 0) { return target.substring(0, index) + s + target.substring(index, target.length); } return s + target; }","title":"util"},{"location":"common/util/#pnpcoreutil","text":"This module contains utility methods that you can import individually from the common library. import { getRandomString, } from \"@pnp/core\"; // use from individually imported method console.log(getRandomString(10));","title":"@pnp/core/util"},{"location":"common/util/#assign","text":"Merges a source object's own enumerable properties into a single target object. Similar to Object.assign, but allows control of overwriting of existing properties. import { assign } from \"@pnp/core\"; let obj1 = { prop: 1, prop2: 2, }; const obj2 = { prop: 4, prop3: 9, }; const example1 = assign(obj1, obj2); // example1 = { prop: 4, prop2: 2, prop3: 9 } //noOverwrite = true stops overwriting existing properties const example2 = assign(obj1, obj2, true); // example2 = { prop: 1, prop2: 2, prop3: 9 }","title":"assign"},{"location":"common/util/#combine","text":"Combines any number of paths, normalizing the slashes as required import { combine } from \"@pnp/core\"; // \"https://microsoft.com/something/more\" const paths = combine(\"https://microsoft.com\", \"something\", \"more\"); // \"also/works/with/relative\" const paths2 = combine(\"/also/\", \"/works\", \"with/\", \"/relative\\\\\");","title":"combine"},{"location":"common/util/#dateadd","text":"Manipulates a date, please see the Stack Overflow discussion from where this method was taken. import { dateAdd } from \"@pnp/core\"; const testDate = new Date(); dateAdd(testDate,'minute',10);","title":"dateAdd"},{"location":"common/util/#getctxcallback","text":"Gets a callback function which will maintain context across async calls. import { getCtxCallback } from \"@pnp/core\"; const contextThis = { myProp: 6, }; function theFunction() { // \"this\" within this function will be the context object supplied // in this case the variable contextThis, so myProp will exist return this.myProp; } const callback = getCtxCallback(contextThis, theFunction); callback(); // returns 6 // You can also supply additional parameters if needed function theFunction2(g: number) { // \"this\" within this function will be the context object supplied // in this case the variable contextThis, so myProp will exist return this.myProp + g; } const callback2 = getCtxCallback(contextThis, theFunction2, 4); callback2(); // returns 10 (6 + 4)","title":"getCtxCallback"},{"location":"common/util/#getguid","text":"Creates a random guid, please see the Stack Overflow discussion from where this method was taken. import { getGUID } from \"@pnp/core\"; const newGUID = getGUID();","title":"getGUID"},{"location":"common/util/#getrandomstring","text":"Gets a random string consisting of the number of characters requested. import { getRandomString } from \"@pnp/core\"; const randomString = getRandomString(10);","title":"getRandomString"},{"location":"common/util/#hop","text":"Shortcut for Object.hasOwnProperty. Determines if an object has a specified property. import { HttpRequestError } from \"@pnp/queryable\"; import { hOP } from \"@pnp/core\"; export async function handleError(e: Error | HttpRequestError): Promise { //Checks to see if the error object has a property called isHttpRequestError. Returns a bool. if (hOP(e, \"isHttpRequestError\")) { // Handle this type or error } else { // not an HttpRequestError so we do something else } }","title":"hOP"},{"location":"common/util/#isarray","text":"Determines if a supplied variable represents an array. import { isArray } from \"@pnp/core\"; let x:String[] = [1,2,3]]; if (isArray(x)){ console.log(\"I am an array\"); }else{ console.log(\"I am not an array\"); }","title":"isArray"},{"location":"common/util/#isfunc","text":"Determines if a supplied variable represents a function. import { isFunc } from \"@pnp/core\"; public testFunction() { console.log(\"test function\"); return } if (isFunc(testFunction)){ console.log(\"this is a function\"); testFunction(); }","title":"isFunc"},{"location":"common/util/#isurlabsolute","text":"Determines if a supplied url is absolute and returns true; otherwise returns false. import { isUrlAbsolute } from \"@pnp/core\"; const webPath = 'https://{tenant}.sharepoint.com/sites/dev/'; if (isUrlAbsolute(webPath)){ console.log(\"URL is absolute\"); }else{ console.log(\"URL is not absolute\"); }","title":"isUrlAbsolute"},{"location":"common/util/#objectdefinednotnull","text":"Determines if an object is defined and not null. import { objectDefinedNotNull } from \"@pnp/core\"; let obj = { prop: 1 }; if (objectDefinedNotNull(obj)){ console.log(\"Not null\"); }else{ console.log(\"Null\"); }","title":"objectDefinedNotNull"},{"location":"common/util/#stringisnullorempty","text":"Determines if a supplied string is null or empty. import { stringIsNullOrEmpty } from \"@pnp/core\"; let x:String = \"hello\"; if (stringIsNullOrEmpty(x)){ console.log(\"Null or empty\"); }else{ console.log(\"Not null or empty\"); }","title":"stringIsNullOrEmpty"},{"location":"common/util/#removed","text":"Some methods that were no longer used internally by the @pnp libraries have been removed. You can find the source for those methods below for use in your projects should you require. /** * Loads a stylesheet into the current page * * @param path The url to the stylesheet * @param avoidCache If true a value will be appended as a query string to avoid browser caching issues */ public static loadStylesheet(path: string, avoidCache: boolean): void { if (avoidCache) { path += \"?\" + encodeURIComponent((new Date()).getTime().toString()); } const head = document.getElementsByTagName(\"head\"); if (head.length > 0) { const e = document.createElement(\"link\"); head[0].appendChild(e); e.setAttribute(\"type\", \"text/css\"); e.setAttribute(\"rel\", \"stylesheet\"); e.setAttribute(\"href\", path); } } /** * Tests if a url param exists * * @param name The name of the url parameter to check */ public static urlParamExists(name: string): boolean { name = name.replace(/[\\[]/, \"\\\\[\").replace(/[\\]]/, \"\\\\]\"); const regex = new RegExp(\"[\\\\?&]\" + name + \"=([^&#]*)\"); return regex.test(location.search); } /** * Gets a url param value by name * * @param name The name of the parameter for which we want the value */ public static getUrlParamByName(name: string): string { name = name.replace(/[\\[]/, \"\\\\[\").replace(/[\\]]/, \"\\\\]\"); const regex = new RegExp(\"[\\\\?&]\" + name + \"=([^&#]*)\"); const results = regex.exec(location.search); return results == null ? \"\" : decodeURIComponent(results[1].replace(/\\+/g, \" \")); } /** * Gets a url param by name and attempts to parse a bool value * * @param name The name of the parameter for which we want the boolean value */ public static getUrlParamBoolByName(name: string): boolean { const p = this.getUrlParamByName(name); const isFalse = (p === \"\" || /false|0/i.test(p)); return !isFalse; } /** * Inserts the string s into the string target as the index specified by index * * @param target The string into which we will insert s * @param index The location in target to insert s (zero based) * @param s The string to insert into target at position index */ public static stringInsert(target: string, index: number, s: string): string { if (index > 0) { return target.substring(0, index) + s + target.substring(index, target.length); } return s + target; }","title":"Removed"},{"location":"concepts/configuration/","text":"PnPjs Configuration \u00b6 This article describes the configuration architecture used by the library as well as the settings available. Starting with version 2.1.0 we updated our configuration design to support the ability to isolate settings to individual objects. The first part of this article discusses the newer design, you can read about the pre v2.1.0 configuration further down. Post v2.1.0 \u00b6 Architecture \u00b6 Starting from v2.1.0 we have modified our configuration design to allow for configuring individual queryable objects. Backward Compatibility \u00b6 If you have no need to use the isolated runtimes introduced in 2.1.0 then you should see no change in library behavior from prior versions. You can continue to refer to the pre v2.1.0 configuration section - and if you see any issues please let us know. All of the available settings as described below remain, unchanged. If you previously used our internal configuration classes directly RuntimeConfigImpl, SPRuntimeConfigImpl, or GraphRuntimeConfigImpl they no longer exist. We do not consider this a breaking change as they were meant to be internal and their direct use was not documented. This includes the concrete default instances RuntimeConfig, SPRuntimeConfig, and GraphRuntimeConfig. Isolated Runtimes \u00b6 You can create an isolated runtime when using either the sp or graph libraries. What this does is create an isolated set of properties and behaviors specific to a given fluent chain. Have a look at this basic example below: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // create an isolated sp root instance const isolatedSP = await sp.createIsolated(); // this configuration applies to all objects created from \"sp\" sp.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/\", }, }); // this configuration applies to all objects created from \"isolatedSP\" isolatedSP.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/sites/dev\", }, }); // details for the web at https://mytenant.sharepoint.com/ const web1 = await sp.web(); // details for the web at https://mytenant.sharepoint.com/sites/dev const web2 = await isolatedSP.web(); This configuration is supplied to all objects down a given fluent chain: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // create an isolated sp root instance const isolatedSP = await sp.createIsolated(); // this configuraiton applies to all objects created from \"sp\" sp.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/\", }, }); // this configuraiton applies to all objects created from \"isolatedSP\" isolatedSP.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/sites/dev\", }, }); // details for the lists at https://mytenant.sharepoint.com/ const lists1 = await sp.web.lists(); // details for the lists at https://mytenant.sharepoint.com/sites/dev const lists2 = await isolatedSP.web.lists(); createIsolated \u00b6 The createIsolated method is used to establish the isolated runtime for a given instance of either the sp or graph libraries. Once created it is no longer connected to the default instance and if you have common settings that must be updated you would need to update them across each isolated instance, this is by design. Currently sp and graph createIsolated methods accept the same init, but we have broken them out to make thing clear. All properties of the init object are optional. Any properties provided will overwrite those cloned from the default if cloneGlobal is true. If cloneGlobal is false you start with an empty config containing only the core defaults . sp.createIsolated \u00b6 import { sp, ISPConfiguration } from \"@pnp/sp\"; // accept all the defaults, will clone any settings from sp const isolatedSP = await sp.createIsolated(); // - specify all the config options, using the ISPConfiguration interface to type the config // - setting baseUrl in the root is equivelent to setting it with sp: { baseUrl: }, it is provided as a shortcut as this seemed to be a common use case // - if you set them both the baseUrl in the root will be used. // - you can set some or all of the settings in config and if you clone from the global the ones you specify will overwrite the cloned values // - for example your global config can specify everything and your isolated config could specify a different fetchClientFactory, see node example below const isolatedSP = await sp.createIsolated({ baseUrl: \"https://mytenant.sharepoint.com\", cloneGlobal: false, config: { cacheExpirationIntervalMilliseconds: 1000, sp: { baseUrl: \"https://mytenant.sharepoint.com\", fetchClientFactory: () => void(0), headers: { \"X-AnotherHeader\": \"54321\", }, }, spfxContext: this.context, // only valid within SPFx }, options: { headers: { \"X-SomeHeader\": \"12345\", }, }, }); Defaults Name Default baseUrl \"\" cloneGlobal true config {} options {} graph.createIsolated \u00b6 import { graph, IGraphConfiguration } from \"@pnp/graph\"; // - specify all the config options, using the IGraphConfiguration interface to type the config // - setting baseUrl in the root is restricted to \"v1.0\" or \"beta\". If you need to specify a different absolute url should use config.graph.baseUrl // - in practice you should use one or the other. You can always swap Graph api version using IGraphQueryable.setEndpoint // - you can set some or all of the settings in config and if you clone from the global the ones you specify will overwrite the cloned values // - for example your global config can specify everything and your isolated config could specify a different fetchClientFactory, see node example below const isolatedGraph = await graph.createIsolated({ baseUrl: \"v1.0\", cloneGlobal: false, config: { cacheExpirationIntervalMilliseconds: 1000, graph: { baseUrl: \"https://graph.microsoft.com\", fetchClientFactory: () => void(0), headers: { \"X-AnotherHeader\": \"54321\", }, }, spfxContext: this.context, // only valid within SPFx }, options: { headers: { \"X-SomeHeader\": \"12345\", }, }, }); Defaults \u00b6 name Default baseUrl \"v1.0\" cloneGlobal true config {} options {} Additional Examples \u00b6 MSAL with Node multiple site requests \u00b6 MSAL Support Added in 2.0.11 In this example you can see how you can setup the MSAL client once and then set a different baseUrl for an isolated instance. More information specific to setting up the MSAL client is available . import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { readFileSync } from \"fs\"; // read in our private key const buffer = readFileSync(\"c:/temp/key.pem\"); // configure node options sp.setup({ sp: { baseUrl: \"https://{my tenant}.sharepoint.com/sites/dev/\", fetchClientFactory: () => { return new MsalFetchClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant id or common}\", clientCertificate: { thumbprint: \"{certificate thumbprint, displayed in AAD}\", privateKey: buffer.toString(), }, clientId: \"{client id}\", } }, [\"https://{my tenant}.sharepoint.com/.default\"]); // you must set the scope for SharePoint access }, }, }); const isolatedSP = await sp.createIsolated({ config: { sp: { baseUrl: \"https://{my tenant}.sharepoint.com/sites/dev2/\", }, }, }); Node multiple site requests \u00b6 Isolated configuration was most requested for scenarios in node where you need to access information in multiple sites. This example shows setting up the global configuration and then creating an isolated config with only the baseUrl updated. import { SPFetchClient } from \"@pnp/nodejs\"; import { ISPConfigurationPart, sp } from \"@pnp/sp\"; sp.setup({ cacheExpirationIntervalMilliseconds: 1000, defaultCachingStore: \"local\", sp: { fetchClientFactory: () => { return new SPFetchClient(\"https://mytenant.sharepoint.com/\", \"id\", \"secret\"); }, headers: { \"X-MyRequiredHeader\": \"SomeValue\", \"X-MyRequiredHeader2\": \"SomeValue\", }, }, }); const isolatedSP = await sp.createIsolated({ config: { sp: { fetchClientFactory: () => { return new SPFetchClient(\"https://mytenant.sharepoint.com/site/dev\", \"id\", \"secret\"); }, }, }, }); Batching \u00b6 All batching functionality works as expected, but you must take care to only associate requests from the same isolated instance as you create the batch. Mixing requests across isolation boundaries is not supported. This applies to sp and graph batching. sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"url1\", \"id\", \"secret\"); }, }, }); const isolated = await sp.createIsolated({ config: { sp: { fetchClientFactory: () => { return new SPFetchClient(\"url2\", \"id\", \"secret\"); }, }, }, }); const batch1 = sp.createBatch(); sp.web.lists.select(\"Title\").top(3).inBatch(batch1)().then(r => console.log(`here 1: ${JSON.stringify(r, null, 2)}`)); sp.web.select(\"Title\").inBatch(batch1)().then(r => console.log(`here 2: ${JSON.stringify(r, null, 2)}`)); await batch1.execute(); const batch2 = isolated.createBatch(); isolated.web.lists.select(\"Title\").top(3).inBatch(batch2)().then(r => console.log(`here 3: ${JSON.stringify(r, null, 2)}`)); isolated.web.select(\"Title\").inBatch(batch2)().then(r => console.log(`here 4: ${JSON.stringify(r, null, 2)}`)); await batch2.execute(); IE11 Mode \u00b6 The IE11 mode setting is always global. There is no scenario we care to support where once instance needs to run in ie11 mode and another does not. Your code either does or does not run in ie11. Prior to v2.1.0 \u00b6 Architecture \u00b6 PnPjs uses an additive configuration design with multiple libraries sharing a single global configuration instance. If you need non-global configuration please see this section . There are three ways to access the setup functionality - through either the common, sp, or graph library's setup method. While the configuration is global the various methods have different typing on their input parameter. You can review the libconfig article for more details on storing your own configuration. Common Configuration \u00b6 The common libary's setup method takes parameters defined by ILibraryConfiguration . The properties and their defaults are listed below, followed by a code sample. You can call setup multiple times and any new values will be added to the existing configuration or replace the previous value if one existed. All values are optional. Name Description Default defaultCachingStore Where will PnPjs store cached data by default (session or local) session defaultCachingTimeoutSeconds The global default value used for cached data timeouts in seconds 60 globalCacheDisable Provides a way to globally within PnPjs disable all caching false enableCacheExpiration If true a timeout expired items will be removed from the cache in intervals determined by cacheTimeoutInterval false cacheExpirationIntervalMilliseconds Determines the interval in milliseconds at which the cache is checked to see if items have expired (min: 100) 750 spfxContext When running in SPFx the current context should always be supplied to PnPjs when available null ie11 If true the library downgrades functionality to work in IE11 false For more information on setting up in SPFx please see the authentication section For more details on ie11 mode please see the topic article import { setup } from \"@pnp/core\"; // called before other code setup({ cacheExpirationIntervalMilliseconds: 15000, defaultCachingStore: \"local\", defaultCachingTimeoutSeconds: 600, enableCacheExpiration: true, globalCacheDisable: false, ie11: false, spfxContext: this.context, // if in SPFx, otherwise leave it out }); SP Configuration \u00b6 The sp library's configuration is defined by the ISPConfiguration interface which extends ILibraryConfiguration. All of the sp values are contained in a top level property named \"sp\". The following table describes the properties with a code sample following. All values are optional. Name Description Default headers Allows you to apply any headers to all calls made by the sp library none baseUrl Allows you to define a base site url for all requests, takes precedence over all other url logic. Must be absolute. none fetchClientFactory Allows you to specify a factory function used to produce IHttpClientImpl instances none There are many examples of using fetchClientFactory available in the authentication section . import { sp } from \"@pnp/sp\"; import { SPFxAdalClient } from \"@pnp/core\"; // note you can still set the global configuration such as ie11 using the same object as // the interface extends ILibraryConfiguration sp.setup({ ie11: false, sp: { baseUrl: \"https://tenant.sharepoint.com/sites/dev\", fetchClientFactory: () => { return new SPFxAdalClient(this.context); }, headers: { \"Accept\": \"application/json;odata=verbose\", \"X-Something\": \"header-value\", }, }, spfxContext: this.context, }); SharePoint Framework \u00b6 You can optionally supply only the SPFx context to the sp configure method. import { sp } from \"@pnp/sp\"; // in SPFx only sp.setup(this.context); Graph Configuration \u00b6 The graph configuration works exactly the same as the sp configuration but is defined by the IGraphConfiguration interface which extends ILibraryConfiguration. All of the graph values are contained in a top level property named \"graph\". The following table describes the properties with a code sample following. All values are optional. Name Description Default headers Allows you to apply any headers to all calls made by the sp library none baseUrl Allows you to define a base site url for all requests, takes precedence over all other url logic. Must be absolute. ( Added in 2.0.8 ) none fetchClientFactory Allows you to specify a factory function used to produce IHttpClientImpl instances none There are many examples of using fetchClientFactory available in the authentication section . import { graph } from \"@pnp/graph\"; import { MsalClientSetup } from \"@pnp/msaljsclient\"; // note you can still set the global configuration such as ie11 using the same object as // the interface extends ILibraryConfiguration graph.setup({ ie11: false, graph: { // we set the GCC url baseUrl: \"https://graph.microsoft.us\", fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/tenant.onmicrosoft.com\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://tenant.sharepoint.com/sites/dev/SitePages/test.aspx\", }, }, [\"Group.Read.All\"]), headers: { \"Accept\": \"application/json;odata=verbose\", \"X-Something\": \"header-value\", }, }, spfxContext: this.context, }); SharePoint Framework \u00b6 You can optionally supply only the SPFx context to the graph configure method. We will attempt to set the baseUrl property from the context - but if that is failing in your environment and you need to call a special cloud (i.e. graph.microsoft.us) please set the baseUrl property. import { graph } from \"@pnp/graph\"; // in SPFx only graph.setup(this.context); Configure Everything At Once \u00b6 In some cases you might want to configure everything in one go. Because the configuration is stored in a single location you can use the common library's setup method and adjust the typings to ensure you are using the correct property names while only having to setup things with a single call. In versions before 2.0.8 ISPConfigurationPart, IGraphConfigurationPart, and ILibraryConfiguration incorrectly were missing the \"I\" prefix. That was fixed in 2.0.8 - but note if you are using an older version of the library you'll need to use the old names. Everything else in the below example works as expected. import { ISPConfigurationPart } from \"@pnp/sp\"; import { IGraphConfigurationPart } from \"@pnp/graph\"; import { ILibraryConfiguration, setup } from \"@pnp/core\"; // you could also include your custom configuration parts export interface AllConfig extends ILibraryConfiguration, ISPConfigurationPart, IGraphConfigurationPart { } // create a single big configuration entry const config: AllConfig = { graph: { baseUrl: \"https://graph.microsoft.us\", }, ie11: false, sp: { baseUrl: \"https://tenant.sharepoint.com/sites/dev\", }, }; setup(config);","title":"Configuration"},{"location":"concepts/configuration/#pnpjs-configuration","text":"This article describes the configuration architecture used by the library as well as the settings available. Starting with version 2.1.0 we updated our configuration design to support the ability to isolate settings to individual objects. The first part of this article discusses the newer design, you can read about the pre v2.1.0 configuration further down.","title":"PnPjs Configuration"},{"location":"concepts/configuration/#post-v210","text":"","title":"Post v2.1.0"},{"location":"concepts/configuration/#architecture","text":"Starting from v2.1.0 we have modified our configuration design to allow for configuring individual queryable objects.","title":"Architecture"},{"location":"concepts/configuration/#backward-compatibility","text":"If you have no need to use the isolated runtimes introduced in 2.1.0 then you should see no change in library behavior from prior versions. You can continue to refer to the pre v2.1.0 configuration section - and if you see any issues please let us know. All of the available settings as described below remain, unchanged. If you previously used our internal configuration classes directly RuntimeConfigImpl, SPRuntimeConfigImpl, or GraphRuntimeConfigImpl they no longer exist. We do not consider this a breaking change as they were meant to be internal and their direct use was not documented. This includes the concrete default instances RuntimeConfig, SPRuntimeConfig, and GraphRuntimeConfig.","title":"Backward Compatibility"},{"location":"concepts/configuration/#isolated-runtimes","text":"You can create an isolated runtime when using either the sp or graph libraries. What this does is create an isolated set of properties and behaviors specific to a given fluent chain. Have a look at this basic example below: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // create an isolated sp root instance const isolatedSP = await sp.createIsolated(); // this configuration applies to all objects created from \"sp\" sp.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/\", }, }); // this configuration applies to all objects created from \"isolatedSP\" isolatedSP.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/sites/dev\", }, }); // details for the web at https://mytenant.sharepoint.com/ const web1 = await sp.web(); // details for the web at https://mytenant.sharepoint.com/sites/dev const web2 = await isolatedSP.web(); This configuration is supplied to all objects down a given fluent chain: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // create an isolated sp root instance const isolatedSP = await sp.createIsolated(); // this configuraiton applies to all objects created from \"sp\" sp.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/\", }, }); // this configuraiton applies to all objects created from \"isolatedSP\" isolatedSP.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/sites/dev\", }, }); // details for the lists at https://mytenant.sharepoint.com/ const lists1 = await sp.web.lists(); // details for the lists at https://mytenant.sharepoint.com/sites/dev const lists2 = await isolatedSP.web.lists();","title":"Isolated Runtimes"},{"location":"concepts/configuration/#createisolated","text":"The createIsolated method is used to establish the isolated runtime for a given instance of either the sp or graph libraries. Once created it is no longer connected to the default instance and if you have common settings that must be updated you would need to update them across each isolated instance, this is by design. Currently sp and graph createIsolated methods accept the same init, but we have broken them out to make thing clear. All properties of the init object are optional. Any properties provided will overwrite those cloned from the default if cloneGlobal is true. If cloneGlobal is false you start with an empty config containing only the core defaults .","title":"createIsolated"},{"location":"concepts/configuration/#spcreateisolated","text":"import { sp, ISPConfiguration } from \"@pnp/sp\"; // accept all the defaults, will clone any settings from sp const isolatedSP = await sp.createIsolated(); // - specify all the config options, using the ISPConfiguration interface to type the config // - setting baseUrl in the root is equivelent to setting it with sp: { baseUrl: }, it is provided as a shortcut as this seemed to be a common use case // - if you set them both the baseUrl in the root will be used. // - you can set some or all of the settings in config and if you clone from the global the ones you specify will overwrite the cloned values // - for example your global config can specify everything and your isolated config could specify a different fetchClientFactory, see node example below const isolatedSP = await sp.createIsolated({ baseUrl: \"https://mytenant.sharepoint.com\", cloneGlobal: false, config: { cacheExpirationIntervalMilliseconds: 1000, sp: { baseUrl: \"https://mytenant.sharepoint.com\", fetchClientFactory: () => void(0), headers: { \"X-AnotherHeader\": \"54321\", }, }, spfxContext: this.context, // only valid within SPFx }, options: { headers: { \"X-SomeHeader\": \"12345\", }, }, }); Defaults Name Default baseUrl \"\" cloneGlobal true config {} options {}","title":"sp.createIsolated"},{"location":"concepts/configuration/#graphcreateisolated","text":"import { graph, IGraphConfiguration } from \"@pnp/graph\"; // - specify all the config options, using the IGraphConfiguration interface to type the config // - setting baseUrl in the root is restricted to \"v1.0\" or \"beta\". If you need to specify a different absolute url should use config.graph.baseUrl // - in practice you should use one or the other. You can always swap Graph api version using IGraphQueryable.setEndpoint // - you can set some or all of the settings in config and if you clone from the global the ones you specify will overwrite the cloned values // - for example your global config can specify everything and your isolated config could specify a different fetchClientFactory, see node example below const isolatedGraph = await graph.createIsolated({ baseUrl: \"v1.0\", cloneGlobal: false, config: { cacheExpirationIntervalMilliseconds: 1000, graph: { baseUrl: \"https://graph.microsoft.com\", fetchClientFactory: () => void(0), headers: { \"X-AnotherHeader\": \"54321\", }, }, spfxContext: this.context, // only valid within SPFx }, options: { headers: { \"X-SomeHeader\": \"12345\", }, }, });","title":"graph.createIsolated"},{"location":"concepts/configuration/#defaults","text":"name Default baseUrl \"v1.0\" cloneGlobal true config {} options {}","title":"Defaults"},{"location":"concepts/configuration/#additional-examples","text":"","title":"Additional Examples"},{"location":"concepts/configuration/#msal-with-node-multiple-site-requests","text":"MSAL Support Added in 2.0.11 In this example you can see how you can setup the MSAL client once and then set a different baseUrl for an isolated instance. More information specific to setting up the MSAL client is available . import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { readFileSync } from \"fs\"; // read in our private key const buffer = readFileSync(\"c:/temp/key.pem\"); // configure node options sp.setup({ sp: { baseUrl: \"https://{my tenant}.sharepoint.com/sites/dev/\", fetchClientFactory: () => { return new MsalFetchClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant id or common}\", clientCertificate: { thumbprint: \"{certificate thumbprint, displayed in AAD}\", privateKey: buffer.toString(), }, clientId: \"{client id}\", } }, [\"https://{my tenant}.sharepoint.com/.default\"]); // you must set the scope for SharePoint access }, }, }); const isolatedSP = await sp.createIsolated({ config: { sp: { baseUrl: \"https://{my tenant}.sharepoint.com/sites/dev2/\", }, }, });","title":"MSAL with Node multiple site requests"},{"location":"concepts/configuration/#node-multiple-site-requests","text":"Isolated configuration was most requested for scenarios in node where you need to access information in multiple sites. This example shows setting up the global configuration and then creating an isolated config with only the baseUrl updated. import { SPFetchClient } from \"@pnp/nodejs\"; import { ISPConfigurationPart, sp } from \"@pnp/sp\"; sp.setup({ cacheExpirationIntervalMilliseconds: 1000, defaultCachingStore: \"local\", sp: { fetchClientFactory: () => { return new SPFetchClient(\"https://mytenant.sharepoint.com/\", \"id\", \"secret\"); }, headers: { \"X-MyRequiredHeader\": \"SomeValue\", \"X-MyRequiredHeader2\": \"SomeValue\", }, }, }); const isolatedSP = await sp.createIsolated({ config: { sp: { fetchClientFactory: () => { return new SPFetchClient(\"https://mytenant.sharepoint.com/site/dev\", \"id\", \"secret\"); }, }, }, });","title":"Node multiple site requests"},{"location":"concepts/configuration/#batching","text":"All batching functionality works as expected, but you must take care to only associate requests from the same isolated instance as you create the batch. Mixing requests across isolation boundaries is not supported. This applies to sp and graph batching. sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"url1\", \"id\", \"secret\"); }, }, }); const isolated = await sp.createIsolated({ config: { sp: { fetchClientFactory: () => { return new SPFetchClient(\"url2\", \"id\", \"secret\"); }, }, }, }); const batch1 = sp.createBatch(); sp.web.lists.select(\"Title\").top(3).inBatch(batch1)().then(r => console.log(`here 1: ${JSON.stringify(r, null, 2)}`)); sp.web.select(\"Title\").inBatch(batch1)().then(r => console.log(`here 2: ${JSON.stringify(r, null, 2)}`)); await batch1.execute(); const batch2 = isolated.createBatch(); isolated.web.lists.select(\"Title\").top(3).inBatch(batch2)().then(r => console.log(`here 3: ${JSON.stringify(r, null, 2)}`)); isolated.web.select(\"Title\").inBatch(batch2)().then(r => console.log(`here 4: ${JSON.stringify(r, null, 2)}`)); await batch2.execute();","title":"Batching"},{"location":"concepts/configuration/#ie11-mode","text":"The IE11 mode setting is always global. There is no scenario we care to support where once instance needs to run in ie11 mode and another does not. Your code either does or does not run in ie11.","title":"IE11 Mode"},{"location":"concepts/configuration/#prior-to-v210","text":"","title":"Prior to v2.1.0"},{"location":"concepts/configuration/#architecture_1","text":"PnPjs uses an additive configuration design with multiple libraries sharing a single global configuration instance. If you need non-global configuration please see this section . There are three ways to access the setup functionality - through either the common, sp, or graph library's setup method. While the configuration is global the various methods have different typing on their input parameter. You can review the libconfig article for more details on storing your own configuration.","title":"Architecture"},{"location":"concepts/configuration/#common-configuration","text":"The common libary's setup method takes parameters defined by ILibraryConfiguration . The properties and their defaults are listed below, followed by a code sample. You can call setup multiple times and any new values will be added to the existing configuration or replace the previous value if one existed. All values are optional. Name Description Default defaultCachingStore Where will PnPjs store cached data by default (session or local) session defaultCachingTimeoutSeconds The global default value used for cached data timeouts in seconds 60 globalCacheDisable Provides a way to globally within PnPjs disable all caching false enableCacheExpiration If true a timeout expired items will be removed from the cache in intervals determined by cacheTimeoutInterval false cacheExpirationIntervalMilliseconds Determines the interval in milliseconds at which the cache is checked to see if items have expired (min: 100) 750 spfxContext When running in SPFx the current context should always be supplied to PnPjs when available null ie11 If true the library downgrades functionality to work in IE11 false For more information on setting up in SPFx please see the authentication section For more details on ie11 mode please see the topic article import { setup } from \"@pnp/core\"; // called before other code setup({ cacheExpirationIntervalMilliseconds: 15000, defaultCachingStore: \"local\", defaultCachingTimeoutSeconds: 600, enableCacheExpiration: true, globalCacheDisable: false, ie11: false, spfxContext: this.context, // if in SPFx, otherwise leave it out });","title":"Common Configuration"},{"location":"concepts/configuration/#sp-configuration","text":"The sp library's configuration is defined by the ISPConfiguration interface which extends ILibraryConfiguration. All of the sp values are contained in a top level property named \"sp\". The following table describes the properties with a code sample following. All values are optional. Name Description Default headers Allows you to apply any headers to all calls made by the sp library none baseUrl Allows you to define a base site url for all requests, takes precedence over all other url logic. Must be absolute. none fetchClientFactory Allows you to specify a factory function used to produce IHttpClientImpl instances none There are many examples of using fetchClientFactory available in the authentication section . import { sp } from \"@pnp/sp\"; import { SPFxAdalClient } from \"@pnp/core\"; // note you can still set the global configuration such as ie11 using the same object as // the interface extends ILibraryConfiguration sp.setup({ ie11: false, sp: { baseUrl: \"https://tenant.sharepoint.com/sites/dev\", fetchClientFactory: () => { return new SPFxAdalClient(this.context); }, headers: { \"Accept\": \"application/json;odata=verbose\", \"X-Something\": \"header-value\", }, }, spfxContext: this.context, });","title":"SP Configuration"},{"location":"concepts/configuration/#sharepoint-framework","text":"You can optionally supply only the SPFx context to the sp configure method. import { sp } from \"@pnp/sp\"; // in SPFx only sp.setup(this.context);","title":"SharePoint Framework"},{"location":"concepts/configuration/#graph-configuration","text":"The graph configuration works exactly the same as the sp configuration but is defined by the IGraphConfiguration interface which extends ILibraryConfiguration. All of the graph values are contained in a top level property named \"graph\". The following table describes the properties with a code sample following. All values are optional. Name Description Default headers Allows you to apply any headers to all calls made by the sp library none baseUrl Allows you to define a base site url for all requests, takes precedence over all other url logic. Must be absolute. ( Added in 2.0.8 ) none fetchClientFactory Allows you to specify a factory function used to produce IHttpClientImpl instances none There are many examples of using fetchClientFactory available in the authentication section . import { graph } from \"@pnp/graph\"; import { MsalClientSetup } from \"@pnp/msaljsclient\"; // note you can still set the global configuration such as ie11 using the same object as // the interface extends ILibraryConfiguration graph.setup({ ie11: false, graph: { // we set the GCC url baseUrl: \"https://graph.microsoft.us\", fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/tenant.onmicrosoft.com\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://tenant.sharepoint.com/sites/dev/SitePages/test.aspx\", }, }, [\"Group.Read.All\"]), headers: { \"Accept\": \"application/json;odata=verbose\", \"X-Something\": \"header-value\", }, }, spfxContext: this.context, });","title":"Graph Configuration"},{"location":"concepts/configuration/#sharepoint-framework_1","text":"You can optionally supply only the SPFx context to the graph configure method. We will attempt to set the baseUrl property from the context - but if that is failing in your environment and you need to call a special cloud (i.e. graph.microsoft.us) please set the baseUrl property. import { graph } from \"@pnp/graph\"; // in SPFx only graph.setup(this.context);","title":"SharePoint Framework"},{"location":"concepts/configuration/#configure-everything-at-once","text":"In some cases you might want to configure everything in one go. Because the configuration is stored in a single location you can use the common library's setup method and adjust the typings to ensure you are using the correct property names while only having to setup things with a single call. In versions before 2.0.8 ISPConfigurationPart, IGraphConfigurationPart, and ILibraryConfiguration incorrectly were missing the \"I\" prefix. That was fixed in 2.0.8 - but note if you are using an older version of the library you'll need to use the old names. Everything else in the below example works as expected. import { ISPConfigurationPart } from \"@pnp/sp\"; import { IGraphConfigurationPart } from \"@pnp/graph\"; import { ILibraryConfiguration, setup } from \"@pnp/core\"; // you could also include your custom configuration parts export interface AllConfig extends ILibraryConfiguration, ISPConfigurationPart, IGraphConfigurationPart { } // create a single big configuration entry const config: AllConfig = { graph: { baseUrl: \"https://graph.microsoft.us\", }, ie11: false, sp: { baseUrl: \"https://tenant.sharepoint.com/sites/dev\", }, }; setup(config);","title":"Configure Everything At Once"},{"location":"concepts/custom-bundle/","text":"Custom Bundling \u00b6 With the introduction of selective imports it is now possible to create your own bundle to exactly fit your needs. This provides much greater control over how your solutions are deployed and what is included in your bundles. Scenarios could include: Deploying a company-wide PnPjs custom bundle shared by all your components so it only needs to be downloaded once. Creating SPFx libraries either for one project or a single webpart. Create a single library containing the PnPjs code you need bundled along with your custom extensions . Create a custom bundle \u00b6 Webpack \u00b6 You can see/clone a sample project of this example here . Rollup \u00b6 You can see/clone a sample project of this example here .","title":"Custom Bundle"},{"location":"concepts/custom-bundle/#custom-bundling","text":"With the introduction of selective imports it is now possible to create your own bundle to exactly fit your needs. This provides much greater control over how your solutions are deployed and what is included in your bundles. Scenarios could include: Deploying a company-wide PnPjs custom bundle shared by all your components so it only needs to be downloaded once. Creating SPFx libraries either for one project or a single webpart. Create a single library containing the PnPjs code you need bundled along with your custom extensions .","title":"Custom Bundling"},{"location":"concepts/custom-bundle/#create-a-custom-bundle","text":"","title":"Create a custom bundle"},{"location":"concepts/custom-bundle/#webpack","text":"You can see/clone a sample project of this example here .","title":"Webpack"},{"location":"concepts/custom-bundle/#rollup","text":"You can see/clone a sample project of this example here .","title":"Rollup"},{"location":"concepts/error-handling/","text":"Error Handling \u00b6 This article describes the most common types of errors generated by the library. It provides context on the error object, and ways to handle the errors. As always you should tailor your error handling to what your application needs. These are ideas that can be applied to many different patterns. For 429, 503, and 504 errors we include retry logic within the library The HttpRequestError \u00b6 All errors resulting from executed web requests will be returned as an HttpRequestError object which extends the base Error . In addition to the standard Error properties it has some other properties to help you figure out what went wrong. We used a custom error to attempt to normalize what can be a wide assortment of http related errors, while also seeking to provide as much information to library consumers as possible. Property Name Description name Standard Error.name property. Always 'Error' message Normalized string containing the status, status text, and the full response text stack The callstack producing the error isHttpRequestError Always true, allows you to reliably determine if you have an HttpRequestError instance response Unread copy of the Response object resulting in the thrown error status The Response.status value (such as 404) statusText The Response.statusText value (such as 'Not Found') Basic Handling \u00b6 For all operations involving a web request you should account for the possibility they might fail. That failure might be transient or permanent - you won't know until they happen \ud83d\ude09. The most basic type of error handling involves a simple try-catch. import { sp } from \"@pnp/sp/presets/all\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { console.error(e); } This will produce output like: Error making HttpClient request in queryable [404] Not Found ::> {\"odata.error\":{\"code\":\"-1, System.ArgumentException\",\"message\":{\"lang\":\"en-US\",\"value\":\"List 'no' does not exist at site with URL 'https://tenant.sharepoint.com/sites/dev'.\"}}} Data: {\"response\":{\"size\":0,\"timeout\":0},\"status\":404,\"statusText\":\"Not Found\",\"isHttpRequestError\":true} This is very descriptive and provides full details as to what happened, but you might want to handle things a little more cleanly. Reading the Response \u00b6 In some cases the response body will have additional details such as a localized error messages which can be nicer to display rather than our normalized string. You can read the response directly and process it however you desire: import { sp } from \"@pnp/sp/presets/all\"; import { HttpRequestError } from \"@pnp/queryable\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { // are we dealing with an HttpRequestError? if (e?.isHttpRequestError) { // we can read the json from the response const json = await (e).response.json(); // if we have a value property we can show it console.log(typeof json[\"odata.error\"] === \"object\" ? json[\"odata.error\"].message.value : e.message); // add of course you have access to the other properties and can make choices on how to act if ((e).status === 404) { console.error((e).statusText); // maybe create the resource, or redirect, or fallback to a secondary data source // just ideas, handle any of the status codes uniquely as needed } } else { // not an HttpRequestError so we just log message console.log(e.message); } } Logging errors \u00b6 Using the PnPjs Logging Framework you can directly pass the error object and the normalized message will be logged. These techniques can be applied to any logging framework. import { Logger } from \"@pnp/logging\"; import { sp } from \"@pnp/sp/presets/all\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { Logger.error(e); } You may want to read the response and customize the message as described above: import { Logger } from \"@pnp/logging\"; import { sp } from \"@pnp/sp/presets/all\"; import { HttpRequestError } from \"@pnp/queryable\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { if (e?.isHttpRequestError) { // we can read the json from the response const data = await (e).response.json(); // parse this however you want const message = typeof data[\"odata.error\"] === \"object\" ? data[\"odata.error\"].message.value : e.message; // we use the status to determine a custom logging level const level: LogLevel = (e).status === 404 ? LogLevel.Warning : LogLevel.Info; // create a custom log entry Logger.log({ data, level, message, }); } else { // not an HttpRequestError so we just log message Logger.error(e); } } Putting it All Together \u00b6 After reviewing the above section you might have thought it seems like a lot of work to include all that logic for every error. One approach is to establish a single function you use application wide to process errors. This allows all the error handling logic to be easily updated and consistent across the application. errorhandler.ts \u00b6 import { Logger } from \"@pnp/logging\"; import { HttpRequestError } from \"@pnp/queryable\"; import { hOP } from \"@pnp/core\"; export async function handleError(e: Error | HttpRequestError): Promise { if (hOP(e, \"isHttpRequestError\")) { // we can read the json from the response const data = await (e).response.json(); // parse this however you want const message = typeof data[\"odata.error\"] === \"object\" ? data[\"odata.error\"].message.value : e.message; // we use the status to determine a custom logging level const level: LogLevel = (e).status === 404 ? LogLevel.Warning : LogLevel.Info; // create a custom log entry Logger.log({ data, level, message, }); } else { // not an HttpRequestError so we just log message Logger.error(e); } } web-request.ts \u00b6 import { sp } from \"@pnp/sp/presets/all\"; import { handleError } from \"./errorhandler\"; try { const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { await handleError(e); } web-request2.ts \u00b6 import { sp } from \"@pnp/sp/presets/all\"; import { handleError } from \"./errorhandler\"; try { const w = await sp.web.lists(); } catch (e) { await handleError(e); }","title":"Error Handling"},{"location":"concepts/error-handling/#error-handling","text":"This article describes the most common types of errors generated by the library. It provides context on the error object, and ways to handle the errors. As always you should tailor your error handling to what your application needs. These are ideas that can be applied to many different patterns. For 429, 503, and 504 errors we include retry logic within the library","title":"Error Handling"},{"location":"concepts/error-handling/#the-httprequesterror","text":"All errors resulting from executed web requests will be returned as an HttpRequestError object which extends the base Error . In addition to the standard Error properties it has some other properties to help you figure out what went wrong. We used a custom error to attempt to normalize what can be a wide assortment of http related errors, while also seeking to provide as much information to library consumers as possible. Property Name Description name Standard Error.name property. Always 'Error' message Normalized string containing the status, status text, and the full response text stack The callstack producing the error isHttpRequestError Always true, allows you to reliably determine if you have an HttpRequestError instance response Unread copy of the Response object resulting in the thrown error status The Response.status value (such as 404) statusText The Response.statusText value (such as 'Not Found')","title":"The HttpRequestError"},{"location":"concepts/error-handling/#basic-handling","text":"For all operations involving a web request you should account for the possibility they might fail. That failure might be transient or permanent - you won't know until they happen \ud83d\ude09. The most basic type of error handling involves a simple try-catch. import { sp } from \"@pnp/sp/presets/all\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { console.error(e); } This will produce output like: Error making HttpClient request in queryable [404] Not Found ::> {\"odata.error\":{\"code\":\"-1, System.ArgumentException\",\"message\":{\"lang\":\"en-US\",\"value\":\"List 'no' does not exist at site with URL 'https://tenant.sharepoint.com/sites/dev'.\"}}} Data: {\"response\":{\"size\":0,\"timeout\":0},\"status\":404,\"statusText\":\"Not Found\",\"isHttpRequestError\":true} This is very descriptive and provides full details as to what happened, but you might want to handle things a little more cleanly.","title":"Basic Handling"},{"location":"concepts/error-handling/#reading-the-response","text":"In some cases the response body will have additional details such as a localized error messages which can be nicer to display rather than our normalized string. You can read the response directly and process it however you desire: import { sp } from \"@pnp/sp/presets/all\"; import { HttpRequestError } from \"@pnp/queryable\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { // are we dealing with an HttpRequestError? if (e?.isHttpRequestError) { // we can read the json from the response const json = await (e).response.json(); // if we have a value property we can show it console.log(typeof json[\"odata.error\"] === \"object\" ? json[\"odata.error\"].message.value : e.message); // add of course you have access to the other properties and can make choices on how to act if ((e).status === 404) { console.error((e).statusText); // maybe create the resource, or redirect, or fallback to a secondary data source // just ideas, handle any of the status codes uniquely as needed } } else { // not an HttpRequestError so we just log message console.log(e.message); } }","title":"Reading the Response"},{"location":"concepts/error-handling/#logging-errors","text":"Using the PnPjs Logging Framework you can directly pass the error object and the normalized message will be logged. These techniques can be applied to any logging framework. import { Logger } from \"@pnp/logging\"; import { sp } from \"@pnp/sp/presets/all\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { Logger.error(e); } You may want to read the response and customize the message as described above: import { Logger } from \"@pnp/logging\"; import { sp } from \"@pnp/sp/presets/all\"; import { HttpRequestError } from \"@pnp/queryable\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { if (e?.isHttpRequestError) { // we can read the json from the response const data = await (e).response.json(); // parse this however you want const message = typeof data[\"odata.error\"] === \"object\" ? data[\"odata.error\"].message.value : e.message; // we use the status to determine a custom logging level const level: LogLevel = (e).status === 404 ? LogLevel.Warning : LogLevel.Info; // create a custom log entry Logger.log({ data, level, message, }); } else { // not an HttpRequestError so we just log message Logger.error(e); } }","title":"Logging errors"},{"location":"concepts/error-handling/#putting-it-all-together","text":"After reviewing the above section you might have thought it seems like a lot of work to include all that logic for every error. One approach is to establish a single function you use application wide to process errors. This allows all the error handling logic to be easily updated and consistent across the application.","title":"Putting it All Together"},{"location":"concepts/error-handling/#errorhandlerts","text":"import { Logger } from \"@pnp/logging\"; import { HttpRequestError } from \"@pnp/queryable\"; import { hOP } from \"@pnp/core\"; export async function handleError(e: Error | HttpRequestError): Promise { if (hOP(e, \"isHttpRequestError\")) { // we can read the json from the response const data = await (e).response.json(); // parse this however you want const message = typeof data[\"odata.error\"] === \"object\" ? data[\"odata.error\"].message.value : e.message; // we use the status to determine a custom logging level const level: LogLevel = (e).status === 404 ? LogLevel.Warning : LogLevel.Info; // create a custom log entry Logger.log({ data, level, message, }); } else { // not an HttpRequestError so we just log message Logger.error(e); } }","title":"errorhandler.ts"},{"location":"concepts/error-handling/#web-requestts","text":"import { sp } from \"@pnp/sp/presets/all\"; import { handleError } from \"./errorhandler\"; try { const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { await handleError(e); }","title":"web-request.ts"},{"location":"concepts/error-handling/#web-request2ts","text":"import { sp } from \"@pnp/sp/presets/all\"; import { handleError } from \"./errorhandler\"; try { const w = await sp.web.lists(); } catch (e) { await handleError(e); }","title":"web-request2.ts"},{"location":"concepts/ie11-mode/","text":"IE11 Mode \u00b6 Starting with v2 we have made the decision to no longer support IE11. Because we know this affects folks we have introduced IE11 compatibility mode. Configuring the library will allow it to work within IE11, however at a possibly reduced level of functionality depending on your use case. Please see the list below of known limitations. Limitations \u00b6 When required to use IE11 mode there is certain functionality which may not work correctly or at all. Unavailable: Extension Methods Unavailable: OData Debugging Configure IE11 Mode \u00b6 To enable IE11 Mode set the ie11 flag to true in the setup object. Optionally, supply the context object when working in SharePoint Framework . import { sp } from \"@pnp/sp\"; sp.setup({ // set ie 11 mode ie11: true, // only needed when working within SharePoint Framework spfxContext: this.context }); If you are supporting IE 11, please see the article on required polyfills . A note on ie11 mode and support \u00b6 Because IE11 is no longer a primary supported browser our policy moving forward will be doing our best not to break anything in ie11 mode, but not all features will work and new features may never come to ie11 mode. Also, if you find an ie11 bug we expect you to work with us on helping to fix it. If you aren't willing to invest some time to support an old browser it seems we shouldn't either.","title":"IE11 Mode"},{"location":"concepts/ie11-mode/#ie11-mode","text":"Starting with v2 we have made the decision to no longer support IE11. Because we know this affects folks we have introduced IE11 compatibility mode. Configuring the library will allow it to work within IE11, however at a possibly reduced level of functionality depending on your use case. Please see the list below of known limitations.","title":"IE11 Mode"},{"location":"concepts/ie11-mode/#limitations","text":"When required to use IE11 mode there is certain functionality which may not work correctly or at all. Unavailable: Extension Methods Unavailable: OData Debugging","title":"Limitations"},{"location":"concepts/ie11-mode/#configure-ie11-mode","text":"To enable IE11 Mode set the ie11 flag to true in the setup object. Optionally, supply the context object when working in SharePoint Framework . import { sp } from \"@pnp/sp\"; sp.setup({ // set ie 11 mode ie11: true, // only needed when working within SharePoint Framework spfxContext: this.context }); If you are supporting IE 11, please see the article on required polyfills .","title":"Configure IE11 Mode"},{"location":"concepts/ie11-mode/#a-note-on-ie11-mode-and-support","text":"Because IE11 is no longer a primary supported browser our policy moving forward will be doing our best not to break anything in ie11 mode, but not all features will work and new features may never come to ie11 mode. Also, if you find an ie11 bug we expect you to work with us on helping to fix it. If you aren't willing to invest some time to support an old browser it seems we shouldn't either.","title":"A note on ie11 mode and support"},{"location":"concepts/invokable/","text":"Invokables \u00b6 For people who have been using the library since the early days you are familiar with the need to use the () method to invoke a method chain: // an example of get const lists = await sp.web.lists(); Starting with v2 this is no longer required, you can invoke the object directly to execute the default action for that class - typically a get. const lists = await sp.web.lists(); This has two main benefits for people using the library: you can write less code, and we now have a way to model default actions for objects that might do something other than a get. The way we designed the library prior to v2 hid the post, put, delete operations as protected methods attached to the Queryable classes. Without diving into why we did this, having a rethink seemed appropriate for v2. Based on that, the entire queryable chain is now invokable as well for any of the operations. Other Operations (post, put, delete) \u00b6 import { sp, spPost } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // do a post to a web - just an example doesn't do anything fancy spPost(sp.web); Things get a little more interesting in that you can now do posts (or any of the operations) to any of the urls defined by a fluent chain. Meaning you can easily implement methods that are not yet part of the library. For this example I have made up a method called \"MagicFieldCreationMethod\" that doesn't exist. Imagine it was just added to the SharePoint API and we do not yet have support for it. You can now write code like so: import { sp, spPost, SharePointQueryable } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/fields/web\"; // call our made up example method spPost(SharePointQueryable(sp.web.fields, \"MagicFieldCreationMethod\"), { body: JSON.stringify({ // ... this would be the post body }), });","title":"Invokables"},{"location":"concepts/invokable/#invokables","text":"For people who have been using the library since the early days you are familiar with the need to use the () method to invoke a method chain: // an example of get const lists = await sp.web.lists(); Starting with v2 this is no longer required, you can invoke the object directly to execute the default action for that class - typically a get. const lists = await sp.web.lists(); This has two main benefits for people using the library: you can write less code, and we now have a way to model default actions for objects that might do something other than a get. The way we designed the library prior to v2 hid the post, put, delete operations as protected methods attached to the Queryable classes. Without diving into why we did this, having a rethink seemed appropriate for v2. Based on that, the entire queryable chain is now invokable as well for any of the operations.","title":"Invokables"},{"location":"concepts/invokable/#other-operations-post-put-delete","text":"import { sp, spPost } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // do a post to a web - just an example doesn't do anything fancy spPost(sp.web); Things get a little more interesting in that you can now do posts (or any of the operations) to any of the urls defined by a fluent chain. Meaning you can easily implement methods that are not yet part of the library. For this example I have made up a method called \"MagicFieldCreationMethod\" that doesn't exist. Imagine it was just added to the SharePoint API and we do not yet have support for it. You can now write code like so: import { sp, spPost, SharePointQueryable } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/fields/web\"; // call our made up example method spPost(SharePointQueryable(sp.web.fields, \"MagicFieldCreationMethod\"), { body: JSON.stringify({ // ... this would be the post body }), });","title":"Other Operations (post, put, delete)"},{"location":"concepts/polyfill/","text":"Polyfills \u00b6 These libraries may make use of some features not found in older browsers. This primarily affects Internet Explorer 11, which requires that we provide this missing functionality. If you are supporting IE11 enable IE11 mode . IE 11 Polyfill package \u00b6 We created a package you try and help provide this missing functionality. This package is independent of the other @pnp/* packages and does not need to be updated monthly unless we introduce additional polyfills and publish a new version. This package is only needed if you are required to support IE 11. Install \u00b6 npm install @pnp/polyfill-ie11 --save Use \u00b6 import \"@pnp/polyfill-ie11\"; import { sp } from \"@pnp/sp/presets/all\"; sp.web.lists.getByTitle(\"BigList\").items.filter(`ID gt 6000`)().then(r => { this.domElement.innerHTML += r.map(l => `${l.Title}
      `); }); Selective Use \u00b6 Starting with version 2.0.2 you can selectively include the polyfills from the package. Depending on your needs it may make sense in your application to use the underlying libraries directly. We have added an expanded statement on our polyfills . // individually include polyfills as needed to match your requirements import \"@pnp/polyfill-ie11/dist/fetch\"; import \"@pnp/polyfill-ie11/dist/fill\"; import \"@pnp/polyfill-ie11/dist/from\"; import \"@pnp/polyfill-ie11/dist/iterator\"; import \"@pnp/polyfill-ie11/dist/map\"; import \"@pnp/polyfill-ie11/dist/promise\"; import \"@pnp/polyfill-ie11/dist/reflect\"; import \"@pnp/polyfill-ie11/dist/symbol\"; // works in IE11 and other browsers sp.web.lists.getByTitle(\"BigList\").items.filter(`ID gt 6000`)().then(r => { this.domElement.innerHTML += r.map(l => `${l.Title}
      `); }); SearchQueryBuilder \u00b6 Because the latest version of SearchQueryBuilder uses Proxy internally you can fall back on the older version as shown below. import \"@pnp/polyfill-ie11\"; import { SearchQueryBuilder } from \"@pnp/polyfill-ie11/dist/searchquerybuilder\"; import { sp, ISearchQueryBuilder } from \"@pnp/sp/presets/all\"; // works in IE11 and other browsers const builder: ISearchQueryBuilder = SearchQueryBuilder().text(\"test\"); sp.search(builder).then(r => { this.domElement.innerHTML = JSON.stringify(r); }); General Statement on Polyfills \u00b6 Internet Explorer 11 (IE11) has been an enterprise standard browser for many years. Given the complexity in changing technical platforms in many organizations, it is no surprise standardization on this out-of-date browser continues. Unfortunately, for those organizations, the Internet has moved on and many - if not all - SaaS platforms are embracing modern standards and no longer supporting the legacy IE11 browser. Even Microsoft states in their official documentation that Microsoft 365 is best experienced with a modern browser. They have even gone so far to build the latest version of Microsoft Edge based on Chromium (Edge Chromium), with an \"Internet Explorer mode\" allowing organizations to load legacy sites which require IE automatically. PnPjs is now \"modern\" as well, and by that we mean we have moved to using capabilities of current browsers and JavaScript which are not present in IE11. We understand as a developer your ability to require an organization to switch browsers is unrealistic. We want to do everything we can to support you, but it is up to you to ensure your application is properly supported in IE11. There are many polyfills available, depending on the platform you're running on, the frameworks you are using, and the libraries you consume. Although the majority of PnPjs users build for SharePoint Online, a significant number build for earlier versions of the platform as well as for their own node-based solutions or websites. Unfortunately, there is no way our polyfill library can support all these scenarios. What we intended with the @pnp/polyfill-ie11 package was to provide a comprehensive group of all the polyfills that would be needed based on the complete PnPjs library. We are finding when we aggregate our polyfills with the polyfills provided in the SharePoint page and from other sources, things don't always work well. We cannot solve this for your specific situations except by providing you transparency into the polyfills which we know are necessary for our packages. You may need to adjust what polyfills your application uses based on the other libraries you are using. To that end, we want to provide the list of polyfills we recommend here - along with the associated packages \u2013 with the goal of helping you to work out what combination of polyfills might work with your code. Also, if you haven't reviewed it yet, please check out the information on IE11 Mode for how to configure IE11 mode in the sp.setup as well as what limitations doing so will have on your usage of PnPjs. imports import \"core-js/stable/array/from\"; import \"core-js/stable/array/fill\"; import \"core-js/stable/array/iterator\"; import \"core-js/stable/promise\"; import \"core-js/stable/reflect\"; import \"es6-map/implement\"; import \"core-js/stable/symbol\"; import \"whatwg-fetch\"; The following NPM packages are what we use to do the above indicated imports |package| |---| | core-js | | es6-map | | whatwg-fetch |","title":"Polyfills"},{"location":"concepts/polyfill/#polyfills","text":"These libraries may make use of some features not found in older browsers. This primarily affects Internet Explorer 11, which requires that we provide this missing functionality. If you are supporting IE11 enable IE11 mode .","title":"Polyfills"},{"location":"concepts/polyfill/#ie-11-polyfill-package","text":"We created a package you try and help provide this missing functionality. This package is independent of the other @pnp/* packages and does not need to be updated monthly unless we introduce additional polyfills and publish a new version. This package is only needed if you are required to support IE 11.","title":"IE 11 Polyfill package"},{"location":"concepts/polyfill/#install","text":"npm install @pnp/polyfill-ie11 --save","title":"Install"},{"location":"concepts/polyfill/#use","text":"import \"@pnp/polyfill-ie11\"; import { sp } from \"@pnp/sp/presets/all\"; sp.web.lists.getByTitle(\"BigList\").items.filter(`ID gt 6000`)().then(r => { this.domElement.innerHTML += r.map(l => `${l.Title}
      `); });","title":"Use"},{"location":"concepts/polyfill/#selective-use","text":"Starting with version 2.0.2 you can selectively include the polyfills from the package. Depending on your needs it may make sense in your application to use the underlying libraries directly. We have added an expanded statement on our polyfills . // individually include polyfills as needed to match your requirements import \"@pnp/polyfill-ie11/dist/fetch\"; import \"@pnp/polyfill-ie11/dist/fill\"; import \"@pnp/polyfill-ie11/dist/from\"; import \"@pnp/polyfill-ie11/dist/iterator\"; import \"@pnp/polyfill-ie11/dist/map\"; import \"@pnp/polyfill-ie11/dist/promise\"; import \"@pnp/polyfill-ie11/dist/reflect\"; import \"@pnp/polyfill-ie11/dist/symbol\"; // works in IE11 and other browsers sp.web.lists.getByTitle(\"BigList\").items.filter(`ID gt 6000`)().then(r => { this.domElement.innerHTML += r.map(l => `${l.Title}
      `); });","title":"Selective Use"},{"location":"concepts/polyfill/#searchquerybuilder","text":"Because the latest version of SearchQueryBuilder uses Proxy internally you can fall back on the older version as shown below. import \"@pnp/polyfill-ie11\"; import { SearchQueryBuilder } from \"@pnp/polyfill-ie11/dist/searchquerybuilder\"; import { sp, ISearchQueryBuilder } from \"@pnp/sp/presets/all\"; // works in IE11 and other browsers const builder: ISearchQueryBuilder = SearchQueryBuilder().text(\"test\"); sp.search(builder).then(r => { this.domElement.innerHTML = JSON.stringify(r); });","title":"SearchQueryBuilder"},{"location":"concepts/polyfill/#general-statement-on-polyfills","text":"Internet Explorer 11 (IE11) has been an enterprise standard browser for many years. Given the complexity in changing technical platforms in many organizations, it is no surprise standardization on this out-of-date browser continues. Unfortunately, for those organizations, the Internet has moved on and many - if not all - SaaS platforms are embracing modern standards and no longer supporting the legacy IE11 browser. Even Microsoft states in their official documentation that Microsoft 365 is best experienced with a modern browser. They have even gone so far to build the latest version of Microsoft Edge based on Chromium (Edge Chromium), with an \"Internet Explorer mode\" allowing organizations to load legacy sites which require IE automatically. PnPjs is now \"modern\" as well, and by that we mean we have moved to using capabilities of current browsers and JavaScript which are not present in IE11. We understand as a developer your ability to require an organization to switch browsers is unrealistic. We want to do everything we can to support you, but it is up to you to ensure your application is properly supported in IE11. There are many polyfills available, depending on the platform you're running on, the frameworks you are using, and the libraries you consume. Although the majority of PnPjs users build for SharePoint Online, a significant number build for earlier versions of the platform as well as for their own node-based solutions or websites. Unfortunately, there is no way our polyfill library can support all these scenarios. What we intended with the @pnp/polyfill-ie11 package was to provide a comprehensive group of all the polyfills that would be needed based on the complete PnPjs library. We are finding when we aggregate our polyfills with the polyfills provided in the SharePoint page and from other sources, things don't always work well. We cannot solve this for your specific situations except by providing you transparency into the polyfills which we know are necessary for our packages. You may need to adjust what polyfills your application uses based on the other libraries you are using. To that end, we want to provide the list of polyfills we recommend here - along with the associated packages \u2013 with the goal of helping you to work out what combination of polyfills might work with your code. Also, if you haven't reviewed it yet, please check out the information on IE11 Mode for how to configure IE11 mode in the sp.setup as well as what limitations doing so will have on your usage of PnPjs. imports import \"core-js/stable/array/from\"; import \"core-js/stable/array/fill\"; import \"core-js/stable/array/iterator\"; import \"core-js/stable/promise\"; import \"core-js/stable/reflect\"; import \"es6-map/implement\"; import \"core-js/stable/symbol\"; import \"whatwg-fetch\"; The following NPM packages are what we use to do the above indicated imports |package| |---| | core-js | | es6-map | | whatwg-fetch |","title":"General Statement on Polyfills"},{"location":"concepts/selective-imports/","text":"Selective Imports \u00b6 As the libraries have grown to support more of the SharePoint and Graph API they have also grown in size. On one hand this is good as more functionality becomes available but you had to include lots of code you didn't use if you were only doing simple operations. To solve this we introduced selective imports in v2. This allows you to only import the parts of the sp or graph library you need, allowing you to greatly reduce your overall solution bundle size - and enables treeshaking . This concept works well with custom bundling to create a shared package tailored exactly to your needs. If you would prefer to not worry about selective imports please see the section on presets . Old way \u00b6 // the sp var came with all library functionality already attached // meaning treeshaking couldn't reduce the size import { sp } from \"@pnp/sp\"; const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)(); New Way \u00b6 // the sp var now has almost nothing attached at import time and relies on import { sp } from \"@pnp/sp\"; // we need to import each of the pieces we need to \"attach\" them for chaining // here we are importing the specific sub modules we need and attaching the functionality for lists to web and items to list import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items/list\"; const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)(); Above we are being very specific in what we are importing, but you can also import entire sub-modules and be slightly less specific // the sp var now has almost nothing attached at import time and relies on import { sp } from \"@pnp/sp\"; // we need to import each of the pieces we need to \"attach\" them for chaining // here we are importing the specific sub modules we need and attaching the functionality for lists to web and items to list import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)(); The above two examples both work just fine but you may end up with slightly smaller bundle sizes using the first. Consider this example: // this import statement will attach content-type functionality to list, web, and item import \"@pnp/sp/content-types\"; // this import statement will only attach content-type functionality to web import \"@pnp/sp/content-types/web\"; If you only need to access content types on the web object you can reduce size by only importing that piece. // this will fail import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IList } from \"@pnp/sp/lists\"; // do this instead import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { IList } from \"@pnp/sp/lists\"; const lists = await sp.web.lists(); Presets \u00b6 Sometimes you don't care as much about bundle size - testing or node development for example. In these cases we have provided what we are calling presets to allow you to skip importing each module individually. SP \u00b6 For the sp library there are two presets \"all\" and \"core\". The all preset mimics the behavior in v1 and includes everything in the library already attached to the sp var. import { sp } from \"@pnp/sp/presets/all\"; // sp.* exists as it did in v1, tree shaking will not work const lists = await sp.web.lists(); The \"core\" preset includes sites, webs, lists, and items. import { sp } from \"@pnp/sp/presets/core\"; // sp.* exists as it did in v1, tree shaking will not work const lists = await sp.web.lists(); Graph \u00b6 The graph library contains a single preset, \"all\" mimicking the v1 structure. import { graph } from \"@pnp/graph/presets/all\"; // graph.* exists as it did in v1, tree shaking will not work While we may look to add additional presets in the future you are encouraged to look at making your own custom bundles as a preferred solution.","title":"Selective Imports"},{"location":"concepts/selective-imports/#selective-imports","text":"As the libraries have grown to support more of the SharePoint and Graph API they have also grown in size. On one hand this is good as more functionality becomes available but you had to include lots of code you didn't use if you were only doing simple operations. To solve this we introduced selective imports in v2. This allows you to only import the parts of the sp or graph library you need, allowing you to greatly reduce your overall solution bundle size - and enables treeshaking . This concept works well with custom bundling to create a shared package tailored exactly to your needs. If you would prefer to not worry about selective imports please see the section on presets .","title":"Selective Imports"},{"location":"concepts/selective-imports/#old-way","text":"// the sp var came with all library functionality already attached // meaning treeshaking couldn't reduce the size import { sp } from \"@pnp/sp\"; const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)();","title":"Old way"},{"location":"concepts/selective-imports/#new-way","text":"// the sp var now has almost nothing attached at import time and relies on import { sp } from \"@pnp/sp\"; // we need to import each of the pieces we need to \"attach\" them for chaining // here we are importing the specific sub modules we need and attaching the functionality for lists to web and items to list import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items/list\"; const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)(); Above we are being very specific in what we are importing, but you can also import entire sub-modules and be slightly less specific // the sp var now has almost nothing attached at import time and relies on import { sp } from \"@pnp/sp\"; // we need to import each of the pieces we need to \"attach\" them for chaining // here we are importing the specific sub modules we need and attaching the functionality for lists to web and items to list import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)(); The above two examples both work just fine but you may end up with slightly smaller bundle sizes using the first. Consider this example: // this import statement will attach content-type functionality to list, web, and item import \"@pnp/sp/content-types\"; // this import statement will only attach content-type functionality to web import \"@pnp/sp/content-types/web\"; If you only need to access content types on the web object you can reduce size by only importing that piece. // this will fail import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IList } from \"@pnp/sp/lists\"; // do this instead import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { IList } from \"@pnp/sp/lists\"; const lists = await sp.web.lists();","title":"New Way"},{"location":"concepts/selective-imports/#presets","text":"Sometimes you don't care as much about bundle size - testing or node development for example. In these cases we have provided what we are calling presets to allow you to skip importing each module individually.","title":"Presets"},{"location":"concepts/selective-imports/#sp","text":"For the sp library there are two presets \"all\" and \"core\". The all preset mimics the behavior in v1 and includes everything in the library already attached to the sp var. import { sp } from \"@pnp/sp/presets/all\"; // sp.* exists as it did in v1, tree shaking will not work const lists = await sp.web.lists(); The \"core\" preset includes sites, webs, lists, and items. import { sp } from \"@pnp/sp/presets/core\"; // sp.* exists as it did in v1, tree shaking will not work const lists = await sp.web.lists();","title":"SP"},{"location":"concepts/selective-imports/#graph","text":"The graph library contains a single preset, \"all\" mimicking the v1 structure. import { graph } from \"@pnp/graph/presets/all\"; // graph.* exists as it did in v1, tree shaking will not work While we may look to add additional presets in the future you are encouraged to look at making your own custom bundles as a preferred solution.","title":"Graph"},{"location":"concepts/settings/","text":"Project Settings \u00b6 This article discusses creating a project settings file for use in local development and debugging of the libraries. The settings file contains authentication and other settings to enable you to run and debug the project locally. The settings file is a JavaScript file that exports a single object representing the settings of your project. You can view the example settings file in the project root . Settings File Format (>= 2.0.13) \u00b6 Starting with version 2.0.13 we have added support within the settings file for MSAL authentication for both SharePoint and Graph. You are NOT required to update your existing settings file unless you want to use MSAL authentication with a Graph application. The existing id/secret settings continue to work however we recommend updating when you have an opportunity. For more information coinfiguring MSAL please review the section in the authentication section for node . MSAL configuration has two parts, these are the initialization which is passed directly to the MsalFetchClient (and on to the underlying msal-node instance) and the scopes. The scopes are always \"https://{tenant}.sharepoint.com/.default\" or \"https://graph.microsoft.com/.default\" depending on what you are calling. If you are calling Microsoft Graph sovereign or gov clouds the scope may need to be updated. const privateKey = `-----BEGIN RSA PRIVATE KEY----- your private key, read from a file or included here -----END RSA PRIVATE KEY----- `; var msalInit = { auth: { authority: \"https://login.microsoftonline.com/{tenant id}\", clientCertificate: { thumbprint: \"{certificate thumbnail}\", privateKey: privateKey, }, clientId: \"{AAD App registration id}\", } } var settings = { testing: { enableWebTests: true, testUser: \"i:0#.f|membership|user@consto.com\", sp: { url: \"{required for MSAL - absolute url of test site}\", notificationUrl: \"{ optional: notification url }\", msal: { init: msalInit, scopes: [\"https://{tenant}.sharepoint.com/.default\"] }, }, graph: { msal: { init: msalInit, scopes: [\"https://graph.microsoft.com/.default\"] }, }, }, } module.exports = settings; The settings object has a single sub-object testing which contains the configuration used for debugging and testing PnPjs. The parts of this object are described in detail below. enableWebTests Flag to toggle if tests are run against the live services or not. If this is set to false none of the other sections are required. testUser AAD login account to be used when running tests. sp Settings used to configure SharePoint (sp library) debugging and tests graph Settings used to configure Microsoft Graph (graph library) debugging and tests SP values \u00b6 name description url The url of the site to use for all requests. If a site parameter is not specified a child web will be created under the web at this url. See scripts article for more details. notificationUrl Url used when registering test subscriptions msal Information about MSAL authentication setup Graph value \u00b6 The graph values are described in the table below and come from registering an AAD Application . The permissions required by the registered application are dictated by the tests you want to run or resources you wish to test against. name description msal Information about MSAL authentication setup Settings File Format (<= 2.0.12) \u00b6 var settings = { testing: { enableWebTests: true, sp: { id: \"{ client id }\", secret: \"{ client secret }\", url: \"{ site collection url }\", notificationUrl: \"{ optional: notification url }\", }, graph: { tenant: \"{tenant.onmicrosoft.com}\", id: \"{your app id}\", secret: \"{your secret}\" }, } } module.exports = settings; enableWebTests Flag to toggle if tests are run against the live services or not. If this is set to false none of the other sections are required. sp Settings used to configure SharePoint (sp library) debugging and tests graph Settings used to configure Microsoft Graph (graph library) debugging and tests SP values \u00b6 The sp values are described in the table below and come from registering a legacy SharePoint add-in . name description id The client id of the registered application secret The client secret of the registered application url The url of the site to use for all requests. If a site parameter is not specified a child web will be created under the web at this url. See scripts article for more details. notificationUrl Url used when registering test subscriptions Graph values \u00b6 The graph values are described in the table below and come from registering an AAD Application . The permissions required by the registered application are dictated by the tests you want to run or resources you wish to test against. name description tenant Tenant to target for authentication and data (ex: contoso.onmicrosoft.com) id The application id secret The application secret Create Settings.js file \u00b6 Copy the example file and rename it settings.js. Place the file in the root of your project. Update the settings as needed for your environment. If you are only doing SharePoint testing you can leave the graph section off and vice-versa. Also, if you are not testing anything with hooks you can leave off the notificationUrl.","title":"Settings"},{"location":"concepts/settings/#project-settings","text":"This article discusses creating a project settings file for use in local development and debugging of the libraries. The settings file contains authentication and other settings to enable you to run and debug the project locally. The settings file is a JavaScript file that exports a single object representing the settings of your project. You can view the example settings file in the project root .","title":"Project Settings"},{"location":"concepts/settings/#settings-file-format-2013","text":"Starting with version 2.0.13 we have added support within the settings file for MSAL authentication for both SharePoint and Graph. You are NOT required to update your existing settings file unless you want to use MSAL authentication with a Graph application. The existing id/secret settings continue to work however we recommend updating when you have an opportunity. For more information coinfiguring MSAL please review the section in the authentication section for node . MSAL configuration has two parts, these are the initialization which is passed directly to the MsalFetchClient (and on to the underlying msal-node instance) and the scopes. The scopes are always \"https://{tenant}.sharepoint.com/.default\" or \"https://graph.microsoft.com/.default\" depending on what you are calling. If you are calling Microsoft Graph sovereign or gov clouds the scope may need to be updated. const privateKey = `-----BEGIN RSA PRIVATE KEY----- your private key, read from a file or included here -----END RSA PRIVATE KEY----- `; var msalInit = { auth: { authority: \"https://login.microsoftonline.com/{tenant id}\", clientCertificate: { thumbprint: \"{certificate thumbnail}\", privateKey: privateKey, }, clientId: \"{AAD App registration id}\", } } var settings = { testing: { enableWebTests: true, testUser: \"i:0#.f|membership|user@consto.com\", sp: { url: \"{required for MSAL - absolute url of test site}\", notificationUrl: \"{ optional: notification url }\", msal: { init: msalInit, scopes: [\"https://{tenant}.sharepoint.com/.default\"] }, }, graph: { msal: { init: msalInit, scopes: [\"https://graph.microsoft.com/.default\"] }, }, }, } module.exports = settings; The settings object has a single sub-object testing which contains the configuration used for debugging and testing PnPjs. The parts of this object are described in detail below. enableWebTests Flag to toggle if tests are run against the live services or not. If this is set to false none of the other sections are required. testUser AAD login account to be used when running tests. sp Settings used to configure SharePoint (sp library) debugging and tests graph Settings used to configure Microsoft Graph (graph library) debugging and tests","title":"Settings File Format (>= 2.0.13)"},{"location":"concepts/settings/#sp-values","text":"name description url The url of the site to use for all requests. If a site parameter is not specified a child web will be created under the web at this url. See scripts article for more details. notificationUrl Url used when registering test subscriptions msal Information about MSAL authentication setup","title":"SP values"},{"location":"concepts/settings/#graph-value","text":"The graph values are described in the table below and come from registering an AAD Application . The permissions required by the registered application are dictated by the tests you want to run or resources you wish to test against. name description msal Information about MSAL authentication setup","title":"Graph value"},{"location":"concepts/settings/#settings-file-format-2012","text":"var settings = { testing: { enableWebTests: true, sp: { id: \"{ client id }\", secret: \"{ client secret }\", url: \"{ site collection url }\", notificationUrl: \"{ optional: notification url }\", }, graph: { tenant: \"{tenant.onmicrosoft.com}\", id: \"{your app id}\", secret: \"{your secret}\" }, } } module.exports = settings; enableWebTests Flag to toggle if tests are run against the live services or not. If this is set to false none of the other sections are required. sp Settings used to configure SharePoint (sp library) debugging and tests graph Settings used to configure Microsoft Graph (graph library) debugging and tests","title":"Settings File Format (<= 2.0.12)"},{"location":"concepts/settings/#sp-values_1","text":"The sp values are described in the table below and come from registering a legacy SharePoint add-in . name description id The client id of the registered application secret The client secret of the registered application url The url of the site to use for all requests. If a site parameter is not specified a child web will be created under the web at this url. See scripts article for more details. notificationUrl Url used when registering test subscriptions","title":"SP values"},{"location":"concepts/settings/#graph-values","text":"The graph values are described in the table below and come from registering an AAD Application . The permissions required by the registered application are dictated by the tests you want to run or resources you wish to test against. name description tenant Tenant to target for authentication and data (ex: contoso.onmicrosoft.com) id The application id secret The application secret","title":"Graph values"},{"location":"concepts/settings/#create-settingsjs-file","text":"Copy the example file and rename it settings.js. Place the file in the root of your project. Update the settings as needed for your environment. If you are only doing SharePoint testing you can leave the graph section off and vice-versa. Also, if you are not testing anything with hooks you can leave off the notificationUrl.","title":"Create Settings.js file"},{"location":"config-store/","text":"@pnp/config-store \u00b6 This module provides a way to load application configuration from one or more providers and share it across an application in a consistent way. A provider can be anything - but we have included one to load information from a SharePoint list. This library is most helpful for larger applications where a formal configuration model is needed. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/sp @pnp/config-store --save See the topics below for usage: configuration providers","title":"config-store"},{"location":"config-store/#pnpconfig-store","text":"This module provides a way to load application configuration from one or more providers and share it across an application in a consistent way. A provider can be anything - but we have included one to load information from a SharePoint list. This library is most helpful for larger applications where a formal configuration model is needed.","title":"@pnp/config-store"},{"location":"config-store/#getting-started","text":"Install the library and required dependencies npm install @pnp/sp @pnp/config-store --save See the topics below for usage: configuration providers","title":"Getting Started"},{"location":"config-store/configuration/","text":"@pnp/config-store/configuration \u00b6 The main class exported from the config-store package is Settings. This is the class through which you will load and access your settings via providers . import { Web } from \"@pnp/sp/presets/all\"; import { Settings, SPListConfigurationProvider } from \"@pnp/config-store\"; // create an instance of the settings class, could be static and shared across your application // or built as needed. const settings = new Settings(); // you can add/update a single value using add settings.add(\"mykey\", \"myvalue\"); // you can also add/update a JSON value which will be stringified for you as a shorthand settings.addJSON(\"mykey2\", { field: 1, field2: 2, field3: 3, }); // and you can apply a plain object of keys/values that will be written as single values // this results in each enumerable property of the supplied object being added to the settings collection settings.apply({ field: 1, field2: 2, field3: 3, }); // and finally you can load values from a configuration provider const w = Web(\"https://mytenant.sharepoint.com/sites/dev\"); const provider = new SPListConfigurationProvider(w, \"myconfiglistname\"); // this will load values from the supplied list // by default the key will be from the Title field and the value from a column named Value await settings.load(provider); // once we have loaded values we can then read them const value = settings.get(\"mykey\"); // or read JSON that will be parsed for you from the store const value2 = settings.getJSON(\"mykey2\");","title":"configuration"},{"location":"config-store/configuration/#pnpconfig-storeconfiguration","text":"The main class exported from the config-store package is Settings. This is the class through which you will load and access your settings via providers . import { Web } from \"@pnp/sp/presets/all\"; import { Settings, SPListConfigurationProvider } from \"@pnp/config-store\"; // create an instance of the settings class, could be static and shared across your application // or built as needed. const settings = new Settings(); // you can add/update a single value using add settings.add(\"mykey\", \"myvalue\"); // you can also add/update a JSON value which will be stringified for you as a shorthand settings.addJSON(\"mykey2\", { field: 1, field2: 2, field3: 3, }); // and you can apply a plain object of keys/values that will be written as single values // this results in each enumerable property of the supplied object being added to the settings collection settings.apply({ field: 1, field2: 2, field3: 3, }); // and finally you can load values from a configuration provider const w = Web(\"https://mytenant.sharepoint.com/sites/dev\"); const provider = new SPListConfigurationProvider(w, \"myconfiglistname\"); // this will load values from the supplied list // by default the key will be from the Title field and the value from a column named Value await settings.load(provider); // once we have loaded values we can then read them const value = settings.get(\"mykey\"); // or read JSON that will be parsed for you from the store const value2 = settings.getJSON(\"mykey2\");","title":"@pnp/config-store/configuration"},{"location":"config-store/providers/","text":"@pnp/config-store/providers \u00b6 Currently there is a single provider included in the library, but contributions of additional providers are welcome. SPListConfigurationProvider \u00b6 This provider is based on a SharePoint list it reads all of the rows and makes them available as a TypedHash . By default the column names used are Title for key and \"Value\" for value, but you can update these as needed. Additionally, the settings class supports the idea of last value in wins - so you can easily load multiple configurations. This helps to support a common scenario in the enterprise where you might have one main list for global configuration but some settings can be set at the web level. In this case you would first load the global, then the local settings and any local values will take precedence. import { Web } from \"@pnp/sp/presets/all\"; import { Settings, SPListConfigurationProvider } from \"@pnp/config-store\"; // create a new provider instance const w = Web(\"https://mytenant.sharepoint.com/sites/dev\"); const provider = new SPListConfigurationProvider(w, \"myconfiglistname\"); const settings = new Settings(); // load our values from the list await settings.load(provider); CachingConfigurationProvider \u00b6 Because making requests on each page load is very inefficient you can optionally use the caching configuration provider, which wraps a provider and caches the configuration in local or session storage. import { Web } from \"@pnp/sp/presets/all\"; import { Settings, SPListConfigurationProvider } from \"@pnp/config-store\"; // create a new provider instance const w = Web(\"https://mytenant.sharepoint.com/sites/dev\"); const provider = new SPListConfigurationProvider(w, \"myconfiglistname\"); // get an instance of the provider wrapped // you can optionally provide a key that will be used in the cache to the asCaching method const wrappedProvider = provider.asCaching(); // use that wrapped provider to populate the settings await settings.load(wrappedProvider);","title":"providers"},{"location":"config-store/providers/#pnpconfig-storeproviders","text":"Currently there is a single provider included in the library, but contributions of additional providers are welcome.","title":"@pnp/config-store/providers"},{"location":"config-store/providers/#splistconfigurationprovider","text":"This provider is based on a SharePoint list it reads all of the rows and makes them available as a TypedHash . By default the column names used are Title for key and \"Value\" for value, but you can update these as needed. Additionally, the settings class supports the idea of last value in wins - so you can easily load multiple configurations. This helps to support a common scenario in the enterprise where you might have one main list for global configuration but some settings can be set at the web level. In this case you would first load the global, then the local settings and any local values will take precedence. import { Web } from \"@pnp/sp/presets/all\"; import { Settings, SPListConfigurationProvider } from \"@pnp/config-store\"; // create a new provider instance const w = Web(\"https://mytenant.sharepoint.com/sites/dev\"); const provider = new SPListConfigurationProvider(w, \"myconfiglistname\"); const settings = new Settings(); // load our values from the list await settings.load(provider);","title":"SPListConfigurationProvider"},{"location":"config-store/providers/#cachingconfigurationprovider","text":"Because making requests on each page load is very inefficient you can optionally use the caching configuration provider, which wraps a provider and caches the configuration in local or session storage. import { Web } from \"@pnp/sp/presets/all\"; import { Settings, SPListConfigurationProvider } from \"@pnp/config-store\"; // create a new provider instance const w = Web(\"https://mytenant.sharepoint.com/sites/dev\"); const provider = new SPListConfigurationProvider(w, \"myconfiglistname\"); // get an instance of the provider wrapped // you can optionally provide a key that will be used in the cache to the asCaching method const wrappedProvider = provider.asCaching(); // use that wrapped provider to populate the settings await settings.load(wrappedProvider);","title":"CachingConfigurationProvider"},{"location":"contributing/","text":"Contributing to PnPjs \u00b6 Thank you for your interest in contributing to PnPjs. We have updated our contribution section to make things easier to get started, debug the library locally, and learn how to extend the functionality. Section Description Setup Dev Machine Covers setting up your machine to ensure you are ready to debug the solution Local Debug Configuration Discusses the steps required to establish local configuration used for debugging and running tests Debugging Describes how to debug PnPjs locally Extending the library Basic examples on how to extend the library such as adding a method or property Writing Tests How to write and debug tests Update Documentation Describes the steps required to edit and locally view the documentation Submit a Pull Request Outlines guidance for submitting a pull request Need Help? \u00b6 The PnP \"Sharing Is Caring\" initiative teaches the basics around making changes in GitHub, submitting pull requests to the PnP & Microsoft 365 open-source repositories such as PnPjs. Every month, we provide multiple live hands-on sessions that walk attendees through the process of using and contributing to PnP initiatives. To learn more and register for an upcoming session, please visit the Sharing is Caring website.","title":"Contributing"},{"location":"contributing/#contributing-to-pnpjs","text":"Thank you for your interest in contributing to PnPjs. We have updated our contribution section to make things easier to get started, debug the library locally, and learn how to extend the functionality. Section Description Setup Dev Machine Covers setting up your machine to ensure you are ready to debug the solution Local Debug Configuration Discusses the steps required to establish local configuration used for debugging and running tests Debugging Describes how to debug PnPjs locally Extending the library Basic examples on how to extend the library such as adding a method or property Writing Tests How to write and debug tests Update Documentation Describes the steps required to edit and locally view the documentation Submit a Pull Request Outlines guidance for submitting a pull request","title":"Contributing to PnPjs"},{"location":"contributing/#need-help","text":"The PnP \"Sharing Is Caring\" initiative teaches the basics around making changes in GitHub, submitting pull requests to the PnP & Microsoft 365 open-source repositories such as PnPjs. Every month, we provide multiple live hands-on sessions that walk attendees through the process of using and contributing to PnP initiatives. To learn more and register for an upcoming session, please visit the Sharing is Caring website.","title":"Need Help?"},{"location":"contributing/debug-tests/","text":"Writing Tests \u00b6 With version 2 we have made a significant effort to improve out test coverage. To keep that up, all changes submitted will require one or more tests be included. For new functionality at least a basic test that the method executes is required. For bug fixes please include a test that would have caught the bug (i.e. fail before your fix) and passes with your fix in place. How to write Tests \u00b6 We use Mocha and Chai for our testing framework. You can see many examples of writing tests within the ./test folder. Here is a sample with extra comments to help explain what's happening, taken from ./test/sp/items.ts : import { getRandomString } from \"@pnp/core\"; import { testSettings } from \"../main\"; import { expect } from \"chai\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items/list\"; import { IList } from \"@pnp/sp/lists\"; describe(\"Items\", () => { // any tests that make a web request should be withing a block checking if web tests are enabled if (testSettings.enableWebTests) { // a block scoped var we will use across our tests let list: IList = null; // we use the before block to setup // executed before all the tests in this block, see the mocha docs for more details // mocha prefers using function vs arrow functions and this is recommended before(async function () { // execute a request to ensure we have a list const ler = await sp.web.lists.ensure(\"ItemTestList\", \"Used to test item operations\"); list = ler.list; // in this case we want to have some items in the list for testing so we add those // only if the list was just created if (ler.created) { // add a few items to get started const batch = sp.web.createBatch(); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); await batch.execute(); } }); // this test has a label \"get items\" and is run via an async function it(\"get items\", async function () { // make a request for the list's items const items = await list.items(); // report that we expect that result to be an array with more than 0 items expect(items.length).to.be.gt(0); }); // ... remainder of code removed } } General Guidelines for Writing Tests \u00b6 Tests should operate within the site defined in testSettings Tests should be able to run multiple times on the same site, but do not need to cleanup after themselves Each test should be self contained and not depend on other tests, they can depend on work done in before or beforeAll When writing tests you can use \"only\" and \"skip\" from mochajs to focus on only the tests you are writing Be sure to review the various options when running your tests If you are writing a test and the endpoint doesn't support app only permissions, you can skip writing a test - but please note that in the PR description Next Steps \u00b6 Now that you've written tests to cover your changes you'll need to update the docs .","title":"Writing Tests"},{"location":"contributing/debug-tests/#writing-tests","text":"With version 2 we have made a significant effort to improve out test coverage. To keep that up, all changes submitted will require one or more tests be included. For new functionality at least a basic test that the method executes is required. For bug fixes please include a test that would have caught the bug (i.e. fail before your fix) and passes with your fix in place.","title":"Writing Tests"},{"location":"contributing/debug-tests/#how-to-write-tests","text":"We use Mocha and Chai for our testing framework. You can see many examples of writing tests within the ./test folder. Here is a sample with extra comments to help explain what's happening, taken from ./test/sp/items.ts : import { getRandomString } from \"@pnp/core\"; import { testSettings } from \"../main\"; import { expect } from \"chai\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items/list\"; import { IList } from \"@pnp/sp/lists\"; describe(\"Items\", () => { // any tests that make a web request should be withing a block checking if web tests are enabled if (testSettings.enableWebTests) { // a block scoped var we will use across our tests let list: IList = null; // we use the before block to setup // executed before all the tests in this block, see the mocha docs for more details // mocha prefers using function vs arrow functions and this is recommended before(async function () { // execute a request to ensure we have a list const ler = await sp.web.lists.ensure(\"ItemTestList\", \"Used to test item operations\"); list = ler.list; // in this case we want to have some items in the list for testing so we add those // only if the list was just created if (ler.created) { // add a few items to get started const batch = sp.web.createBatch(); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); await batch.execute(); } }); // this test has a label \"get items\" and is run via an async function it(\"get items\", async function () { // make a request for the list's items const items = await list.items(); // report that we expect that result to be an array with more than 0 items expect(items.length).to.be.gt(0); }); // ... remainder of code removed } }","title":"How to write Tests"},{"location":"contributing/debug-tests/#general-guidelines-for-writing-tests","text":"Tests should operate within the site defined in testSettings Tests should be able to run multiple times on the same site, but do not need to cleanup after themselves Each test should be self contained and not depend on other tests, they can depend on work done in before or beforeAll When writing tests you can use \"only\" and \"skip\" from mochajs to focus on only the tests you are writing Be sure to review the various options when running your tests If you are writing a test and the endpoint doesn't support app only permissions, you can skip writing a test - but please note that in the PR description","title":"General Guidelines for Writing Tests"},{"location":"contributing/debug-tests/#next-steps","text":"Now that you've written tests to cover your changes you'll need to update the docs .","title":"Next Steps"},{"location":"contributing/debugging/","text":"Debugging \u00b6 Using the steps in this article you will be able to locally debug the library internals as well as new features you are working on. Before proceeding be sure you have reviewed how to setup for local configuration and debugging. Debugging Library Features \u00b6 The easiest way to debug the library when working on new features is using F5 in Visual Studio Code. This uses launch.json to build and run the library using ./debug/launch/main.ts as the entry point. Basic SharePoint Testing \u00b6 You can start the base debugging case by hitting F5. Before you do place a break point in ./debug/launch/sp.ts. You can also place a break point within any of the libraries or modules. Feel free to edit the sp.ts file to try things out, debug suspected issues, or test new features, etc - but please don't commit any changes as this is a shared file. See the section on creating your own debug modules . All of the setup for the node client is handled within sp.ts using the settings from the local configuration . Basic Graph Testing \u00b6 Testing and debugging Graph calls follows the same process as outlined for SharePoint, however you need to update main.ts to import graph instead of sp. You can place break points anywhere within the library code and they should be hit. All of the setup for the node client is handled within graph.ts using the settings from the local configuration . How to: Create a Debug Module \u00b6 If you are working on multiple features or want to save sample code for various tasks you can create your own debugging modules and leave them in the debug/launch folder locally. The gitignore file is setup to ignore any files that aren't already in git. Using ./debug/launch/sp.ts as a reference create a file in the debug/launch folder, let's call it mydebug.ts and add this content: // note we can use the actual package names for our imports (ex: @pnp/logging) import { Logger, LogLevel, ConsoleListener } from \"@pnp/logging\"; // using the all preset for simplicity in the example, selective imports work as expected import { sp, ListEnsureResult } from \"@pnp/sp/presets/all\"; declare var process: { exit(code?: number): void }; export async function MyDebug() { // configure your options // you can have different configs in different modules as needed for your testing/dev work sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret); }, }, }); // run some debugging const list = await sp.web.lists.ensure(\"MyFirstList\"); Logger.log({ data: list.created, level: LogLevel.Info, message: \"Was list created?\", }); if (list.created) { Logger.log({ data: list.data, level: LogLevel.Info, message: \"Raw data from list creation.\", }); } else { Logger.log({ data: null, level: LogLevel.Info, message: \"List already existed!\", }); } process.exit(0); } Update main.ts to launch your module \u00b6 First comment out the import for the default example and then add the import and function call for yours, the updated launch/main.ts should look like this: // ... // comment out the example // import { Example } from \"./example\"; // Example(); import { MyDebug } from \"./mydebug\" MyDebug(); // ... Remember, please don't commit any changes to the shared files within the debug folder. (Unless you've found a bug that needs fixing in the original file) Debug \u00b6 Place a break point within the mydebug.ts file and hit F5. Your module should run and your break point hit. You can then examine the contents of the objects and see the run time state. Remember, you can also set breakpoints within the package src folders to see exactly how things are working during your debugging scenarios. Debug Module Next Steps \u00b6 Using this pattern you can create and preserve multiple debugging scenarios in separate modules locally - they won't be added to git. You just have to update main.ts to point to the one you want to run. In Browser Debugging \u00b6 You can also serve files locally to debug as a user in the browser by serving code using ./debug/serve/main.ts as the entry. The file is served as https://localhost:8080/assets/pnp.js , allowing you to create a single page in your tenant for in browser testing. The remainder of this section describes the process to setup a SharePoint page to debug in this manner. Start the local serve \u00b6 This will serve a package with ./debug/serve/main.ts as the entry. gulp serve Add reference to library \u00b6 Within a SharePoint page add a script editor web part and then paste in the following code. The div is to give you a place to target with visual updates should you desire.
      You should see an alert with the current web's title using the default main.ts. Feel free to update main.ts to do whatever you would like, but remember not to commit changes to the shared files. Debug \u00b6 Refresh the page and open the developer tools in your browser of choice. If the pnp.js file is blocked due to security restrictions you will need to allow it. Next Steps \u00b6 You can make changes to the library and immediately see them reflected in the browser. All files are watched so changes will be available as soon as webpack reloads the package. This allows you to rapidly test the library in the browser. Now you can learn about extending the library .","title":"Debugging"},{"location":"contributing/debugging/#debugging","text":"Using the steps in this article you will be able to locally debug the library internals as well as new features you are working on. Before proceeding be sure you have reviewed how to setup for local configuration and debugging.","title":"Debugging"},{"location":"contributing/debugging/#debugging-library-features","text":"The easiest way to debug the library when working on new features is using F5 in Visual Studio Code. This uses launch.json to build and run the library using ./debug/launch/main.ts as the entry point.","title":"Debugging Library Features"},{"location":"contributing/debugging/#basic-sharepoint-testing","text":"You can start the base debugging case by hitting F5. Before you do place a break point in ./debug/launch/sp.ts. You can also place a break point within any of the libraries or modules. Feel free to edit the sp.ts file to try things out, debug suspected issues, or test new features, etc - but please don't commit any changes as this is a shared file. See the section on creating your own debug modules . All of the setup for the node client is handled within sp.ts using the settings from the local configuration .","title":"Basic SharePoint Testing"},{"location":"contributing/debugging/#basic-graph-testing","text":"Testing and debugging Graph calls follows the same process as outlined for SharePoint, however you need to update main.ts to import graph instead of sp. You can place break points anywhere within the library code and they should be hit. All of the setup for the node client is handled within graph.ts using the settings from the local configuration .","title":"Basic Graph Testing"},{"location":"contributing/debugging/#how-to-create-a-debug-module","text":"If you are working on multiple features or want to save sample code for various tasks you can create your own debugging modules and leave them in the debug/launch folder locally. The gitignore file is setup to ignore any files that aren't already in git. Using ./debug/launch/sp.ts as a reference create a file in the debug/launch folder, let's call it mydebug.ts and add this content: // note we can use the actual package names for our imports (ex: @pnp/logging) import { Logger, LogLevel, ConsoleListener } from \"@pnp/logging\"; // using the all preset for simplicity in the example, selective imports work as expected import { sp, ListEnsureResult } from \"@pnp/sp/presets/all\"; declare var process: { exit(code?: number): void }; export async function MyDebug() { // configure your options // you can have different configs in different modules as needed for your testing/dev work sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret); }, }, }); // run some debugging const list = await sp.web.lists.ensure(\"MyFirstList\"); Logger.log({ data: list.created, level: LogLevel.Info, message: \"Was list created?\", }); if (list.created) { Logger.log({ data: list.data, level: LogLevel.Info, message: \"Raw data from list creation.\", }); } else { Logger.log({ data: null, level: LogLevel.Info, message: \"List already existed!\", }); } process.exit(0); }","title":"How to: Create a Debug Module"},{"location":"contributing/debugging/#update-maints-to-launch-your-module","text":"First comment out the import for the default example and then add the import and function call for yours, the updated launch/main.ts should look like this: // ... // comment out the example // import { Example } from \"./example\"; // Example(); import { MyDebug } from \"./mydebug\" MyDebug(); // ... Remember, please don't commit any changes to the shared files within the debug folder. (Unless you've found a bug that needs fixing in the original file)","title":"Update main.ts to launch your module"},{"location":"contributing/debugging/#debug","text":"Place a break point within the mydebug.ts file and hit F5. Your module should run and your break point hit. You can then examine the contents of the objects and see the run time state. Remember, you can also set breakpoints within the package src folders to see exactly how things are working during your debugging scenarios.","title":"Debug"},{"location":"contributing/debugging/#debug-module-next-steps","text":"Using this pattern you can create and preserve multiple debugging scenarios in separate modules locally - they won't be added to git. You just have to update main.ts to point to the one you want to run.","title":"Debug Module Next Steps"},{"location":"contributing/debugging/#in-browser-debugging","text":"You can also serve files locally to debug as a user in the browser by serving code using ./debug/serve/main.ts as the entry. The file is served as https://localhost:8080/assets/pnp.js , allowing you to create a single page in your tenant for in browser testing. The remainder of this section describes the process to setup a SharePoint page to debug in this manner.","title":"In Browser Debugging"},{"location":"contributing/debugging/#start-the-local-serve","text":"This will serve a package with ./debug/serve/main.ts as the entry. gulp serve","title":"Start the local serve"},{"location":"contributing/debugging/#add-reference-to-library","text":"Within a SharePoint page add a script editor web part and then paste in the following code. The div is to give you a place to target with visual updates should you desire.
      You should see an alert with the current web's title using the default main.ts. Feel free to update main.ts to do whatever you would like, but remember not to commit changes to the shared files.","title":"Add reference to library"},{"location":"contributing/debugging/#debug_1","text":"Refresh the page and open the developer tools in your browser of choice. If the pnp.js file is blocked due to security restrictions you will need to allow it.","title":"Debug"},{"location":"contributing/debugging/#next-steps","text":"You can make changes to the library and immediately see them reflected in the browser. All files are watched so changes will be available as soon as webpack reloads the package. This allows you to rapidly test the library in the browser. Now you can learn about extending the library .","title":"Next Steps"},{"location":"contributing/documentation/","text":"Documentation \u00b6 Just like with tests we have invested much time in updating the documentation and when you make a change to the library you should update the associated documentation as part of the pull request. Writing Docs \u00b6 Our docs are all written in markdown and processed using MkDocs. You can use code blocks, tables, and other markdown formatting. You can review the other articles for examples on writing docs. Generally articles should focus on how to use the library and where appropriate link to official outside documents as needed. Official documentation could be Microsoft, other library project docs such as MkDocs, or other sources. Building Docs Locally \u00b6 Building the documentation locally can help you visualize change you are making to the docs. What you see locally will be what you see online. Documentation is built using MkDocs. You will need to latest version of Python (tested on version 3.7.1) and pip. If you're on the Windows operating system, make sure you have added Python to your Path environment variable . When executing the pip module on Windows you can prefix it with python -m . For example: python -m pip install mkdocs-material Install MkDocs pip install mkdocs Install the Material theme pip install mkdocs-material install the mkdocs-markdownextradata-plugin - this is used for the version variable pip install mkdocs-markdownextradata-plugin (doesn't work on Python v2.7) install redirect plugin - used to redirect from moved pages pip install mkdocs-redirects Serve it up mkdocs serve Open a browser to http://127.0.0.1:8000/ Please see the official mkdocs site for more details on working with mkdocs Next Steps \u00b6 After your changes are made, you've added/updated tests, and updated the docs you're ready to submit a pull request !","title":"Update Documentation"},{"location":"contributing/documentation/#documentation","text":"Just like with tests we have invested much time in updating the documentation and when you make a change to the library you should update the associated documentation as part of the pull request.","title":"Documentation"},{"location":"contributing/documentation/#writing-docs","text":"Our docs are all written in markdown and processed using MkDocs. You can use code blocks, tables, and other markdown formatting. You can review the other articles for examples on writing docs. Generally articles should focus on how to use the library and where appropriate link to official outside documents as needed. Official documentation could be Microsoft, other library project docs such as MkDocs, or other sources.","title":"Writing Docs"},{"location":"contributing/documentation/#building-docs-locally","text":"Building the documentation locally can help you visualize change you are making to the docs. What you see locally will be what you see online. Documentation is built using MkDocs. You will need to latest version of Python (tested on version 3.7.1) and pip. If you're on the Windows operating system, make sure you have added Python to your Path environment variable . When executing the pip module on Windows you can prefix it with python -m . For example: python -m pip install mkdocs-material Install MkDocs pip install mkdocs Install the Material theme pip install mkdocs-material install the mkdocs-markdownextradata-plugin - this is used for the version variable pip install mkdocs-markdownextradata-plugin (doesn't work on Python v2.7) install redirect plugin - used to redirect from moved pages pip install mkdocs-redirects Serve it up mkdocs serve Open a browser to http://127.0.0.1:8000/ Please see the official mkdocs site for more details on working with mkdocs","title":"Building Docs Locally"},{"location":"contributing/documentation/#next-steps","text":"After your changes are made, you've added/updated tests, and updated the docs you're ready to submit a pull request !","title":"Next Steps"},{"location":"contributing/extending-the-library/","text":"Extending PnPjs \u00b6 This article is targeted at people wishing to extend PnPjs itself, usually by adding a method or property. At the most basic level PnPjs is a set of libraries used to build and execute a web request and handle the response from that request. Conceptually each object in the fluent chain serves as input when creating the next object in the chain. This is how configuration, url, query, and other values are passed along. To get a sense for what this looks like see the code below. This is taken from inside the webs submodule and shows how the \"webs\" property is added to the web class. // TypeScript property, returning an interface public get webs(): IWebs { // using the Webs factory function and providing \"this\" as the first parameter return Webs(this); } Understanding Factory Functions \u00b6 PnPjs v2 is designed to only expose interfaces and factory functions. Let's look at the Webs factory function, used above as an example. All factory functions in sp and graph have a similar form. // create a constant which is a function of type ISPInvokableFactory having the name Webs // this is bound by the generic type param to return an IWebs instance // and it will use the _Webs concrete class to form the internal type of the invocable export const Webs = spInvokableFactory(_Webs); The ISPInvokableFactory type looks like: export type ISPInvokableFactory = (baseUrl: string | ISharePointQueryable, path?: string) => R; And the matching graph type: (f: any): (baseUrl: string | IGraphQueryable, path?: string) => R The general idea of a factory function is that it takes two parameters. The first is either a string or Queryable derivative which forms base for the new object. The second is the next part of the url. In some cases (like the webs property example above) you will note there is no second parameter. Some classes are decorated with defaultPath, which automatically fills the second param. Don't worry too much right now about the deep internals of the library, let's instead focus on some concrete examples. import { Web } from \"@pnp/sp/webs\"; // create a web from an absolute url const web = Web(\"https://tenant.sharepoint.com\"); // as an example, create a new web using the first as a base // targets: https://tenant.sharepoint.com/sites/dev const web2 = Web(web, \"sites/dev\"); // or you can add any path components you want, here as an example we access the current user property const cu = Web(web, \"currentuser\"); const currentUserInfo = cu(); Now hey you might say - you can't create a request to current user using the Web factory. Well you can, since everything is just based on urls under the covers the actual factory names don't mean anything other than they have the appropriate properties and method hung off them. This is brought up as you will see in many cases objects being used to create queries within methods and properties that don't match their \"type\". It is an important concept when working with the library to always remember we are just building strings. Class structure \u00b6 Internally to the library we have a bit of complexity to make the whole invocable proxy architecture work and provide the typings folks expect. Here is an example implementation with extra comments explaining what is happening. You don't need to understand the entire stack to add a property or method /* The concrete class implementation. This is never exported or shown directly to consumers of the library. It is wrapped by the Proxy we do expose. It extends the _SharePointQueryableInstance class for which there is a matching _SharePointQueryableCollection. The generic parameter defines the return type of a get operation and the invoked result. Classes can have methods and properties as normal. This one has a single property as a simple example */ export class _HubSite extends _SharePointQueryableInstance { /** * Gets the ISite instance associated with this hub site */ // the tag decorator is used to provide some additional telemetry on what methods are // being called. @tag(\"hs.getSite\") public async getSite(): Promise { // we execute a request using this instance, selecting the SiteUrl property, and invoking it immediately and awaiting the result const d = await this.select(\"SiteUrl\")(); // we then return a new ISite instance created from the Site factory using the returned SiteUrl property as the baseUrl return Site(d.SiteUrl); } } /* This defines the interface we export and expose to consumers. In most cases this extends the concrete object but may add or remove some methods/properties in special cases */ export interface IHubSite extends _HubSite { } /* This defines the HubSite factory function as discussed above binding the spInvokableFactory to a generic param of IHubSite and a param of _HubSite. This is understood to mean that HubSite is a factory function that returns a types of IHubSite which the spInvokableFactory will create using _HubSite as the concrete underlying type. */ export const HubSite = spInvokableFactory(_HubSite); Add a Property \u00b6 In most cases you won't need to create the class, interface, or factory - you just want to add a property or method. An example of this is sp.web.lists. web is a property of sp and lists is a property of web. You can have a look at those classes as examples. Let's have a look at the fields on the _View class. export class _View extends _SharePointQueryableInstance { // ... other code removed // add the property, and provide a return type // return types should be interfaces public get fields(): IViewFields { // we use the ViewFields factory function supplying \"this\" as the first parameter // this will create a url like \".../fields/viewfields\" due to the defaultPath decorator // on the _ViewFields class. This is equivalent to: ViewFields(this, \"viewfields\") return ViewFields(this); } // ... other code removed } There are many examples throughout the library that follow this pattern. Add a Method \u00b6 Adding a method is just like adding a property with the key difference that a method usually does something like make a web request or act like a property but take parameters. Let's look at the _Items getById method: @defaultPath(\"items\") export class _Items extends _SharePointQueryableCollection { /** * Gets an Item by id * * @param id The integer id of the item to retrieve */ // we declare a method and set the return type to an interface public getById(id: number): IItem { // here we use the tag helper to add some telemetry to our request // we create a new IItem using the factory and appending the id value to the end // this gives us a valid url path to a single item .../items/getById(2) // we can then use the returned IItem to extend our chain or execute a request return tag.configure(Item(this).concat(`(${id})`), \"is.getById\"); } // ... other code removed } Web Request Method \u00b6 A second example is a method that performs a request. Here we use the _Item recycle method as an example: /** * Moves the list item to the Recycle Bin and returns the identifier of the new Recycle Bin item. */ // we use the tag decorator to add telemetry @tag(\"i.recycle\") // we return a promise public recycle(): Promise { // we use the spPost method to post the request created by cloning our current instance IItem using // the Item factory and adding the path \"recycle\" to the end. Url will look like .../items/getById(2)/recycle return spPost(this.clone(Item, \"recycle\")); } Augment Using Selective Imports \u00b6 To understand is how to extend functionality within the selective imports structures look at list.ts file in the items submodule. Here you can see the code below, with extra comments to explain what is happening. Again, you will see this pattern repeated throughout the library so there are many examples available. // import the addProp helper import { addProp } from \"@pnp/queryable\"; // import the _List concrete class from the types module (not the index!) import { _List } from \"../lists/types\"; // import the interface and factory we are going to add to the List import { Items, IItems } from \"./types\"; // This module declaration fixes up the types, allowing .items to appear in intellisense // when you import \"@pnp/sp/items/list\"; declare module \"../lists/types\" { // we need to extend the concrete type interface _List { readonly items: IItems; } // we need to extend the interface // this may not be strictly necessary as the IList interface extends _List so it // should pick up the same additions, but we have seen in some cases this does seem // to be required. So we include it for safety as it will all be removed during // transpilation we don't need to care about the extra code interface IList { readonly items: IItems; } } // finally we add the property to the _List class // this method call says add a property to _List named \"items\" and that property returns a result using the Items factory // The factory will be called with \"this\" when the property is accessed. If needed there is a fourth parameter to append additional path // information to the property url addProp(_List, \"items\", Items); General Rules for Extending PnPjs \u00b6 Only expose interfaces to consumers Use the factory functions except in very special cases Look for other properties and methods as examples Simple is always preferable, but not always possible - use your best judgement If you find yourself writing a ton of code to solve a problem you think should be easy, ask If you find yourself deep within the core classes or odata library trying to make a change, ask - changes to the core classes are rarely needed Next Steps \u00b6 Now that you have extended the library you need to write a test to cover it!","title":"Extending the library"},{"location":"contributing/extending-the-library/#extending-pnpjs","text":"This article is targeted at people wishing to extend PnPjs itself, usually by adding a method or property. At the most basic level PnPjs is a set of libraries used to build and execute a web request and handle the response from that request. Conceptually each object in the fluent chain serves as input when creating the next object in the chain. This is how configuration, url, query, and other values are passed along. To get a sense for what this looks like see the code below. This is taken from inside the webs submodule and shows how the \"webs\" property is added to the web class. // TypeScript property, returning an interface public get webs(): IWebs { // using the Webs factory function and providing \"this\" as the first parameter return Webs(this); }","title":"Extending PnPjs"},{"location":"contributing/extending-the-library/#understanding-factory-functions","text":"PnPjs v2 is designed to only expose interfaces and factory functions. Let's look at the Webs factory function, used above as an example. All factory functions in sp and graph have a similar form. // create a constant which is a function of type ISPInvokableFactory having the name Webs // this is bound by the generic type param to return an IWebs instance // and it will use the _Webs concrete class to form the internal type of the invocable export const Webs = spInvokableFactory(_Webs); The ISPInvokableFactory type looks like: export type ISPInvokableFactory = (baseUrl: string | ISharePointQueryable, path?: string) => R; And the matching graph type: (f: any): (baseUrl: string | IGraphQueryable, path?: string) => R The general idea of a factory function is that it takes two parameters. The first is either a string or Queryable derivative which forms base for the new object. The second is the next part of the url. In some cases (like the webs property example above) you will note there is no second parameter. Some classes are decorated with defaultPath, which automatically fills the second param. Don't worry too much right now about the deep internals of the library, let's instead focus on some concrete examples. import { Web } from \"@pnp/sp/webs\"; // create a web from an absolute url const web = Web(\"https://tenant.sharepoint.com\"); // as an example, create a new web using the first as a base // targets: https://tenant.sharepoint.com/sites/dev const web2 = Web(web, \"sites/dev\"); // or you can add any path components you want, here as an example we access the current user property const cu = Web(web, \"currentuser\"); const currentUserInfo = cu(); Now hey you might say - you can't create a request to current user using the Web factory. Well you can, since everything is just based on urls under the covers the actual factory names don't mean anything other than they have the appropriate properties and method hung off them. This is brought up as you will see in many cases objects being used to create queries within methods and properties that don't match their \"type\". It is an important concept when working with the library to always remember we are just building strings.","title":"Understanding Factory Functions"},{"location":"contributing/extending-the-library/#class-structure","text":"Internally to the library we have a bit of complexity to make the whole invocable proxy architecture work and provide the typings folks expect. Here is an example implementation with extra comments explaining what is happening. You don't need to understand the entire stack to add a property or method /* The concrete class implementation. This is never exported or shown directly to consumers of the library. It is wrapped by the Proxy we do expose. It extends the _SharePointQueryableInstance class for which there is a matching _SharePointQueryableCollection. The generic parameter defines the return type of a get operation and the invoked result. Classes can have methods and properties as normal. This one has a single property as a simple example */ export class _HubSite extends _SharePointQueryableInstance { /** * Gets the ISite instance associated with this hub site */ // the tag decorator is used to provide some additional telemetry on what methods are // being called. @tag(\"hs.getSite\") public async getSite(): Promise { // we execute a request using this instance, selecting the SiteUrl property, and invoking it immediately and awaiting the result const d = await this.select(\"SiteUrl\")(); // we then return a new ISite instance created from the Site factory using the returned SiteUrl property as the baseUrl return Site(d.SiteUrl); } } /* This defines the interface we export and expose to consumers. In most cases this extends the concrete object but may add or remove some methods/properties in special cases */ export interface IHubSite extends _HubSite { } /* This defines the HubSite factory function as discussed above binding the spInvokableFactory to a generic param of IHubSite and a param of _HubSite. This is understood to mean that HubSite is a factory function that returns a types of IHubSite which the spInvokableFactory will create using _HubSite as the concrete underlying type. */ export const HubSite = spInvokableFactory(_HubSite);","title":"Class structure"},{"location":"contributing/extending-the-library/#add-a-property","text":"In most cases you won't need to create the class, interface, or factory - you just want to add a property or method. An example of this is sp.web.lists. web is a property of sp and lists is a property of web. You can have a look at those classes as examples. Let's have a look at the fields on the _View class. export class _View extends _SharePointQueryableInstance { // ... other code removed // add the property, and provide a return type // return types should be interfaces public get fields(): IViewFields { // we use the ViewFields factory function supplying \"this\" as the first parameter // this will create a url like \".../fields/viewfields\" due to the defaultPath decorator // on the _ViewFields class. This is equivalent to: ViewFields(this, \"viewfields\") return ViewFields(this); } // ... other code removed } There are many examples throughout the library that follow this pattern.","title":"Add a Property"},{"location":"contributing/extending-the-library/#add-a-method","text":"Adding a method is just like adding a property with the key difference that a method usually does something like make a web request or act like a property but take parameters. Let's look at the _Items getById method: @defaultPath(\"items\") export class _Items extends _SharePointQueryableCollection { /** * Gets an Item by id * * @param id The integer id of the item to retrieve */ // we declare a method and set the return type to an interface public getById(id: number): IItem { // here we use the tag helper to add some telemetry to our request // we create a new IItem using the factory and appending the id value to the end // this gives us a valid url path to a single item .../items/getById(2) // we can then use the returned IItem to extend our chain or execute a request return tag.configure(Item(this).concat(`(${id})`), \"is.getById\"); } // ... other code removed }","title":"Add a Method"},{"location":"contributing/extending-the-library/#web-request-method","text":"A second example is a method that performs a request. Here we use the _Item recycle method as an example: /** * Moves the list item to the Recycle Bin and returns the identifier of the new Recycle Bin item. */ // we use the tag decorator to add telemetry @tag(\"i.recycle\") // we return a promise public recycle(): Promise { // we use the spPost method to post the request created by cloning our current instance IItem using // the Item factory and adding the path \"recycle\" to the end. Url will look like .../items/getById(2)/recycle return spPost(this.clone(Item, \"recycle\")); }","title":"Web Request Method"},{"location":"contributing/extending-the-library/#augment-using-selective-imports","text":"To understand is how to extend functionality within the selective imports structures look at list.ts file in the items submodule. Here you can see the code below, with extra comments to explain what is happening. Again, you will see this pattern repeated throughout the library so there are many examples available. // import the addProp helper import { addProp } from \"@pnp/queryable\"; // import the _List concrete class from the types module (not the index!) import { _List } from \"../lists/types\"; // import the interface and factory we are going to add to the List import { Items, IItems } from \"./types\"; // This module declaration fixes up the types, allowing .items to appear in intellisense // when you import \"@pnp/sp/items/list\"; declare module \"../lists/types\" { // we need to extend the concrete type interface _List { readonly items: IItems; } // we need to extend the interface // this may not be strictly necessary as the IList interface extends _List so it // should pick up the same additions, but we have seen in some cases this does seem // to be required. So we include it for safety as it will all be removed during // transpilation we don't need to care about the extra code interface IList { readonly items: IItems; } } // finally we add the property to the _List class // this method call says add a property to _List named \"items\" and that property returns a result using the Items factory // The factory will be called with \"this\" when the property is accessed. If needed there is a fourth parameter to append additional path // information to the property url addProp(_List, \"items\", Items);","title":"Augment Using Selective Imports"},{"location":"contributing/extending-the-library/#general-rules-for-extending-pnpjs","text":"Only expose interfaces to consumers Use the factory functions except in very special cases Look for other properties and methods as examples Simple is always preferable, but not always possible - use your best judgement If you find yourself writing a ton of code to solve a problem you think should be easy, ask If you find yourself deep within the core classes or odata library trying to make a change, ask - changes to the core classes are rarely needed","title":"General Rules for Extending PnPjs"},{"location":"contributing/extending-the-library/#next-steps","text":"Now that you have extended the library you need to write a test to cover it!","title":"Next Steps"},{"location":"contributing/local-debug-configuration/","text":"Local Debugging Configuration \u00b6 This article covers the local setup required to debug the library and run tests. This only needs to be done once (unless you update the app registrations, then you just need to update the settings.js file accordingly). Create settings.js \u00b6 Both local debugging and tests make use of a settings.js file located in the root of the project. Ensure you create a settings.js files by copying settings.example.js and renaming it to settings.js. For more information the settings file please see Settings Minimal Configuration \u00b6 You can control which tests are run by including or omitting sp and graph sections. If sp is present and graph is not, only sp tests are run. Include both and all tests are run, respecting the enableWebTests flag. The following configuration file allows you to run all the tests that do not contact services. var sets = { testing: { enableWebTests: false, } } module.exports = sets; Test your setup \u00b6 If you hit F5 in VSCode now you should be able to see the full response from getting the web's title in the internal console window. If not, ensure that you have properly updated the settings file and registered the add-in perms correctly.","title":"Local Debug Configuration"},{"location":"contributing/local-debug-configuration/#local-debugging-configuration","text":"This article covers the local setup required to debug the library and run tests. This only needs to be done once (unless you update the app registrations, then you just need to update the settings.js file accordingly).","title":"Local Debugging Configuration"},{"location":"contributing/local-debug-configuration/#create-settingsjs","text":"Both local debugging and tests make use of a settings.js file located in the root of the project. Ensure you create a settings.js files by copying settings.example.js and renaming it to settings.js. For more information the settings file please see Settings","title":"Create settings.js"},{"location":"contributing/local-debug-configuration/#minimal-configuration","text":"You can control which tests are run by including or omitting sp and graph sections. If sp is present and graph is not, only sp tests are run. Include both and all tests are run, respecting the enableWebTests flag. The following configuration file allows you to run all the tests that do not contact services. var sets = { testing: { enableWebTests: false, } } module.exports = sets;","title":"Minimal Configuration"},{"location":"contributing/local-debug-configuration/#test-your-setup","text":"If you hit F5 in VSCode now you should be able to see the full response from getting the web's title in the internal console window. If not, ensure that you have properly updated the settings file and registered the add-in perms correctly.","title":"Test your setup"},{"location":"contributing/pull-requests/","text":"Submitting Pull Requests \u00b6 Pull requests may be large or small - adding whole new features or fixing some misspellings. Regardless, they are all appreciated and help improve the library for everyone! By following the below guidelines we'll have an easier time merging your work and getting it into the next release. Target your pull requests to the version-2 branch Add/Update any relevant docs articles in the relevant package's docs folder related to your changes Include a test for any new functionality and ensure all existing tests are passing by running npm test Ensure linting checks pass by typing npm run lint Ensure everything works for a build by running npm run package Keep your PRs as simple as possible and describe the changes to help the reviewer understand your work If you have an idea for a larger change to the library please open an issue and let's discuss before you invest many hours - these are very welcome but want to ensure it is something we can merge before you spend the time :) If you need to target a PR for version 1, please target the \"version-1\" branch Sharing is Caring - Pull Request Guidance \u00b6 The PnP \"Sharing Is Caring\" initiative teaches the basics around making changes in GitHub, submitting pull requests to the PnP & Microsoft 365 open-source repositories such as PnPjs. Every month, we provide multiple live hands-on sessions that walk attendees through the process of using and contributing to PnP initiatives. To learn more and register for an upcoming session, please visit the Sharing is Caring website. Next Steps \u00b6 Now that you've submitted your PR please keep an eye on it as we might have questions. Once an initial review is complete we'll tag it with the expected version number for which it is targeted. Thank you for helping PnPjs grow and improve!!","title":"Submit a Pull Request"},{"location":"contributing/pull-requests/#submitting-pull-requests","text":"Pull requests may be large or small - adding whole new features or fixing some misspellings. Regardless, they are all appreciated and help improve the library for everyone! By following the below guidelines we'll have an easier time merging your work and getting it into the next release. Target your pull requests to the version-2 branch Add/Update any relevant docs articles in the relevant package's docs folder related to your changes Include a test for any new functionality and ensure all existing tests are passing by running npm test Ensure linting checks pass by typing npm run lint Ensure everything works for a build by running npm run package Keep your PRs as simple as possible and describe the changes to help the reviewer understand your work If you have an idea for a larger change to the library please open an issue and let's discuss before you invest many hours - these are very welcome but want to ensure it is something we can merge before you spend the time :) If you need to target a PR for version 1, please target the \"version-1\" branch","title":"Submitting Pull Requests"},{"location":"contributing/pull-requests/#sharing-is-caring-pull-request-guidance","text":"The PnP \"Sharing Is Caring\" initiative teaches the basics around making changes in GitHub, submitting pull requests to the PnP & Microsoft 365 open-source repositories such as PnPjs. Every month, we provide multiple live hands-on sessions that walk attendees through the process of using and contributing to PnP initiatives. To learn more and register for an upcoming session, please visit the Sharing is Caring website.","title":"Sharing is Caring - Pull Request Guidance"},{"location":"contributing/pull-requests/#next-steps","text":"Now that you've submitted your PR please keep an eye on it as we might have questions. Once an initial review is complete we'll tag it with the expected version number for which it is targeted. Thank you for helping PnPjs grow and improve!!","title":"Next Steps"},{"location":"contributing/setup-dev-machine/","text":"Setting up your Developer Machine \u00b6 If you are a longtime client side developer you likely have your machine already configured and can skip to forking the repo and debugging . Setup your development environment \u00b6 These steps will help you get your environment setup for contributing to the core library. Install Visual Studio Code - this is the development environment we use so the contribution sections expect you are as well. If you prefer you can use Visual Studio or any editor you like. Install Node JS - this provides two key capabilities; the first is the nodejs server which will act as our development server (think iisexpress), the second is npm a package manager (think nuget). This library requires node >= 10.18.0 On Windows: Install Python [Optional] Install the tslint extension in VS Code: Press Shift + Ctrl + \"p\" to open the command panel Begin typing \"install extension\" and select the command when it appears in view Begin typing \"tslint\" and select the package when it appears in view Restart Code after installation Fork The Repo \u00b6 All of our contributions come via pull requests and you'll need to fork the repository Now we need to fork and clone the git repository. This can be done using your console or using your preferred Git GUI tool. Once you have the code locally, navigate to the root of the project in your console. Type the following command: npm install Follow the guidance to complete the one-time local configuration required to debug and run tests. Then you can follow the guidance in the debugging article.","title":"Setup Dev Machine"},{"location":"contributing/setup-dev-machine/#setting-up-your-developer-machine","text":"If you are a longtime client side developer you likely have your machine already configured and can skip to forking the repo and debugging .","title":"Setting up your Developer Machine"},{"location":"contributing/setup-dev-machine/#setup-your-development-environment","text":"These steps will help you get your environment setup for contributing to the core library. Install Visual Studio Code - this is the development environment we use so the contribution sections expect you are as well. If you prefer you can use Visual Studio or any editor you like. Install Node JS - this provides two key capabilities; the first is the nodejs server which will act as our development server (think iisexpress), the second is npm a package manager (think nuget). This library requires node >= 10.18.0 On Windows: Install Python [Optional] Install the tslint extension in VS Code: Press Shift + Ctrl + \"p\" to open the command panel Begin typing \"install extension\" and select the command when it appears in view Begin typing \"tslint\" and select the package when it appears in view Restart Code after installation","title":"Setup your development environment"},{"location":"contributing/setup-dev-machine/#fork-the-repo","text":"All of our contributions come via pull requests and you'll need to fork the repository Now we need to fork and clone the git repository. This can be done using your console or using your preferred Git GUI tool. Once you have the code locally, navigate to the root of the project in your console. Type the following command: npm install Follow the guidance to complete the one-time local configuration required to debug and run tests. Then you can follow the guidance in the debugging article.","title":"Fork The Repo"},{"location":"graph/","text":"@pnp/graph \u00b6 This package contains the fluent api used to call the graph rest services. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save Import the library into your application and access the root sp object import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; (function main() { // here we will load the current web's properties graph.groups().then(g => { console.log(`Groups: ${JSON.stringify(g, null, 4)}`); }); })() Getting Started with SharePoint Framework \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save Import the library into your application, update OnInit, and access the root sp object in render import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present graph.setup({ spfxContext: this.context }); }); } // ... public render(): void { // A simple loading message this.domElement.innerHTML = `Loading...`; // here we will load the current web's properties graph.groups().then(groups => { this.domElement.innerHTML = `Groups:
        ${groups.map(g => `
      • ${g.displayName}
      • `).join(\"\")}
      `; }); } Getting Started on Nodejs \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph @pnp/nodejs --save Import the library into your application, setup the node client, make a request import { graph } from \"@pnp/graph\"; import { AdalFetchClient } from \"@pnp/nodejs\"; import \"@pnp/graph/groups\"; // do this once per page load graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{tenant}.onmicrosoft.com\", \"AAD Application Id\", \"AAD Application Secret\"); }, }, }); // here we will load the groups information graph.groups().then(g => { console.log(`Groups: ${JSON.stringify(g, null, 4)}`); });","title":"graph"},{"location":"graph/#pnpgraph","text":"This package contains the fluent api used to call the graph rest services.","title":"@pnp/graph"},{"location":"graph/#getting-started","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save Import the library into your application and access the root sp object import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; (function main() { // here we will load the current web's properties graph.groups().then(g => { console.log(`Groups: ${JSON.stringify(g, null, 4)}`); }); })()","title":"Getting Started"},{"location":"graph/#getting-started-with-sharepoint-framework","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save Import the library into your application, update OnInit, and access the root sp object in render import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present graph.setup({ spfxContext: this.context }); }); } // ... public render(): void { // A simple loading message this.domElement.innerHTML = `Loading...`; // here we will load the current web's properties graph.groups().then(groups => { this.domElement.innerHTML = `Groups:
        ${groups.map(g => `
      • ${g.displayName}
      • `).join(\"\")}
      `; }); }","title":"Getting Started with SharePoint Framework"},{"location":"graph/#getting-started-on-nodejs","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph @pnp/nodejs --save Import the library into your application, setup the node client, make a request import { graph } from \"@pnp/graph\"; import { AdalFetchClient } from \"@pnp/nodejs\"; import \"@pnp/graph/groups\"; // do this once per page load graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{tenant}.onmicrosoft.com\", \"AAD Application Id\", \"AAD Application Secret\"); }, }, }); // here we will load the groups information graph.groups().then(g => { console.log(`Groups: ${JSON.stringify(g, null, 4)}`); });","title":"Getting Started on Nodejs"},{"location":"graph/calendars/","text":"@pnp/graph/calendars \u00b6 Calendars exist in Outlook and can belong to either a user or group. With @pnp/graph@<=2.0.6 , only events for a user and group's default calendar could be fetched/created/updated. In versions 2.0.7 and up, all calendars and their events can be fetched. More information can be found in the official Graph documentation: Calendar Resource Type Event Resource Type ICalendar, ICalendars \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/calendars\"; Preset: All import { graph } from \"@pnp/graph/presets/all\"; Get All Calendars For a User \u00b6 import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const calendars = await graph.users.getById('user@tenant.onmicrosoft.com').calendars(); const myCalendars = await graph.me.calendars(); Get a Specific Calendar For a User \u00b6 import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const CALENDAR_ID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA-xUBMMopY1NkrWA0qGcXHsHAG4I-wMXjoRMkgRnRetM5oIAAAIBBgAAAG4I-wMXjoRMkgRnRetM5oIAAAIsYgAAAA=='; const calendar = await graph.users.getById('user@tenant.onmicrosoft.com').calendars.getById(CALENDAR_ID)(); const myCalendar = await graph.me.calendars.getById(CALENDAR_ID)(); Get a User's Default Calendar \u00b6 import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const calendar = await graph.users.getById('user@tenant.onmicrosoft.com').calendar(); const myCalendar = await graph.me.calendar(); Get Events For a User's Default Calendar \u00b6 import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; // You can get the default calendar events const events = await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events(); // or get all events for the user const events = await graph.users.getById('user@tenant.onmicrosoft.com').events(); // You can get my default calendar events const events = await graph.me.calendar.events(); // or get all events for me const events = await graph.me.events(); Get Events By ID \u00b6 You can use .events.getByID to search through all the events in all calendars or narrow the request to a specific calendar. import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const CalendarID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA=='; const EventID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA-xUBMMopY1NkrWA0qGcXHsHAG4I-wMXjoRMkgRnRetM5oIAAAIBBgAAAG4I-wMXjoRMkgRnRetM5oIAAAIsYgAAAA=='; // Get events by ID const event = await graph.users.getById('user@tenant.onmicrosoft.com').events.getByID(EventID); const events = await graph.me.events.getByID(EventID); // Get an event by ID from a specific calendar const event = await graph.users.getById('user@tenant.onmicrosoft.com').calendars.getByID(CalendarID).events.getByID(EventID); const events = await graph.me.calendars.getByID(CalendarID).events.getByID(EventID); Create Events \u00b6 This will work on any IEvents objects (e.g. anything accessed using an events key). import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events.add( { \"subject\": \"Let's go for lunch\", \"body\": { \"contentType\": \"HTML\", \"content\": \"Does late morning work for you?\" }, \"start\": { \"dateTime\": \"2017-04-15T12:00:00\", \"timeZone\": \"Pacific Standard Time\" }, \"end\": { \"dateTime\": \"2017-04-15T14:00:00\", \"timeZone\": \"Pacific Standard Time\" }, \"location\":{ \"displayName\":\"Harry's Bar\" }, \"attendees\": [ { \"emailAddress\": { \"address\":\"samanthab@contoso.onmicrosoft.com\", \"name\": \"Samantha Booth\" }, \"type\": \"required\" } ] }); Update Events \u00b6 This will work on any IEvents objects (e.g. anything accessed using an events key). import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const EVENT_ID = 'BBMkAGZjNmY6MDM3LWI3YTYtNERhZC05Y2FkLTgyZjcwZjE4OTI5ZQBGAAAAAAD8VQTDKKWNTY61gNKhnFzLBwBuCP8DF46ETJIEZ0XrTOaCAAAAAAENAABuCP8DF46ETJFEZ0EnTOaCAAFvdoJvAAA='; await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events.getById(EVENT_ID).update({ reminderMinutesBeforeStart: 99, }); Delete Event \u00b6 This will work on any IEvents objects (e.g. anything accessed using an events key). import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const EVENT_ID = 'BBMkAGZjNmY6MDM3LWI3YTYtNERhZC05Y2FkLTgyZjcwZjE4OTI5ZQBGAAAAAAD8VQTDKKWNTY61gNKhnFzLBwBuCP8DF46ETJIEZ0XrTOaCAAAAAAENAABuCP8DF46ETJFEZ0EnTOaCAAFvdoJvAAA='; await graph.users.getById('user@tenant.onmicrosoft.com').events.getById(EVENT_ID).delete(); await graph.me.events.getById(EVENT_ID).delete(); Get Calendar for a Group \u00b6 import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/groups'; const calendar = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').calendar(); Get Events for a Group \u00b6 import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/groups'; // You can do one of const events = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').calendar.events(); // or const events = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').events(); Get Calendar View \u00b6 Added in 2.0.7 Gets the events in a calendar during a specified date range. import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; // basic request, note need to invoke the returned queryable const view = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView(\"2020-01-01\", \"2020-03-01\")(); // you can use select, top, etc to filter your returned results const view2 = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView(\"2020-01-01\", \"2020-03-01\").select(\"subject\").top(3)(); // you can specify times along with the dates const view3 = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView(\"2020-01-01T19:00:00-08:00\", \"2020-03-01T19:00:00-08:00\")(); const view4 = await graph.me.calendarView(\"2020-01-01\", \"2020-03-01\")();","title":"calendars"},{"location":"graph/calendars/#pnpgraphcalendars","text":"Calendars exist in Outlook and can belong to either a user or group. With @pnp/graph@<=2.0.6 , only events for a user and group's default calendar could be fetched/created/updated. In versions 2.0.7 and up, all calendars and their events can be fetched. More information can be found in the official Graph documentation: Calendar Resource Type Event Resource Type","title":"@pnp/graph/calendars"},{"location":"graph/calendars/#icalendar-icalendars","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/calendars\"; Preset: All import { graph } from \"@pnp/graph/presets/all\";","title":"ICalendar, ICalendars"},{"location":"graph/calendars/#get-all-calendars-for-a-user","text":"import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const calendars = await graph.users.getById('user@tenant.onmicrosoft.com').calendars(); const myCalendars = await graph.me.calendars();","title":"Get All Calendars For a User"},{"location":"graph/calendars/#get-a-specific-calendar-for-a-user","text":"import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const CALENDAR_ID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA-xUBMMopY1NkrWA0qGcXHsHAG4I-wMXjoRMkgRnRetM5oIAAAIBBgAAAG4I-wMXjoRMkgRnRetM5oIAAAIsYgAAAA=='; const calendar = await graph.users.getById('user@tenant.onmicrosoft.com').calendars.getById(CALENDAR_ID)(); const myCalendar = await graph.me.calendars.getById(CALENDAR_ID)();","title":"Get a Specific Calendar For a User"},{"location":"graph/calendars/#get-a-users-default-calendar","text":"import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const calendar = await graph.users.getById('user@tenant.onmicrosoft.com').calendar(); const myCalendar = await graph.me.calendar();","title":"Get a User's Default Calendar"},{"location":"graph/calendars/#get-events-for-a-users-default-calendar","text":"import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; // You can get the default calendar events const events = await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events(); // or get all events for the user const events = await graph.users.getById('user@tenant.onmicrosoft.com').events(); // You can get my default calendar events const events = await graph.me.calendar.events(); // or get all events for me const events = await graph.me.events();","title":"Get Events For a User's Default Calendar"},{"location":"graph/calendars/#get-events-by-id","text":"You can use .events.getByID to search through all the events in all calendars or narrow the request to a specific calendar. import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const CalendarID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA=='; const EventID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA-xUBMMopY1NkrWA0qGcXHsHAG4I-wMXjoRMkgRnRetM5oIAAAIBBgAAAG4I-wMXjoRMkgRnRetM5oIAAAIsYgAAAA=='; // Get events by ID const event = await graph.users.getById('user@tenant.onmicrosoft.com').events.getByID(EventID); const events = await graph.me.events.getByID(EventID); // Get an event by ID from a specific calendar const event = await graph.users.getById('user@tenant.onmicrosoft.com').calendars.getByID(CalendarID).events.getByID(EventID); const events = await graph.me.calendars.getByID(CalendarID).events.getByID(EventID);","title":"Get Events By ID"},{"location":"graph/calendars/#create-events","text":"This will work on any IEvents objects (e.g. anything accessed using an events key). import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events.add( { \"subject\": \"Let's go for lunch\", \"body\": { \"contentType\": \"HTML\", \"content\": \"Does late morning work for you?\" }, \"start\": { \"dateTime\": \"2017-04-15T12:00:00\", \"timeZone\": \"Pacific Standard Time\" }, \"end\": { \"dateTime\": \"2017-04-15T14:00:00\", \"timeZone\": \"Pacific Standard Time\" }, \"location\":{ \"displayName\":\"Harry's Bar\" }, \"attendees\": [ { \"emailAddress\": { \"address\":\"samanthab@contoso.onmicrosoft.com\", \"name\": \"Samantha Booth\" }, \"type\": \"required\" } ] });","title":"Create Events"},{"location":"graph/calendars/#update-events","text":"This will work on any IEvents objects (e.g. anything accessed using an events key). import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const EVENT_ID = 'BBMkAGZjNmY6MDM3LWI3YTYtNERhZC05Y2FkLTgyZjcwZjE4OTI5ZQBGAAAAAAD8VQTDKKWNTY61gNKhnFzLBwBuCP8DF46ETJIEZ0XrTOaCAAAAAAENAABuCP8DF46ETJFEZ0EnTOaCAAFvdoJvAAA='; await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events.getById(EVENT_ID).update({ reminderMinutesBeforeStart: 99, });","title":"Update Events"},{"location":"graph/calendars/#delete-event","text":"This will work on any IEvents objects (e.g. anything accessed using an events key). import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const EVENT_ID = 'BBMkAGZjNmY6MDM3LWI3YTYtNERhZC05Y2FkLTgyZjcwZjE4OTI5ZQBGAAAAAAD8VQTDKKWNTY61gNKhnFzLBwBuCP8DF46ETJIEZ0XrTOaCAAAAAAENAABuCP8DF46ETJFEZ0EnTOaCAAFvdoJvAAA='; await graph.users.getById('user@tenant.onmicrosoft.com').events.getById(EVENT_ID).delete(); await graph.me.events.getById(EVENT_ID).delete();","title":"Delete Event"},{"location":"graph/calendars/#get-calendar-for-a-group","text":"import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/groups'; const calendar = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').calendar();","title":"Get Calendar for a Group"},{"location":"graph/calendars/#get-events-for-a-group","text":"import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/groups'; // You can do one of const events = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').calendar.events(); // or const events = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').events();","title":"Get Events for a Group"},{"location":"graph/calendars/#get-calendar-view","text":"Added in 2.0.7 Gets the events in a calendar during a specified date range. import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; // basic request, note need to invoke the returned queryable const view = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView(\"2020-01-01\", \"2020-03-01\")(); // you can use select, top, etc to filter your returned results const view2 = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView(\"2020-01-01\", \"2020-03-01\").select(\"subject\").top(3)(); // you can specify times along with the dates const view3 = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView(\"2020-01-01T19:00:00-08:00\", \"2020-03-01T19:00:00-08:00\")(); const view4 = await graph.me.calendarView(\"2020-01-01\", \"2020-03-01\")();","title":"Get Calendar View"},{"location":"graph/contacts/","text":"@pnp/graph/contacts \u00b6 The ability to manage contacts and folders in Outlook is a capability introduced in version 1.2.2 of @pnp/graph. Through the methods described you can add and edit both contacts and folders in a users Outlook. More information can be found in the official Graph documentation: Contact Resource Type IContact, IContacts, IContactFolder, IContactFolders \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/contacts\"; Preset: All import { graph } from \"@pnp/graph/presets/all\"; Set up notes \u00b6 To make user calls you can use getById where the id is the users email address. Contact ID, Folder ID, and Parent Folder ID use the following format \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\" Get all of the Contacts \u00b6 Gets a list of all the contacts for the user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contacts = await graph.users.getById('user@tenant.onmicrosoft.com').contacts(); const contacts2 = await graph.me.contacts(); Get Contact by Id \u00b6 Gets a specific contact by ID for the user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"; const contact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID)(); const contact2 = await graph.me.contacts.getById(contactID)(); Add a new Contact \u00b6 Adds a new contact for the user. import { graph } from \"@pnp/graph\"; import { EmailAddress } from \"@microsoft/microsoft-graph-types\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const addedContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']); const addedContact2 = await graph.me.contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']); Update a Contact \u00b6 Updates a specific contact by ID for teh designated user import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"; const updContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID).update({birthday: \"1986-05-30\" }); const updContact2 = await graph.me.contacts.getById(contactID).update({birthday: \"1986-05-30\" }); Delete a Contact \u00b6 Delete a contact from the list of contacts for a user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"; const delContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID).delete(); const delContact2 = await graph.me.contacts.getById(contactID).delete(); Get all of the Contact Folders \u00b6 Get all the folders for the designated user's contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactFolders = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders(); const contactFolders2 = await graph.me.contactFolders(); Get Contact Folder by Id \u00b6 Get a contact folder by ID for the specified user import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const contactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID)(); const contactFolder2 = await graph.me.contactFolders.getById(folderID)(); Add a new Contact Folder \u00b6 Add a new folder in the users contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const parentFolderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAAAAAEOAAA=\"; const addedContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.add(\"New Folder\", parentFolderID); const addedContactFolder2 = await graph.me.contactFolders.add(\"New Folder\", parentFolderID); Update a Contact Folder \u00b6 Update an existing folder in the users contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const updContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).update({displayName: \"Updated Folder\" }); const updContactFolder2 = await graph.me.contactFolders.getById(folderID).update({displayName: \"Updated Folder\" }); Delete a Contact Folder \u00b6 Delete a folder from the users contacts list. Deleting a folder deletes the contacts in that folder. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const delContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).delete(); const delContactFolder2 = await graph.me.contactFolders.getById(folderID).delete(); Get all of the Contacts from the Contact Folder \u00b6 Get all the contacts in a folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const contactsInContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).contacts(); const contactsInContactFolder2 = await graph.me.contactFolders.getById(folderID).contacts(); Get Child Folders of the Contact Folder \u00b6 Get child folders from contact folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const childFolders = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders(); const childFolders2 = await graph.me.contactFolders.getById(folderID).childFolders(); Add a new Child Folder \u00b6 Add a new child folder to a contact folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const addedChildFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.add(\"Sub Folder\", folderID); const addedChildFolder2 = await graph.me.contactFolders.getById(folderID).childFolders.add(\"Sub Folder\", folderID); Get Child Folder by Id \u00b6 Get child folder by ID from user contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const subFolderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqIZAAA=\"; const childFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.getById(subFolderID)(); const childFolder2 = await graph.me.contactFolders.getById(folderID).childFolders.getById(subFolderID)(); Add Contact in Child Folder of Contact Folder \u00b6 Add a new contact to a child folder import { graph } from \"@pnp/graph\"; import { EmailAddress } from \"./@microsoft/microsoft-graph-types\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const subFolderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqIZAAA=\"; const addedContact = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.getById(subFolderID).contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']); const addedContact2 = await graph.me.contactFolders.getById(folderID).childFolders.getById(subFolderID).contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);","title":"contacts"},{"location":"graph/contacts/#pnpgraphcontacts","text":"The ability to manage contacts and folders in Outlook is a capability introduced in version 1.2.2 of @pnp/graph. Through the methods described you can add and edit both contacts and folders in a users Outlook. More information can be found in the official Graph documentation: Contact Resource Type","title":"@pnp/graph/contacts"},{"location":"graph/contacts/#icontact-icontacts-icontactfolder-icontactfolders","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/contacts\"; Preset: All import { graph } from \"@pnp/graph/presets/all\";","title":"IContact, IContacts, IContactFolder, IContactFolders"},{"location":"graph/contacts/#set-up-notes","text":"To make user calls you can use getById where the id is the users email address. Contact ID, Folder ID, and Parent Folder ID use the following format \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"","title":"Set up notes"},{"location":"graph/contacts/#get-all-of-the-contacts","text":"Gets a list of all the contacts for the user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contacts = await graph.users.getById('user@tenant.onmicrosoft.com').contacts(); const contacts2 = await graph.me.contacts();","title":"Get all of the Contacts"},{"location":"graph/contacts/#get-contact-by-id","text":"Gets a specific contact by ID for the user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"; const contact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID)(); const contact2 = await graph.me.contacts.getById(contactID)();","title":"Get Contact by Id"},{"location":"graph/contacts/#add-a-new-contact","text":"Adds a new contact for the user. import { graph } from \"@pnp/graph\"; import { EmailAddress } from \"@microsoft/microsoft-graph-types\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const addedContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']); const addedContact2 = await graph.me.contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);","title":"Add a new Contact"},{"location":"graph/contacts/#update-a-contact","text":"Updates a specific contact by ID for teh designated user import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"; const updContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID).update({birthday: \"1986-05-30\" }); const updContact2 = await graph.me.contacts.getById(contactID).update({birthday: \"1986-05-30\" });","title":"Update a Contact"},{"location":"graph/contacts/#delete-a-contact","text":"Delete a contact from the list of contacts for a user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"; const delContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID).delete(); const delContact2 = await graph.me.contacts.getById(contactID).delete();","title":"Delete a Contact"},{"location":"graph/contacts/#get-all-of-the-contact-folders","text":"Get all the folders for the designated user's contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactFolders = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders(); const contactFolders2 = await graph.me.contactFolders();","title":"Get all of the Contact Folders"},{"location":"graph/contacts/#get-contact-folder-by-id","text":"Get a contact folder by ID for the specified user import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const contactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID)(); const contactFolder2 = await graph.me.contactFolders.getById(folderID)();","title":"Get Contact Folder by Id"},{"location":"graph/contacts/#add-a-new-contact-folder","text":"Add a new folder in the users contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const parentFolderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAAAAAEOAAA=\"; const addedContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.add(\"New Folder\", parentFolderID); const addedContactFolder2 = await graph.me.contactFolders.add(\"New Folder\", parentFolderID);","title":"Add a new Contact Folder"},{"location":"graph/contacts/#update-a-contact-folder","text":"Update an existing folder in the users contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const updContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).update({displayName: \"Updated Folder\" }); const updContactFolder2 = await graph.me.contactFolders.getById(folderID).update({displayName: \"Updated Folder\" });","title":"Update a Contact Folder"},{"location":"graph/contacts/#delete-a-contact-folder","text":"Delete a folder from the users contacts list. Deleting a folder deletes the contacts in that folder. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const delContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).delete(); const delContactFolder2 = await graph.me.contactFolders.getById(folderID).delete();","title":"Delete a Contact Folder"},{"location":"graph/contacts/#get-all-of-the-contacts-from-the-contact-folder","text":"Get all the contacts in a folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const contactsInContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).contacts(); const contactsInContactFolder2 = await graph.me.contactFolders.getById(folderID).contacts();","title":"Get all of the Contacts from the Contact Folder"},{"location":"graph/contacts/#get-child-folders-of-the-contact-folder","text":"Get child folders from contact folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const childFolders = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders(); const childFolders2 = await graph.me.contactFolders.getById(folderID).childFolders();","title":"Get Child Folders of the Contact Folder"},{"location":"graph/contacts/#add-a-new-child-folder","text":"Add a new child folder to a contact folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const addedChildFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.add(\"Sub Folder\", folderID); const addedChildFolder2 = await graph.me.contactFolders.getById(folderID).childFolders.add(\"Sub Folder\", folderID);","title":"Add a new Child Folder"},{"location":"graph/contacts/#get-child-folder-by-id","text":"Get child folder by ID from user contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const subFolderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqIZAAA=\"; const childFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.getById(subFolderID)(); const childFolder2 = await graph.me.contactFolders.getById(folderID).childFolders.getById(subFolderID)();","title":"Get Child Folder by Id"},{"location":"graph/contacts/#add-contact-in-child-folder-of-contact-folder","text":"Add a new contact to a child folder import { graph } from \"@pnp/graph\"; import { EmailAddress } from \"./@microsoft/microsoft-graph-types\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const subFolderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqIZAAA=\"; const addedContact = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.getById(subFolderID).contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']); const addedContact2 = await graph.me.contactFolders.getById(folderID).childFolders.getById(subFolderID).contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);","title":"Add Contact in Child Folder of Contact Folder"},{"location":"graph/directoryobjects/","text":"@pnp/graph/directoryObjects \u00b6 Represents an Azure Active Directory object. The directoryObject type is the base type for many other directory entity types. More information can be found in the official Graph documentation: DirectoryObject Resource Type IDirectoryObject, IDirectoryObjects \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/directory-objects\"; Preset: All import { graph } from \"@pnp/sp/presets/all\"; The groups and directory roles for the user \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" const memberOf = await graph.users.getById('user@tenant.onmicrosoft.com').memberOf(); const memberOf2 = await graph.me.memberOf(); Return all the groups the user, group or directoryObject is a member of. Add true parameter to return only security enabled groups \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/groups\" const memberGroups = await graph.users.getById('user@tenant.onmicrosoft.com').getMemberGroups(); const memberGroups2 = await graph.me.getMemberGroups(); // Returns only security enabled groups const memberGroups3 = await graph.me.getMemberGroups(true); const memberGroups4 = await graph.groups.getById('user@tenant.onmicrosoft.com').getMemberGroups(); Returns all the groups, administrative units and directory roles that a user, group, or directory object is a member of. Add true parameter to return only security enabled groups \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/groups\"; const memberObjects = await graph.users.getById('user@tenant.onmicrosoft.com').getMemberObjects(); const memberObjects2 = await graph.me.getMemberObjects(); // Returns only security enabled groups const memberObjects3 = await graph.me.getMemberObjects(true); const memberObjects4 = await graph.groups.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').getMemberObjects(); Check for membership in a specified list of groups \u00b6 And returns from that list those groups of which the specified user, group, or directory object is a member import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/groups\"; const checkedMembers = await graph.users.getById('user@tenant.onmicrosoft.com').checkMemberGroups([\"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\",\"2001bb09-1d46-40a6-8176-7bb867fb75aa\"]); const checkedMembers2 = await graph.me.checkMemberGroups([\"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\",\"2001bb09-1d46-40a6-8176-7bb867fb75aa\"]); const checkedMembers3 = await graph.groups.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').checkMemberGroups([\"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\",\"2001bb09-1d46-40a6-8176-7bb867fb75aa\"]); Get directoryObject by Id \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/directory-objects\"; const dirObject = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26'); Delete directoryObject \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/directory-objects\"; const deleted = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').delete()","title":"directory objects"},{"location":"graph/directoryobjects/#pnpgraphdirectoryobjects","text":"Represents an Azure Active Directory object. The directoryObject type is the base type for many other directory entity types. More information can be found in the official Graph documentation: DirectoryObject Resource Type","title":"@pnp/graph/directoryObjects"},{"location":"graph/directoryobjects/#idirectoryobject-idirectoryobjects","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/directory-objects\"; Preset: All import { graph } from \"@pnp/sp/presets/all\";","title":"IDirectoryObject, IDirectoryObjects"},{"location":"graph/directoryobjects/#the-groups-and-directory-roles-for-the-user","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" const memberOf = await graph.users.getById('user@tenant.onmicrosoft.com').memberOf(); const memberOf2 = await graph.me.memberOf();","title":"The groups and directory roles for the user"},{"location":"graph/directoryobjects/#return-all-the-groups-the-user-group-or-directoryobject-is-a-member-of-add-true-parameter-to-return-only-security-enabled-groups","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/groups\" const memberGroups = await graph.users.getById('user@tenant.onmicrosoft.com').getMemberGroups(); const memberGroups2 = await graph.me.getMemberGroups(); // Returns only security enabled groups const memberGroups3 = await graph.me.getMemberGroups(true); const memberGroups4 = await graph.groups.getById('user@tenant.onmicrosoft.com').getMemberGroups();","title":"Return all the groups the user, group or directoryObject is a member of. Add true parameter to return only security enabled groups"},{"location":"graph/directoryobjects/#returns-all-the-groups-administrative-units-and-directory-roles-that-a-user-group-or-directory-object-is-a-member-of-add-true-parameter-to-return-only-security-enabled-groups","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/groups\"; const memberObjects = await graph.users.getById('user@tenant.onmicrosoft.com').getMemberObjects(); const memberObjects2 = await graph.me.getMemberObjects(); // Returns only security enabled groups const memberObjects3 = await graph.me.getMemberObjects(true); const memberObjects4 = await graph.groups.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').getMemberObjects();","title":"Returns all the groups, administrative units and directory roles that a user, group, or directory object is a member of. Add true parameter to return only security enabled groups"},{"location":"graph/directoryobjects/#check-for-membership-in-a-specified-list-of-groups","text":"And returns from that list those groups of which the specified user, group, or directory object is a member import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/groups\"; const checkedMembers = await graph.users.getById('user@tenant.onmicrosoft.com').checkMemberGroups([\"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\",\"2001bb09-1d46-40a6-8176-7bb867fb75aa\"]); const checkedMembers2 = await graph.me.checkMemberGroups([\"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\",\"2001bb09-1d46-40a6-8176-7bb867fb75aa\"]); const checkedMembers3 = await graph.groups.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').checkMemberGroups([\"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\",\"2001bb09-1d46-40a6-8176-7bb867fb75aa\"]);","title":"Check for membership in a specified list of groups"},{"location":"graph/directoryobjects/#get-directoryobject-by-id","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/directory-objects\"; const dirObject = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26');","title":"Get directoryObject by Id"},{"location":"graph/directoryobjects/#delete-directoryobject","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/directory-objects\"; const deleted = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').delete()","title":"Delete directoryObject"},{"location":"graph/groups/","text":"@pnp/graph/groups \u00b6 Groups are collections of users and other principals who share access to resources in Microsoft services or in your app. All group-related operations in Microsoft Graph require administrator consent. Note: Groups can only be created through work or school accounts. Personal Microsoft accounts don't support groups. You can learn more about Microsoft Graph Groups by reading the Official Microsoft Graph Documentation . IGroup, IGroups \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {Group, GroupType, Groups, IGroup, IGroupAddResult, IGroups} from \"@pnp/graph/groups\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; Preset: All import { graph, Group, GroupType, Groups, IGroup, IGroupAddResult, IGroups } from \"@pnp/graph/presets/all\"; Add a Group \u00b6 Add a new group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; import { GroupType } from '@pnp/graph/groups'; const groupAddResult = await graph.groups.add(\"GroupName\", \"Mail_NickName\", GroupType.Office365); const group = await groupAddResult.group(); Delete a Group \u00b6 Deletes an existing group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").delete(); Update Group Properties \u00b6 Updates an existing group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").update({ displayName: newName, propertyName: updatedValue}); Add favorite \u00b6 Add the group to the list of the current user's favorite groups. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").addFavorite(); Remove favorite \u00b6 Remove the group from the list of the current user's favorite groups. Supported for Office 365 Groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").removeFavorite(); Reset Unseen Count \u00b6 Reset the unseenCount of all the posts that the current user has not seen since their last visit. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").resetUnseenCount(); Subscribe By Mail \u00b6 Calling this method will enable the current user to receive email notifications for this group, about new posts, events, and files in that group. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").subscribeByMail(); Unsubscribe By Mail \u00b6 Calling this method will prevent the current user from receiving email notifications for this group about new posts, events, and files in that group. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").unsubscribeByMail(); Get Calendar View \u00b6 Get the occurrences, exceptions, and single instances of events in a calendar view defined by a time range, from the default calendar of a group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; const startDate = new Date(\"2020-04-01\"); const endDate = new Date(\"2020-03-01\"); const events = graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").getCalendarView(startDate, endDate); Group Photo Operations \u00b6 See Photos Get the Team Site for a Group \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; import \"@pnp/graph/sites/group\"; const teamSite = await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").sites.root(); const url = teamSite.webUrl","title":"groups"},{"location":"graph/groups/#pnpgraphgroups","text":"Groups are collections of users and other principals who share access to resources in Microsoft services or in your app. All group-related operations in Microsoft Graph require administrator consent. Note: Groups can only be created through work or school accounts. Personal Microsoft accounts don't support groups. You can learn more about Microsoft Graph Groups by reading the Official Microsoft Graph Documentation .","title":"@pnp/graph/groups"},{"location":"graph/groups/#igroup-igroups","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {Group, GroupType, Groups, IGroup, IGroupAddResult, IGroups} from \"@pnp/graph/groups\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; Preset: All import { graph, Group, GroupType, Groups, IGroup, IGroupAddResult, IGroups } from \"@pnp/graph/presets/all\";","title":"IGroup, IGroups"},{"location":"graph/groups/#add-a-group","text":"Add a new group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; import { GroupType } from '@pnp/graph/groups'; const groupAddResult = await graph.groups.add(\"GroupName\", \"Mail_NickName\", GroupType.Office365); const group = await groupAddResult.group();","title":"Add a Group"},{"location":"graph/groups/#delete-a-group","text":"Deletes an existing group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").delete();","title":"Delete a Group"},{"location":"graph/groups/#update-group-properties","text":"Updates an existing group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").update({ displayName: newName, propertyName: updatedValue});","title":"Update Group Properties"},{"location":"graph/groups/#add-favorite","text":"Add the group to the list of the current user's favorite groups. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").addFavorite();","title":"Add favorite"},{"location":"graph/groups/#remove-favorite","text":"Remove the group from the list of the current user's favorite groups. Supported for Office 365 Groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").removeFavorite();","title":"Remove favorite"},{"location":"graph/groups/#reset-unseen-count","text":"Reset the unseenCount of all the posts that the current user has not seen since their last visit. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").resetUnseenCount();","title":"Reset Unseen Count"},{"location":"graph/groups/#subscribe-by-mail","text":"Calling this method will enable the current user to receive email notifications for this group, about new posts, events, and files in that group. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").subscribeByMail();","title":"Subscribe By Mail"},{"location":"graph/groups/#unsubscribe-by-mail","text":"Calling this method will prevent the current user from receiving email notifications for this group about new posts, events, and files in that group. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").unsubscribeByMail();","title":"Unsubscribe By Mail"},{"location":"graph/groups/#get-calendar-view","text":"Get the occurrences, exceptions, and single instances of events in a calendar view defined by a time range, from the default calendar of a group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; const startDate = new Date(\"2020-04-01\"); const endDate = new Date(\"2020-03-01\"); const events = graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").getCalendarView(startDate, endDate);","title":"Get Calendar View"},{"location":"graph/groups/#group-photo-operations","text":"See Photos","title":"Group Photo Operations"},{"location":"graph/groups/#get-the-team-site-for-a-group","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; import \"@pnp/graph/sites/group\"; const teamSite = await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").sites.root(); const url = teamSite.webUrl","title":"Get the Team Site for a Group"},{"location":"graph/insights/","text":"@pnp/graph/insights \u00b6 This module helps you get Insights in form of Trending , Used and Shared . The results are based on relationships calculated using advanced analytics and machine learning techniques. IInsights \u00b6 Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; Preset: All import \"@pnp/graph/presets/all\"; Get all Trending documents \u00b6 Returns documents from OneDrive and SharePoint sites trending around a user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const trending = await graph.me.insights.trending() const trending = await graph.users.getById(\"userId\").insights.trending() Get a Trending document by Id \u00b6 Using the getById method to get a trending document by Id. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const trendingDoc = await graph.me.insights.trending.getById('Id')() const trendingDoc = await graph.users.getById(\"userId\").insights.trending.getById('Id')() Get the resource from Trending document \u00b6 Using the resources method to get the resource from a trending document. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const resource = await graph.me.insights.trending.getById('Id').resource() const resource = await graph.users.getById(\"userId\").insights.trending.getById('Id').resource() Get all Used documents \u00b6 Returns documents viewed and modified by a user. Includes documents the user used in OneDrive for Business, SharePoint, opened as email attachments, and as link attachments from sources like Box, DropBox and Google Drive. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const used = await graph.me.insights.used() const used = await graph.users.getById(\"userId\").insights.used() Get a Used document by Id \u00b6 Using the getById method to get a used document by Id. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const usedDoc = await graph.me.insights.used.getById('Id')() const usedDoc = await graph.users.getById(\"userId\").insights.used.getById('Id')() Get the resource from Used document \u00b6 Using the resources method to get the resource from a used document. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const resource = await graph.me.insights.used.getById('Id').resource() const resource = await graph.users.getById(\"userId\").insights.used.getById('Id').resource() Get all Shared documents \u00b6 Returns documents shared with a user. Documents can be shared as email attachments or as OneDrive for Business links sent in emails. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const shared = await graph.me.insights.shared() const shared = await graph.users.getById(\"userId\").insights.shared() Get a Shared document by Id \u00b6 Using the getById method to get a shared document by Id. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const sharedDoc = await graph.me.insights.shared.getById('Id')() const sharedDoc = await graph.users.getById(\"userId\").insights.shared.getById('Id')() Get the resource from a Shared document \u00b6 Using the resources method to get the resource from a shared document. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const resource = await graph.me.insights.shared.getById('Id').resource() const resource = await graph.users.getById(\"userId\").insights.shared.getById('Id').resource()","title":"insights"},{"location":"graph/insights/#pnpgraphinsights","text":"This module helps you get Insights in form of Trending , Used and Shared . The results are based on relationships calculated using advanced analytics and machine learning techniques.","title":"@pnp/graph/insights"},{"location":"graph/insights/#iinsights","text":"Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; Preset: All import \"@pnp/graph/presets/all\";","title":"IInsights"},{"location":"graph/insights/#get-all-trending-documents","text":"Returns documents from OneDrive and SharePoint sites trending around a user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const trending = await graph.me.insights.trending() const trending = await graph.users.getById(\"userId\").insights.trending()","title":"Get all Trending documents"},{"location":"graph/insights/#get-a-trending-document-by-id","text":"Using the getById method to get a trending document by Id. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const trendingDoc = await graph.me.insights.trending.getById('Id')() const trendingDoc = await graph.users.getById(\"userId\").insights.trending.getById('Id')()","title":"Get a Trending document by Id"},{"location":"graph/insights/#get-the-resource-from-trending-document","text":"Using the resources method to get the resource from a trending document. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const resource = await graph.me.insights.trending.getById('Id').resource() const resource = await graph.users.getById(\"userId\").insights.trending.getById('Id').resource()","title":"Get the resource from Trending document"},{"location":"graph/insights/#get-all-used-documents","text":"Returns documents viewed and modified by a user. Includes documents the user used in OneDrive for Business, SharePoint, opened as email attachments, and as link attachments from sources like Box, DropBox and Google Drive. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const used = await graph.me.insights.used() const used = await graph.users.getById(\"userId\").insights.used()","title":"Get all Used documents"},{"location":"graph/insights/#get-a-used-document-by-id","text":"Using the getById method to get a used document by Id. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const usedDoc = await graph.me.insights.used.getById('Id')() const usedDoc = await graph.users.getById(\"userId\").insights.used.getById('Id')()","title":"Get a Used document by Id"},{"location":"graph/insights/#get-the-resource-from-used-document","text":"Using the resources method to get the resource from a used document. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const resource = await graph.me.insights.used.getById('Id').resource() const resource = await graph.users.getById(\"userId\").insights.used.getById('Id').resource()","title":"Get the resource from Used document"},{"location":"graph/insights/#get-all-shared-documents","text":"Returns documents shared with a user. Documents can be shared as email attachments or as OneDrive for Business links sent in emails. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const shared = await graph.me.insights.shared() const shared = await graph.users.getById(\"userId\").insights.shared()","title":"Get all Shared documents"},{"location":"graph/insights/#get-a-shared-document-by-id","text":"Using the getById method to get a shared document by Id. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const sharedDoc = await graph.me.insights.shared.getById('Id')() const sharedDoc = await graph.users.getById(\"userId\").insights.shared.getById('Id')()","title":"Get a Shared document by Id"},{"location":"graph/insights/#get-the-resource-from-a-shared-document","text":"Using the resources method to get the resource from a shared document. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const resource = await graph.me.insights.shared.getById('Id').resource() const resource = await graph.users.getById(\"userId\").insights.shared.getById('Id').resource()","title":"Get the resource from a Shared document"},{"location":"graph/invitations/","text":"@pnp/graph/invitations \u00b6 The ability invite an external user via the invitation manager IInvitations \u00b6 Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/invitations\"; Preset: All import \"@pnp/graph/presets/all\"; Create Invitation \u00b6 Using the invitations.create() you can create an Invitation. We need the email address of the user being invited and the URL user should be redirected to once the invitation is redeemed (redirect URL). import { graph } from \"@pnp/graph\"; import \"@pnp/graph/invitations\" const invitationResult = await graph.invitations.create('external.user@email-address.com', 'https://tenant.sharepoint.com/sites/redirecturi');","title":"invitations"},{"location":"graph/invitations/#pnpgraphinvitations","text":"The ability invite an external user via the invitation manager","title":"@pnp/graph/invitations"},{"location":"graph/invitations/#iinvitations","text":"Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/invitations\"; Preset: All import \"@pnp/graph/presets/all\";","title":"IInvitations"},{"location":"graph/invitations/#create-invitation","text":"Using the invitations.create() you can create an Invitation. We need the email address of the user being invited and the URL user should be redirected to once the invitation is redeemed (redirect URL). import { graph } from \"@pnp/graph\"; import \"@pnp/graph/invitations\" const invitationResult = await graph.invitations.create('external.user@email-address.com', 'https://tenant.sharepoint.com/sites/redirecturi');","title":"Create Invitation"},{"location":"graph/onedrive/","text":"@pnp/graph/onedrive \u00b6 The ability to manage drives and drive items in Onedrive is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described you can manage drives and drive items in Onedrive. IInvitations \u00b6 Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; Preset: All import \"@pnp/graph/presets/all\"; Get the default drive \u00b6 Using the drive() you can get the default drive from Onedrive import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const drives = await graph.users.getById('user@tenant.onmicrosoft.com').drives(); const drives = await graph.me.drives(); Get all of the drives \u00b6 Using the drives() you can get the users available drives from Onedrive import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const drives = await graph.users.getById('user@tenant.onmicrosoft.com').drives(); const drives = await graph.me.drives(); Get drive by Id \u00b6 Using the drives.getById() you can get one of the available drives in Outlook import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const drive = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId'); const drive = await graph.me.drives.getById('driveId'); Get the associated list of a drive \u00b6 Using the list() you get the associated list import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const list = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').list(); const list = await graph.me.drives.getById('driveId').list(); Get the recent files \u00b6 Using the recent() you get the recent files import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const files = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').recent(); const files = await graph.me.drives.getById('driveId').recent(); Get the files shared with me \u00b6 Using the sharedWithMe() you get the files shared with the user import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const shared = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').sharedWithMe(); const shared = await graph.me.drives.getById('driveId').sharedWithMe(); Get the Root folder \u00b6 Using the root() you get the root folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const root = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root(); const root = await graph.me.drives.getById('driveId').root(); Get the Children \u00b6 Using the children() you get the children import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const rootChildren = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root.children(); const rootChildren = await graph.me.drives.getById('driveId').root.children(); const itemChildren = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').children(); const itemChildren = await graph.me.drives.getById('driveId').root.items.getById('itemId').children(); Add folder or item \u00b6 Using the add you can add a folder or an item import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; import { DriveItem as IDriveItem } from \"@microsoft/microsoft-graph-types\"; const addFolder = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root.children.add('New Folder', {folder: {}}); const addFolder = await graph.me.drives.getById('driveId').root.children.add('New Folder', {folder: {}}); Search items \u00b6 Using the search() you can search for items, and optionally select properties import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const search = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId')root.search('queryText')(); const search = await graph.me.drives.getById('driveId')root.search('queryText')(); Get specific item in drive \u00b6 Using the items.getById() you can get a specific item from the current drive import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const item = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId'); const item = await graph.me.drives.getById('driveId').items.getById('itemId'); Get thumbnails \u00b6 Using the thumbnails() you get the thumbnails import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const thumbs = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').thumbnails(); const thumbs = await graph.me.drives.getById('driveId').items.getById('itemId').thumbnails(); Delete drive item \u00b6 Using the delete() you delete the current item import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const thumbs = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').delete(); const thumbs = await graph.me.drives.getById('driveId').items.getById('itemId').delete(); Update drive item \u00b6 Using the update() you update the current item import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const update = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').update({name: \"New Name\"}); const update = await graph.me.drives.getById('driveId').items.getById('itemId').update({name: \"New Name\"}); Move drive item \u00b6 Using the move() you move the current item, and optionally update it import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; // Requires a parentReference to the new folder location const move = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').move({ parentReference: { id: 'itemId'}}, {name: \"New Name\"}); const move = await graph.me.drives.getById('driveId').items.getById('itemId').move({ parentReference: { id: 'itemId'}}, {name: \"New Name\"});","title":"onedrive"},{"location":"graph/onedrive/#pnpgraphonedrive","text":"The ability to manage drives and drive items in Onedrive is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described you can manage drives and drive items in Onedrive.","title":"@pnp/graph/onedrive"},{"location":"graph/onedrive/#iinvitations","text":"Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; Preset: All import \"@pnp/graph/presets/all\";","title":"IInvitations"},{"location":"graph/onedrive/#get-the-default-drive","text":"Using the drive() you can get the default drive from Onedrive import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const drives = await graph.users.getById('user@tenant.onmicrosoft.com').drives(); const drives = await graph.me.drives();","title":"Get the default drive"},{"location":"graph/onedrive/#get-all-of-the-drives","text":"Using the drives() you can get the users available drives from Onedrive import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const drives = await graph.users.getById('user@tenant.onmicrosoft.com').drives(); const drives = await graph.me.drives();","title":"Get all of the drives"},{"location":"graph/onedrive/#get-drive-by-id","text":"Using the drives.getById() you can get one of the available drives in Outlook import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const drive = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId'); const drive = await graph.me.drives.getById('driveId');","title":"Get drive by Id"},{"location":"graph/onedrive/#get-the-associated-list-of-a-drive","text":"Using the list() you get the associated list import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const list = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').list(); const list = await graph.me.drives.getById('driveId').list();","title":"Get the associated list of a drive"},{"location":"graph/onedrive/#get-the-recent-files","text":"Using the recent() you get the recent files import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const files = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').recent(); const files = await graph.me.drives.getById('driveId').recent();","title":"Get the recent files"},{"location":"graph/onedrive/#get-the-files-shared-with-me","text":"Using the sharedWithMe() you get the files shared with the user import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const shared = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').sharedWithMe(); const shared = await graph.me.drives.getById('driveId').sharedWithMe();","title":"Get the files shared with me"},{"location":"graph/onedrive/#get-the-root-folder","text":"Using the root() you get the root folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const root = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root(); const root = await graph.me.drives.getById('driveId').root();","title":"Get the Root folder"},{"location":"graph/onedrive/#get-the-children","text":"Using the children() you get the children import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const rootChildren = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root.children(); const rootChildren = await graph.me.drives.getById('driveId').root.children(); const itemChildren = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').children(); const itemChildren = await graph.me.drives.getById('driveId').root.items.getById('itemId').children();","title":"Get the Children"},{"location":"graph/onedrive/#add-folder-or-item","text":"Using the add you can add a folder or an item import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; import { DriveItem as IDriveItem } from \"@microsoft/microsoft-graph-types\"; const addFolder = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root.children.add('New Folder', {folder: {}}); const addFolder = await graph.me.drives.getById('driveId').root.children.add('New Folder', {folder: {}});","title":"Add folder or item"},{"location":"graph/onedrive/#search-items","text":"Using the search() you can search for items, and optionally select properties import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const search = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId')root.search('queryText')(); const search = await graph.me.drives.getById('driveId')root.search('queryText')();","title":"Search items"},{"location":"graph/onedrive/#get-specific-item-in-drive","text":"Using the items.getById() you can get a specific item from the current drive import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const item = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId'); const item = await graph.me.drives.getById('driveId').items.getById('itemId');","title":"Get specific item in drive"},{"location":"graph/onedrive/#get-thumbnails","text":"Using the thumbnails() you get the thumbnails import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const thumbs = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').thumbnails(); const thumbs = await graph.me.drives.getById('driveId').items.getById('itemId').thumbnails();","title":"Get thumbnails"},{"location":"graph/onedrive/#delete-drive-item","text":"Using the delete() you delete the current item import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const thumbs = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').delete(); const thumbs = await graph.me.drives.getById('driveId').items.getById('itemId').delete();","title":"Delete drive item"},{"location":"graph/onedrive/#update-drive-item","text":"Using the update() you update the current item import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const update = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').update({name: \"New Name\"}); const update = await graph.me.drives.getById('driveId').items.getById('itemId').update({name: \"New Name\"});","title":"Update drive item"},{"location":"graph/onedrive/#move-drive-item","text":"Using the move() you move the current item, and optionally update it import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; // Requires a parentReference to the new folder location const move = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').move({ parentReference: { id: 'itemId'}}, {name: \"New Name\"}); const move = await graph.me.drives.getById('driveId').items.getById('itemId').move({ parentReference: { id: 'itemId'}}, {name: \"New Name\"});","title":"Move drive item"},{"location":"graph/outlook/","text":"@pnp/graph/outlook \u00b6 Represents the Outlook services available to a user. Currently, only interacting with categories is supported. You can learn more by reading the Official Microsoft Graph Documentation . IUsers, IUser, IPeople \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {Outlook, IOutlook, MasterCategories, IMasterCategories, OutlookCategory, IOutlookCategory} from \"@pnp/graph/outlook\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/outlook\"; Preset: All import { graph, Outlook, IOutlook, MasterCategories, IMasterCategories } from \"@pnp/graph/presets/all\"; Get All Categories User \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; // Delegated permissions const categories = await graph.me.outlook.masterCategories(); // Application permissions const categories = await graph.users.getById('{user id}').outlook.masterCategories(); Add Category User \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; // Delegated permissions await graph.me.outlook.masterCategories.add({ displayName: 'Newsletters', color: 'preset2' }); // Application permissions await graph.users.getById('{user id}').outlook.masterCategories.add({ displayName: 'Newsletters', color: 'preset2' }); Update Category \u00b6 Testing has shown that displayName cannot be updated. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; import { OutlookCategory } from \"@microsoft/microsoft-graph-types\"; const categoryUpdate: OutlookCategory = { color: \"preset5\" } // Delegated permissions const categories = await graph.me.outlook.masterCategories.getById('{category id}').update(categoryUpdate); // Application permissions const categories = await graph.users.getById('{user id}').outlook.masterCategories.getById('{category id}').update(categoryUpdate); Delete Category \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; // Delegated permissions const categories = await graph.me.outlook.masterCategories.getById('{category id}').delete(); // Application permissions const categories = await graph.users.getById('{user id}').outlook.masterCategories.getById('{category id}').delete();","title":"outlook"},{"location":"graph/outlook/#pnpgraphoutlook","text":"Represents the Outlook services available to a user. Currently, only interacting with categories is supported. You can learn more by reading the Official Microsoft Graph Documentation .","title":"@pnp/graph/outlook"},{"location":"graph/outlook/#iusers-iuser-ipeople","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {Outlook, IOutlook, MasterCategories, IMasterCategories, OutlookCategory, IOutlookCategory} from \"@pnp/graph/outlook\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/outlook\"; Preset: All import { graph, Outlook, IOutlook, MasterCategories, IMasterCategories } from \"@pnp/graph/presets/all\";","title":"IUsers, IUser, IPeople"},{"location":"graph/outlook/#get-all-categories-user","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; // Delegated permissions const categories = await graph.me.outlook.masterCategories(); // Application permissions const categories = await graph.users.getById('{user id}').outlook.masterCategories();","title":"Get All Categories User"},{"location":"graph/outlook/#add-category-user","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; // Delegated permissions await graph.me.outlook.masterCategories.add({ displayName: 'Newsletters', color: 'preset2' }); // Application permissions await graph.users.getById('{user id}').outlook.masterCategories.add({ displayName: 'Newsletters', color: 'preset2' });","title":"Add Category User"},{"location":"graph/outlook/#update-category","text":"Testing has shown that displayName cannot be updated. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; import { OutlookCategory } from \"@microsoft/microsoft-graph-types\"; const categoryUpdate: OutlookCategory = { color: \"preset5\" } // Delegated permissions const categories = await graph.me.outlook.masterCategories.getById('{category id}').update(categoryUpdate); // Application permissions const categories = await graph.users.getById('{user id}').outlook.masterCategories.getById('{category id}').update(categoryUpdate);","title":"Update Category"},{"location":"graph/outlook/#delete-category","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; // Delegated permissions const categories = await graph.me.outlook.masterCategories.getById('{category id}').delete(); // Application permissions const categories = await graph.users.getById('{user id}').outlook.masterCategories.getById('{category id}').delete();","title":"Delete Category"},{"location":"graph/photos/","text":"@pnp/graph/photos \u00b6 A profile photo of a user, group or an Outlook contact accessed from Exchange Online or Azure Active Directory (AAD). It's binary data not encoded in base-64. You can learn more about Microsoft Graph users by reading the Official Microsoft Graph Documentation . IPhoto \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {IPhoto, Photo} from \"@pnp/graph/photos\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/photos\"; Preset: All import { graph, IPhoto, Photo } from \"@pnp/sp/presets/all\"; Current User Photo \u00b6 This example shows the getBlob() endpoint, there is also a getBuffer() endpoint to support node.js import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const photoValue = await graph.me.photo.getBlob(); const url = window.URL || window.webkitURL; const blobUrl = url.createObjectURL(photoValue); document.getElementById(\"photoElement\").setAttribute(\"src\", blobUrl); Current Group Photo \u00b6 This example shows the getBlob() endpoint, there is also a getBuffer() endpoint to support node.js import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; import \"@pnp/graph/photos\"; const photoValue = await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").photo.getBlob(); const url = window.URL || window.webkitURL; const blobUrl = url.createObjectURL(photoValue); document.getElementById(\"photoElement\").setAttribute(\"src\", blobUrl); Set User Photo \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const input = document.getElementById(\"thefileinput\"); const file = input.files[0]; await graph.me.photo.setContent(file); Set Group Photo \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const input = document.getElementById(\"thefileinput\"); const file = input.files[0]; await graph.me.photo.setContent(file);","title":"photos"},{"location":"graph/photos/#pnpgraphphotos","text":"A profile photo of a user, group or an Outlook contact accessed from Exchange Online or Azure Active Directory (AAD). It's binary data not encoded in base-64. You can learn more about Microsoft Graph users by reading the Official Microsoft Graph Documentation .","title":"@pnp/graph/photos"},{"location":"graph/photos/#iphoto","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {IPhoto, Photo} from \"@pnp/graph/photos\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/photos\"; Preset: All import { graph, IPhoto, Photo } from \"@pnp/sp/presets/all\";","title":"IPhoto"},{"location":"graph/photos/#current-user-photo","text":"This example shows the getBlob() endpoint, there is also a getBuffer() endpoint to support node.js import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const photoValue = await graph.me.photo.getBlob(); const url = window.URL || window.webkitURL; const blobUrl = url.createObjectURL(photoValue); document.getElementById(\"photoElement\").setAttribute(\"src\", blobUrl);","title":"Current User Photo"},{"location":"graph/photos/#current-group-photo","text":"This example shows the getBlob() endpoint, there is also a getBuffer() endpoint to support node.js import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; import \"@pnp/graph/photos\"; const photoValue = await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").photo.getBlob(); const url = window.URL || window.webkitURL; const blobUrl = url.createObjectURL(photoValue); document.getElementById(\"photoElement\").setAttribute(\"src\", blobUrl);","title":"Current Group Photo"},{"location":"graph/photos/#set-user-photo","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const input = document.getElementById(\"thefileinput\"); const file = input.files[0]; await graph.me.photo.setContent(file);","title":"Set User Photo"},{"location":"graph/photos/#set-group-photo","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const input = document.getElementById(\"thefileinput\"); const file = input.files[0]; await graph.me.photo.setContent(file);","title":"Set Group Photo"},{"location":"graph/planner/","text":"@pnp/graph/planner \u00b6 The ability to manage plans and tasks in Planner is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described you can add, update and delete items in Planner. IInvitations \u00b6 Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\"; Preset: All import \"@pnp/graph/presets/all\"; Get Plans by Id \u00b6 Using the planner.plans.getById() you can get a specific Plan. Planner.plans is not an available endpoint, you need to get a specific Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const plan = await graph.planner.plans.getById('planId')(); Add new Plan \u00b6 Using the planner.plans.add() you can create a new Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const newPlan = await graph.planner.plans.add('groupObjectId', 'title'); Get Tasks in Plan \u00b6 Using the tasks() you can get the Tasks in a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const planTasks = await graph.planner.plans.getById('planId').tasks(); Get Buckets in Plan \u00b6 Using the buckets() you can get the Buckets in a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const planBuckets = await graph.planner.plans.getById('planId').buckets(); Get Details in Plan \u00b6 Using the details() you can get the details in a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const planDetails = await graph.planner.plans.getById('planId').details(); Delete Plan \u00b6 Using the delete() you can get delete a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const delPlan = await graph.planner.plans.getById('planId').delete('planEtag'); Update Plan \u00b6 Using the update() you can get update a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const updPlan = await graph.planner.plans.getById('planId').update({title: 'New Title', eTag: 'planEtag'}); Get Task by Id \u00b6 Using the planner.tasks.getById() you can get a specific Task. Planner.tasks is not an available endpoint, you need to get a specific Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const task = await graph.planner.tasks.getById('taskId')(); Add new Task \u00b6 Using the planner.tasks.add() you can create a new Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const newTask = await graph.planner.tasks.add('planId', 'title'); Get Details in Task \u00b6 Using the details() you can get the details in a Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const taskDetails = await graph.planner.tasks.getById('taskId').details(); Delete Task \u00b6 Using the delete() you can get delete a Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const delTask = await graph.planner.tasks.getById('taskId').delete('taskEtag'); Update Task \u00b6 Using the update() you can get update a Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const updTask = await graph.planner.tasks.getById('taskId').update({properties, eTag:'taskEtag'}); Get Buckets by Id \u00b6 Using the planner.buckets.getById() you can get a specific Bucket. planner.buckets is not an available endpoint, you need to get a specific Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const bucket = await graph.planner.buckets.getById('bucketId')(); Add new Bucket \u00b6 Using the planner.buckets.add() you can create a new Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const newBucket = await graph.planner.buckets.add('name', 'planId'); Update Bucket \u00b6 Using the update() you can get update a Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const updBucket = await graph.planner.buckets.getById('bucketId').update({name: \"Name\", eTag:'bucketEtag'}); Delete Bucket \u00b6 Using the delete() you can get delete a Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const delBucket = await graph.planner.buckets.getById('bucketId').delete(eTag:'bucketEtag'); Get Bucket Tasks \u00b6 Using the tasks() you can get Tasks in a Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const bucketTasks = await graph.planner.buckets.getById('bucketId').tasks();","title":"planner"},{"location":"graph/planner/#pnpgraphplanner","text":"The ability to manage plans and tasks in Planner is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described you can add, update and delete items in Planner.","title":"@pnp/graph/planner"},{"location":"graph/planner/#iinvitations","text":"Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\"; Preset: All import \"@pnp/graph/presets/all\";","title":"IInvitations"},{"location":"graph/planner/#get-plans-by-id","text":"Using the planner.plans.getById() you can get a specific Plan. Planner.plans is not an available endpoint, you need to get a specific Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const plan = await graph.planner.plans.getById('planId')();","title":"Get Plans by Id"},{"location":"graph/planner/#add-new-plan","text":"Using the planner.plans.add() you can create a new Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const newPlan = await graph.planner.plans.add('groupObjectId', 'title');","title":"Add new Plan"},{"location":"graph/planner/#get-tasks-in-plan","text":"Using the tasks() you can get the Tasks in a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const planTasks = await graph.planner.plans.getById('planId').tasks();","title":"Get Tasks in Plan"},{"location":"graph/planner/#get-buckets-in-plan","text":"Using the buckets() you can get the Buckets in a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const planBuckets = await graph.planner.plans.getById('planId').buckets();","title":"Get Buckets in Plan"},{"location":"graph/planner/#get-details-in-plan","text":"Using the details() you can get the details in a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const planDetails = await graph.planner.plans.getById('planId').details();","title":"Get Details in Plan"},{"location":"graph/planner/#delete-plan","text":"Using the delete() you can get delete a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const delPlan = await graph.planner.plans.getById('planId').delete('planEtag');","title":"Delete Plan"},{"location":"graph/planner/#update-plan","text":"Using the update() you can get update a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const updPlan = await graph.planner.plans.getById('planId').update({title: 'New Title', eTag: 'planEtag'});","title":"Update Plan"},{"location":"graph/planner/#get-task-by-id","text":"Using the planner.tasks.getById() you can get a specific Task. Planner.tasks is not an available endpoint, you need to get a specific Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const task = await graph.planner.tasks.getById('taskId')();","title":"Get Task by Id"},{"location":"graph/planner/#add-new-task","text":"Using the planner.tasks.add() you can create a new Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const newTask = await graph.planner.tasks.add('planId', 'title');","title":"Add new Task"},{"location":"graph/planner/#get-details-in-task","text":"Using the details() you can get the details in a Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const taskDetails = await graph.planner.tasks.getById('taskId').details();","title":"Get Details in Task"},{"location":"graph/planner/#delete-task","text":"Using the delete() you can get delete a Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const delTask = await graph.planner.tasks.getById('taskId').delete('taskEtag');","title":"Delete Task"},{"location":"graph/planner/#update-task","text":"Using the update() you can get update a Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const updTask = await graph.planner.tasks.getById('taskId').update({properties, eTag:'taskEtag'});","title":"Update Task"},{"location":"graph/planner/#get-buckets-by-id","text":"Using the planner.buckets.getById() you can get a specific Bucket. planner.buckets is not an available endpoint, you need to get a specific Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const bucket = await graph.planner.buckets.getById('bucketId')();","title":"Get Buckets by Id"},{"location":"graph/planner/#add-new-bucket","text":"Using the planner.buckets.add() you can create a new Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const newBucket = await graph.planner.buckets.add('name', 'planId');","title":"Add new Bucket"},{"location":"graph/planner/#update-bucket","text":"Using the update() you can get update a Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const updBucket = await graph.planner.buckets.getById('bucketId').update({name: \"Name\", eTag:'bucketEtag'});","title":"Update Bucket"},{"location":"graph/planner/#delete-bucket","text":"Using the delete() you can get delete a Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const delBucket = await graph.planner.buckets.getById('bucketId').delete(eTag:'bucketEtag');","title":"Delete Bucket"},{"location":"graph/planner/#get-bucket-tasks","text":"Using the tasks() you can get Tasks in a Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const bucketTasks = await graph.planner.buckets.getById('bucketId').tasks();","title":"Get Bucket Tasks"},{"location":"graph/search/","text":"@pnp/graph/search \u00b6 The search module allows you to access the Microsoft Graph Search API. You can read full details of using the API, for library examples please see below. Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/search\"; Preset: All import \"@pnp/graph/presets/all\"; Call graph.query \u00b6 This example shows calling the search API via the query method of the root graph object. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/search\"; const results = await graph.query({ entityTypes: [\"site\"], query: { queryString: \"test\" }, }); Note: This library allows you to pass multiple search requests to the query method as the value consumed by the server is an array, but it only a single requests works at this time. Eventually this may change and no updates will be required.","title":"search"},{"location":"graph/search/#pnpgraphsearch","text":"The search module allows you to access the Microsoft Graph Search API. You can read full details of using the API, for library examples please see below. Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/search\"; Preset: All import \"@pnp/graph/presets/all\";","title":"@pnp/graph/search"},{"location":"graph/search/#call-graphquery","text":"This example shows calling the search API via the query method of the root graph object. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/search\"; const results = await graph.query({ entityTypes: [\"site\"], query: { queryString: \"test\" }, }); Note: This library allows you to pass multiple search requests to the query method as the value consumed by the server is an array, but it only a single requests works at this time. Eventually this may change and no updates will be required.","title":"Call graph.query"},{"location":"graph/subscriptions/","text":"@pnp/graph/subscriptions \u00b6 The ability to manage subscriptions is a capability introduced in version 1.2.9 of @pnp/graph. A subscription allows a client app to receive notifications about changes to data in Microsoft Graph. Currently, subscriptions are enabled for the following resources: Mail, events, and contacts from Outlook. Conversations from Office Groups. Drive root items from OneDrive. Users and Groups from Azure Active Directory. Alerts from the Microsoft Graph Security API. Get all of the Subscriptions \u00b6 Using the subscriptions(). If successful this method returns a 200 OK response code and a list of subscription objects in the response body. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const subscriptions = await graph.subscriptions(); Create a new Subscription \u00b6 Using the subscriptions.add(). Creating a subscription requires read scope to the resource. For example, to get notifications messages, your app needs the Mail.Read permission. To learn more about the scopes visit this url. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const addedSubscription = await graph.subscriptions.add(\"created,updated\", \"https://webhook.azurewebsites.net/api/send/myNotifyClient\", \"me/mailFolders('Inbox')/messages\", \"2019-11-20T18:23:45.9356913Z\"); Get Subscription by Id \u00b6 Using the subscriptions.getById() you can get one of the subscriptions import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const subscription = await graph.subscriptions.getById('subscriptionId')(); Delete a Subscription \u00b6 Using the subscriptions.getById().delete() you can remove one of the Subscriptions import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const delSubscription = await graph.subscriptions.getById('subscriptionId').delete(); Update a Subscription \u00b6 Using the subscriptions.getById().update() you can update one of the Subscriptions import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const updSubscription = await graph.subscriptions.getById('subscriptionId').update({changeType: \"created,updated,deleted\" });","title":"subscriptions"},{"location":"graph/subscriptions/#pnpgraphsubscriptions","text":"The ability to manage subscriptions is a capability introduced in version 1.2.9 of @pnp/graph. A subscription allows a client app to receive notifications about changes to data in Microsoft Graph. Currently, subscriptions are enabled for the following resources: Mail, events, and contacts from Outlook. Conversations from Office Groups. Drive root items from OneDrive. Users and Groups from Azure Active Directory. Alerts from the Microsoft Graph Security API.","title":"@pnp/graph/subscriptions"},{"location":"graph/subscriptions/#get-all-of-the-subscriptions","text":"Using the subscriptions(). If successful this method returns a 200 OK response code and a list of subscription objects in the response body. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const subscriptions = await graph.subscriptions();","title":"Get all of the Subscriptions"},{"location":"graph/subscriptions/#create-a-new-subscription","text":"Using the subscriptions.add(). Creating a subscription requires read scope to the resource. For example, to get notifications messages, your app needs the Mail.Read permission. To learn more about the scopes visit this url. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const addedSubscription = await graph.subscriptions.add(\"created,updated\", \"https://webhook.azurewebsites.net/api/send/myNotifyClient\", \"me/mailFolders('Inbox')/messages\", \"2019-11-20T18:23:45.9356913Z\");","title":"Create a new Subscription"},{"location":"graph/subscriptions/#get-subscription-by-id","text":"Using the subscriptions.getById() you can get one of the subscriptions import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const subscription = await graph.subscriptions.getById('subscriptionId')();","title":"Get Subscription by Id"},{"location":"graph/subscriptions/#delete-a-subscription","text":"Using the subscriptions.getById().delete() you can remove one of the Subscriptions import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const delSubscription = await graph.subscriptions.getById('subscriptionId').delete();","title":"Delete a Subscription"},{"location":"graph/subscriptions/#update-a-subscription","text":"Using the subscriptions.getById().update() you can update one of the Subscriptions import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const updSubscription = await graph.subscriptions.getById('subscriptionId').update({changeType: \"created,updated,deleted\" });","title":"Update a Subscription"},{"location":"graph/teams/","text":"@pnp/graph/teams \u00b6 The ability to manage Team is a capability introduced in the 1.2.7 of @pnp/graph. Through the methods described you can add, update and delete items in Teams. Teams the user is a member of \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/teams\" const joinedTeams = await graph.users.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').joinedTeams(); const myJoinedTeams = await graph.me.joinedTeams(); Get Teams by Id \u00b6 Using the teams.getById() you can get a specific Team. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const team = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528')(); Create new Team/Group - Method #1 \u00b6 The first way to create a new Team and corresponding Group is to first create the group and then create the team. Follow the example in Groups to create the group and get the GroupID. Then make a call to create the team from the group. Create a Team via a specific group \u00b6 Here we get the group via id and use createTeam import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" import \"@pnp/graph/groups\" const createdTeam = await graph.groups.getById('679c8ff4-f07d-40de-b02b-60ec332472dd').createTeam({ \"memberSettings\": { \"allowCreateUpdateChannels\": true }, \"messagingSettings\": { \"allowUserEditMessages\": true, \"allowUserDeleteMessages\": true }, \"funSettings\": { \"allowGiphy\": true, \"giphyContentRating\": \"strict\" }}); Create new Team/Group - Method #2 \u00b6 The second way to create a new Team and corresponding Group is to do so in one call. This can be done by using the createTeam method. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const team = { \"template@odata.bind\": \"https://graph.microsoft.com/v1.0/teamsTemplates('standard')\", \"displayName\": \"PnPJS Test Team\", \"description\": \"PnPJS Test Team\u2019s Description\", \"members\": [ { \"@odata.type\": \"#microsoft.graph.aadUserConversationMember\", \"roles\": [\"owner\"], \"user@odata.bind\": \"https://graph.microsoft.com/v1.0/users('{owners user id}')\", }, ], }; const createdTeam: ITeamCreateResultAsync = await graph.teams.create(team); //To check the status of the team creation, call getOperationById for the newly created team. const createdTeamStatus = await graph.teams.getById(createdTeam.teamId).getOperationById(createdTeam.operationId); Clone a Team \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const clonedTeam = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').cloneTeam( 'Cloned','description','apps,tabs,settings,channels,members','public'); Get Teams Async Operation \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const clonedTeam = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').cloneTeam( 'Cloned','description','apps,tabs,settings,channels,members','public'); const clonedTeamStatus = await graph.teams.getById(clonedTeam.teamId).getOperationById(clonedTeam.operationId); Archive a Team \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const archived = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').archive(); Unarchive a Team \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const archived = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').unarchive(); Get all channels of a Team \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const channels = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels(); Get channel by Id \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const channel = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype')(); Create a new Channel \u00b6 import { graph } from \"@pnp/graph\"; const newChannel = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.create('New Channel', 'Description'); Get installed Apps \u00b6 import { graph } from \"@pnp/graph\"; const installedApps = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps(); Add an App \u00b6 import { graph } from \"@pnp/graph\"; const addedApp = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps.add('https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a'); Remove an App \u00b6 import { graph } from \"@pnp/graph\"; const removedApp = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps.remove(); Get Tabs from a Channel \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const tabs = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528'). channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs(); Get Tab by Id \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const tab = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528'). channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs.getById('Id')(); Add a new Tab \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const newTab = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528'). channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs.add('Tab','https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a',{});","title":"teams"},{"location":"graph/teams/#pnpgraphteams","text":"The ability to manage Team is a capability introduced in the 1.2.7 of @pnp/graph. Through the methods described you can add, update and delete items in Teams.","title":"@pnp/graph/teams"},{"location":"graph/teams/#teams-the-user-is-a-member-of","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/teams\" const joinedTeams = await graph.users.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').joinedTeams(); const myJoinedTeams = await graph.me.joinedTeams();","title":"Teams the user is a member of"},{"location":"graph/teams/#get-teams-by-id","text":"Using the teams.getById() you can get a specific Team. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const team = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528')();","title":"Get Teams by Id"},{"location":"graph/teams/#create-new-teamgroup-method-1","text":"The first way to create a new Team and corresponding Group is to first create the group and then create the team. Follow the example in Groups to create the group and get the GroupID. Then make a call to create the team from the group.","title":"Create new Team/Group - Method #1"},{"location":"graph/teams/#create-a-team-via-a-specific-group","text":"Here we get the group via id and use createTeam import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" import \"@pnp/graph/groups\" const createdTeam = await graph.groups.getById('679c8ff4-f07d-40de-b02b-60ec332472dd').createTeam({ \"memberSettings\": { \"allowCreateUpdateChannels\": true }, \"messagingSettings\": { \"allowUserEditMessages\": true, \"allowUserDeleteMessages\": true }, \"funSettings\": { \"allowGiphy\": true, \"giphyContentRating\": \"strict\" }});","title":"Create a Team via a specific group"},{"location":"graph/teams/#create-new-teamgroup-method-2","text":"The second way to create a new Team and corresponding Group is to do so in one call. This can be done by using the createTeam method. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const team = { \"template@odata.bind\": \"https://graph.microsoft.com/v1.0/teamsTemplates('standard')\", \"displayName\": \"PnPJS Test Team\", \"description\": \"PnPJS Test Team\u2019s Description\", \"members\": [ { \"@odata.type\": \"#microsoft.graph.aadUserConversationMember\", \"roles\": [\"owner\"], \"user@odata.bind\": \"https://graph.microsoft.com/v1.0/users('{owners user id}')\", }, ], }; const createdTeam: ITeamCreateResultAsync = await graph.teams.create(team); //To check the status of the team creation, call getOperationById for the newly created team. const createdTeamStatus = await graph.teams.getById(createdTeam.teamId).getOperationById(createdTeam.operationId);","title":"Create new Team/Group - Method #2"},{"location":"graph/teams/#clone-a-team","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const clonedTeam = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').cloneTeam( 'Cloned','description','apps,tabs,settings,channels,members','public');","title":"Clone a Team"},{"location":"graph/teams/#get-teams-async-operation","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const clonedTeam = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').cloneTeam( 'Cloned','description','apps,tabs,settings,channels,members','public'); const clonedTeamStatus = await graph.teams.getById(clonedTeam.teamId).getOperationById(clonedTeam.operationId);","title":"Get Teams Async Operation"},{"location":"graph/teams/#archive-a-team","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const archived = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').archive();","title":"Archive a Team"},{"location":"graph/teams/#unarchive-a-team","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const archived = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').unarchive();","title":"Unarchive a Team"},{"location":"graph/teams/#get-all-channels-of-a-team","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const channels = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels();","title":"Get all channels of a Team"},{"location":"graph/teams/#get-channel-by-id","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const channel = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype')();","title":"Get channel by Id"},{"location":"graph/teams/#create-a-new-channel","text":"import { graph } from \"@pnp/graph\"; const newChannel = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.create('New Channel', 'Description');","title":"Create a new Channel"},{"location":"graph/teams/#get-installed-apps","text":"import { graph } from \"@pnp/graph\"; const installedApps = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps();","title":"Get installed Apps"},{"location":"graph/teams/#add-an-app","text":"import { graph } from \"@pnp/graph\"; const addedApp = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps.add('https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a');","title":"Add an App"},{"location":"graph/teams/#remove-an-app","text":"import { graph } from \"@pnp/graph\"; const removedApp = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps.remove();","title":"Remove an App"},{"location":"graph/teams/#get-tabs-from-a-channel","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const tabs = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528'). channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs();","title":"Get Tabs from a Channel"},{"location":"graph/teams/#get-tab-by-id","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const tab = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528'). channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs.getById('Id')();","title":"Get Tab by Id"},{"location":"graph/teams/#add-a-new-tab","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const newTab = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528'). channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs.add('Tab','https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a',{});","title":"Add a new Tab"},{"location":"graph/users/","text":"@pnp/graph/users \u00b6 Users are Azure Active Directory objects representing users in the organizations. They represent the single identity for a person across Microsoft 365 services. You can learn more about Microsoft Graph users by reading the Official Microsoft Graph Documentation . IUsers, IUser, IPeople \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {IUser, IUsers, User, Users, IPeople, People} from \"@pnp/graph/users\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; Preset: All import { graph,IUser, IUsers, User, Users, IPeople, People } from \"@pnp/graph/presets/all\"; Current User \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const currentUser = await graph.me(); Get All Users in the Organization \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const allUsers = await graph.users(); Get a User by email address (or user id) \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const matchingUser = await graph.users.getById('jane@contoso.com')(); Update Current User \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; await graph.me.update({ displayName: 'John Doe' }); People \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const people = await graph.me.people(); // get the top 3 people const people = await graph.me.people.top(3)(); People \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const people = await graph.me.people(); // get the top 3 people const people = await graph.me.people.top(3)(); Manager \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const manager = await graph.me.manager(); Direct Reports \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const reports = await graph.me.directReports(); Photo \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const currentUser = await graph.me.photo(); const specificUser = await graph.users.getById('jane@contoso.com').photo(); User Photo Operations \u00b6 See Photos","title":"users"},{"location":"graph/users/#pnpgraphusers","text":"Users are Azure Active Directory objects representing users in the organizations. They represent the single identity for a person across Microsoft 365 services. You can learn more about Microsoft Graph users by reading the Official Microsoft Graph Documentation .","title":"@pnp/graph/users"},{"location":"graph/users/#iusers-iuser-ipeople","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {IUser, IUsers, User, Users, IPeople, People} from \"@pnp/graph/users\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; Preset: All import { graph,IUser, IUsers, User, Users, IPeople, People } from \"@pnp/graph/presets/all\";","title":"IUsers, IUser, IPeople"},{"location":"graph/users/#current-user","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const currentUser = await graph.me();","title":"Current User"},{"location":"graph/users/#get-all-users-in-the-organization","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const allUsers = await graph.users();","title":"Get All Users in the Organization"},{"location":"graph/users/#get-a-user-by-email-address-or-user-id","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const matchingUser = await graph.users.getById('jane@contoso.com')();","title":"Get a User by email address (or user id)"},{"location":"graph/users/#update-current-user","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; await graph.me.update({ displayName: 'John Doe' });","title":"Update Current User"},{"location":"graph/users/#people","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const people = await graph.me.people(); // get the top 3 people const people = await graph.me.people.top(3)();","title":"People"},{"location":"graph/users/#people_1","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const people = await graph.me.people(); // get the top 3 people const people = await graph.me.people.top(3)();","title":"People"},{"location":"graph/users/#manager","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const manager = await graph.me.manager();","title":"Manager"},{"location":"graph/users/#direct-reports","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const reports = await graph.me.directReports();","title":"Direct Reports"},{"location":"graph/users/#photo","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const currentUser = await graph.me.photo(); const specificUser = await graph.users.getById('jane@contoso.com').photo();","title":"Photo"},{"location":"graph/users/#user-photo-operations","text":"See Photos","title":"User Photo Operations"},{"location":"logging/","text":"@pnp/logging \u00b6 The logging module provides light weight subscribable and extensible logging framework which is used internally and available for use in your projects. This article outlines how to setup logging and use the various loggers. Getting Started \u00b6 Install the logging module, it has no other dependencies npm install @pnp/logging --save Understanding the Logging Framework \u00b6 The logging framework is centered on the Logger class to which any number of listeners can be subscribed. Each of these listeners will receive each of the messages logged. Each listener must implement the ILogListener interface, shown below. There is only one method to implement and it takes an instance of the LogEntry interface as a parameter. /** * Interface that defines a log listener * */ export interface ILogListener { /** * Any associated data that a given logging listener may choose to log or ignore * * @param entry The information to be logged */ log(entry: ILogEntry): void; } /** * Interface that defines a log entry * */ export interface ILogEntry { /** * The main message to be logged */ message: string; /** * The level of information this message represents */ level: LogLevel; /** * Any associated data that a given logging listener may choose to log or ignore */ data?: any; } Log Levels \u00b6 export const enum LogLevel { Verbose = 0, Info = 1, Warning = 2, Error = 3, Off = 99, } Writing to the Logger \u00b6 To write information to a logger you can use either write, writeJSON, or log. import { Logger, LogLevel } from \"@pnp/logging\"; // write logs a simple string as the message value of the LogEntry Logger.write(\"This is logging a simple string\"); // optionally passing a level, default level is Verbose Logger.write(\"This is logging a simple string\", LogLevel.Error); // this will convert the object to a string using JSON.stringify and set the message with the result Logger.writeJSON({ name: \"value\", name2: \"value2\"}); // optionally passing a level, default level is Verbose Logger.writeJSON({ name: \"value\", name2: \"value2\"}, LogLevel.Warning); // specify the entire LogEntry interface using log Logger.log({ data: { name: \"value\", name2: \"value2\"}, level: LogLevel.Warning, message: \"This is my message\" }); Log an error \u00b6 There exists a shortcut method to log an error to the Logger. This will log an entry to the subscribed loggers where the data property will be the Error instance passed in, the level will be 'Error', and the message will be the Error instance's message property. const e = Error(\"An Error\"); Logger.error(e); Subscribing a Listener \u00b6 By default no listeners are subscribed, so if you would like to get logging information you need to subscribe at least one listener. This is done as shown below by importing the Logger and your listener(s) of choice. Here we are using the provided ConsoleListener. We are also setting the active log level, which controls the level of logging that will be output. Be aware that Verbose produces a substantial amount of data about each request. import { Logger, ConsoleListener, LogLevel } from \"@pnp/logging\"; // subscribe a listener Logger.subscribe(new ConsoleListener()); // set the active log level Logger.activeLogLevel = LogLevel.Info; Available Listeners \u00b6 There are two listeners included in the library, ConsoleListener and FunctionListener. ConsoleListener \u00b6 This listener outputs information to the console and works in Node as well as within browsers. It can be used without settings and writes to the appropriate console method based on message level. For example a LogEntry with level Warning will be written to console.warn. Basic usage is shown in the example above. Configuration Options \u00b6 Although ConsoleListener can be used without configuration, there are some additional options available to you. ConsoleListener supports adding a prefix to every output (helpful for filtering console messages) and specifying text color for messages (including by LogLevel). Using a Prefix \u00b6 To add a prefix to all output, supply a string in the constructor: import { Logger, ConsoleListener, LogLevel } from \"@pnp/logging\"; const LOG_SOURCE: string = 'MyAwesomeWebPart'; Logger.subscribe(new ConsoleListener(LOG_SOURCE)); Logger.activeLogLevel = LogLevel.Info; With the above configuration, Logger.write(\"My special message\"); will be output to the console as: MyAwesomeWebPart - My special message Customizing Text Color \u00b6 You can also specify text color for your messages by supplying an IConsoleListenerColors object. You can simply specify color to set the default color for all logging levels or you can set one or more logging level specific text colors (if you only want to set color for a specific logging level(s), leave color out and all other log levels will use the default color). Colors can be specified the same way color values are specified in CSS (named colors, hex values, rgb, rgba, hsl, hsla, etc.): import { Logger, ConsoleListener, LogLevel } from \"@pnp/logging\"; const LOG_SOURCE: string = 'MyAwesomeWebPart'; Logger.subscribe(new ConsoleListener(LOG_SOURCE, {color:'#0b6a0b',warningColor:'magenta'})); Logger.activeLogLevel = LogLevel.Info; With the above configuration: Logger.write(\"My special message\"); Logger.write(\"A warning!\", LogLevel.Warning); Will result in messages that look like this: Color options: color : Default text color for all logging levels unless they're specified verboseColor : Text color to use for messages with LogLevel.Verbose infoColor : Text color to use for messages with LogLevel.Info warningColor : Text color to use for messages with LogLevel.Warning errorColor : Text color to use for messages with LogLevel.Error To set colors without a prefix, specify either undefined or an empty string for the first parameter: Logger.subscribe(new ConsoleListener(undefined, {color:'purple'})); FunctionListener \u00b6 The FunctionListener allows you to wrap any functionality by creating a function that takes a LogEntry as its single argument. This produces the same result as implementing the LogListener interface, but is useful if you already have a logging method or framework to which you want to pass the messages. import { Logger, FunctionListener, ILogEntry } from \"@pnp/logging\"; let listener = new FunctionListener((entry: ILogEntry) => { // pass all logging data to an existing framework MyExistingCompanyLoggingFramework.log(entry.message); }); Logger.subscribe(listener); Create a Custom Listener \u00b6 If desirable for your project you can create a custom listener to perform any logging action you would like. This is done by implementing the ILogListener interface. import { Logger, ILogListener, ILogEntry } from \"@pnp/logging\"; class MyListener implements ILogListener { log(entry: ILogEntry): void { // here you would do something with the entry } } Logger.subscribe(new MyListener());","title":"logging"},{"location":"logging/#pnplogging","text":"The logging module provides light weight subscribable and extensible logging framework which is used internally and available for use in your projects. This article outlines how to setup logging and use the various loggers.","title":"@pnp/logging"},{"location":"logging/#getting-started","text":"Install the logging module, it has no other dependencies npm install @pnp/logging --save","title":"Getting Started"},{"location":"logging/#understanding-the-logging-framework","text":"The logging framework is centered on the Logger class to which any number of listeners can be subscribed. Each of these listeners will receive each of the messages logged. Each listener must implement the ILogListener interface, shown below. There is only one method to implement and it takes an instance of the LogEntry interface as a parameter. /** * Interface that defines a log listener * */ export interface ILogListener { /** * Any associated data that a given logging listener may choose to log or ignore * * @param entry The information to be logged */ log(entry: ILogEntry): void; } /** * Interface that defines a log entry * */ export interface ILogEntry { /** * The main message to be logged */ message: string; /** * The level of information this message represents */ level: LogLevel; /** * Any associated data that a given logging listener may choose to log or ignore */ data?: any; }","title":"Understanding the Logging Framework"},{"location":"logging/#log-levels","text":"export const enum LogLevel { Verbose = 0, Info = 1, Warning = 2, Error = 3, Off = 99, }","title":"Log Levels"},{"location":"logging/#writing-to-the-logger","text":"To write information to a logger you can use either write, writeJSON, or log. import { Logger, LogLevel } from \"@pnp/logging\"; // write logs a simple string as the message value of the LogEntry Logger.write(\"This is logging a simple string\"); // optionally passing a level, default level is Verbose Logger.write(\"This is logging a simple string\", LogLevel.Error); // this will convert the object to a string using JSON.stringify and set the message with the result Logger.writeJSON({ name: \"value\", name2: \"value2\"}); // optionally passing a level, default level is Verbose Logger.writeJSON({ name: \"value\", name2: \"value2\"}, LogLevel.Warning); // specify the entire LogEntry interface using log Logger.log({ data: { name: \"value\", name2: \"value2\"}, level: LogLevel.Warning, message: \"This is my message\" });","title":"Writing to the Logger"},{"location":"logging/#log-an-error","text":"There exists a shortcut method to log an error to the Logger. This will log an entry to the subscribed loggers where the data property will be the Error instance passed in, the level will be 'Error', and the message will be the Error instance's message property. const e = Error(\"An Error\"); Logger.error(e);","title":"Log an error"},{"location":"logging/#subscribing-a-listener","text":"By default no listeners are subscribed, so if you would like to get logging information you need to subscribe at least one listener. This is done as shown below by importing the Logger and your listener(s) of choice. Here we are using the provided ConsoleListener. We are also setting the active log level, which controls the level of logging that will be output. Be aware that Verbose produces a substantial amount of data about each request. import { Logger, ConsoleListener, LogLevel } from \"@pnp/logging\"; // subscribe a listener Logger.subscribe(new ConsoleListener()); // set the active log level Logger.activeLogLevel = LogLevel.Info;","title":"Subscribing a Listener"},{"location":"logging/#available-listeners","text":"There are two listeners included in the library, ConsoleListener and FunctionListener.","title":"Available Listeners"},{"location":"logging/#consolelistener","text":"This listener outputs information to the console and works in Node as well as within browsers. It can be used without settings and writes to the appropriate console method based on message level. For example a LogEntry with level Warning will be written to console.warn. Basic usage is shown in the example above.","title":"ConsoleListener"},{"location":"logging/#configuration-options","text":"Although ConsoleListener can be used without configuration, there are some additional options available to you. ConsoleListener supports adding a prefix to every output (helpful for filtering console messages) and specifying text color for messages (including by LogLevel).","title":"Configuration Options"},{"location":"logging/#using-a-prefix","text":"To add a prefix to all output, supply a string in the constructor: import { Logger, ConsoleListener, LogLevel } from \"@pnp/logging\"; const LOG_SOURCE: string = 'MyAwesomeWebPart'; Logger.subscribe(new ConsoleListener(LOG_SOURCE)); Logger.activeLogLevel = LogLevel.Info; With the above configuration, Logger.write(\"My special message\"); will be output to the console as: MyAwesomeWebPart - My special message","title":"Using a Prefix"},{"location":"logging/#customizing-text-color","text":"You can also specify text color for your messages by supplying an IConsoleListenerColors object. You can simply specify color to set the default color for all logging levels or you can set one or more logging level specific text colors (if you only want to set color for a specific logging level(s), leave color out and all other log levels will use the default color). Colors can be specified the same way color values are specified in CSS (named colors, hex values, rgb, rgba, hsl, hsla, etc.): import { Logger, ConsoleListener, LogLevel } from \"@pnp/logging\"; const LOG_SOURCE: string = 'MyAwesomeWebPart'; Logger.subscribe(new ConsoleListener(LOG_SOURCE, {color:'#0b6a0b',warningColor:'magenta'})); Logger.activeLogLevel = LogLevel.Info; With the above configuration: Logger.write(\"My special message\"); Logger.write(\"A warning!\", LogLevel.Warning); Will result in messages that look like this: Color options: color : Default text color for all logging levels unless they're specified verboseColor : Text color to use for messages with LogLevel.Verbose infoColor : Text color to use for messages with LogLevel.Info warningColor : Text color to use for messages with LogLevel.Warning errorColor : Text color to use for messages with LogLevel.Error To set colors without a prefix, specify either undefined or an empty string for the first parameter: Logger.subscribe(new ConsoleListener(undefined, {color:'purple'}));","title":"Customizing Text Color"},{"location":"logging/#functionlistener","text":"The FunctionListener allows you to wrap any functionality by creating a function that takes a LogEntry as its single argument. This produces the same result as implementing the LogListener interface, but is useful if you already have a logging method or framework to which you want to pass the messages. import { Logger, FunctionListener, ILogEntry } from \"@pnp/logging\"; let listener = new FunctionListener((entry: ILogEntry) => { // pass all logging data to an existing framework MyExistingCompanyLoggingFramework.log(entry.message); }); Logger.subscribe(listener);","title":"FunctionListener"},{"location":"logging/#create-a-custom-listener","text":"If desirable for your project you can create a custom listener to perform any logging action you would like. This is done by implementing the ILogListener interface. import { Logger, ILogListener, ILogEntry } from \"@pnp/logging\"; class MyListener implements ILogListener { log(entry: ILogEntry): void { // here you would do something with the entry } } Logger.subscribe(new MyListener());","title":"Create a Custom Listener"},{"location":"news/2020-year-in-review/","text":"2020 Year End Report \u00b6 Welcome to our first year in review report for PnPjs. This year has marked usage milestones, seen more contributors than ever, and expanded the core maintainers team. But none of this would be possible without everyones support and participation - so we start by saying Thank You! We deeply appreciate everyone that has used, helped us grow, and improved the library over the last year. This year we introduced MSAL clients for node and browser, improved our testing/local development plumbing, and updated the libraries to work with the node 15 module resolution rules. We fixed 43 reported bugs, answered 131 questions, and made 55 suggested enhancements to the library - all driven by feedback from users and the community. Planned for release in January 2021 we also undertook the work to enable isolated runtimes, a long requested feature. This allows you to operate on multiple independently configured \"roots\" such as \"sp\" or \"graph\" from the same application. Previously the library was configured globally, so this opens new possibilities for both client and server side scenarios. Finally we made many tooling and project improvements such as moving to GitHub actions, updating the tests to use MSAL, and exploring ways to enhance the developer experience. Usage \u00b6 In 2020 we tracked steady month/month growth in raw usage measured by requests as well as in the number of tenants deploying the library. Starting the year we were used in 14605 tenants and by December that number grew to 21,227. These tenants generated 6.1 billion requests to the service in January growing to 9.2 billion by December, peaking at 10.1 billion requests in November. 1) There was a data glitch in October so the numbers do not fully represent usage. 2) These numbers only include public cloud SPO usage, true usage is higher than we can track due to on-premesis and gov/sovereign clouds Releases \u00b6 We continued our monthly release cadence as it represents a good pace for addressing issues while not expecting folks to update too often and keeping each update to a reasonable size. All changes can be tracked in our change log , updated with each release. You can check our scheduled releases through project milestones , understanding there are occasionally delays. Monthly releases allows us to ensure bugs do not linger and we continually improve and expand the capabilities of the libraries. NPM Package download statistics (@pnp/sp): \u00b6 Month Count * Month Count January 100,686 * July 36,805 February 34,437 * August 38,897 March 34,574 * September 45,968 April 32,436 * October 46,655 May 34,482 * November 45,511 June 34,408 * December 58,977 Grand Total 543,836 With 2020 our total all time downloads of @pnp/sp is now at: 949,638 Stats from https://npm-stat.com/ Future Plans \u00b6 Looking to the future we will continue to actively grow and improve v2 of the library, guided by feedback and reported issues. Additionally, we are beginning to discuss v3 and doing initial planning and prototyping. The v3 work will continue through 2021 with no currently set release date, though we will keep everyone up to date. Additionally in 2021 there will be a general focus on improving not just the code but our tooling, build pipeline, and library contributor experience. We will also look at automatic canary releases with each merge, and other improvements. New Lead Maintainer \u00b6 With the close of 2020 we are very excited to announce a new lead maintainer for PnPjs, Julie Turner ! Julie brings deep expertise with SharePoint Framework, TypeScript, and SharePoint development to the team, coupled with dedication and care in the work. Over the last year she has gotten more involved with handling releases, responding to issues, and helping to keep the code updated and clean. We are very lucky to have her working on the project and look forward to seeing her lead the growth and direction for years to come. Contributors \u00b6 As always we have abundant thanks and appreciation for your contributors. Taking your time to help improve PnPjs for the community is massive and valuable to ensure our sustainability. Thank you for all your help in 2020! If you are interested in becoming a contributor check out our guide on ways to get started. Sponsors \u00b6 We want to thank our sponsors for their support in 2020! This year we put the money towards helping offset the cost and shipping of hoodies to contributors and sponsors. Your continued generosity makes a big difference in our ability to recognize and reward the folks building PnPjs. Thank You Closing \u00b6 In closing we want say Thank You to everyone who uses, contributes to, and participates in PnPjs and the SharePoint Patterns and Practices program. Wishing you the very best for 2021, The PnPjs Team","title":"2020 Year In Review"},{"location":"news/2020-year-in-review/#2020-year-end-report","text":"Welcome to our first year in review report for PnPjs. This year has marked usage milestones, seen more contributors than ever, and expanded the core maintainers team. But none of this would be possible without everyones support and participation - so we start by saying Thank You! We deeply appreciate everyone that has used, helped us grow, and improved the library over the last year. This year we introduced MSAL clients for node and browser, improved our testing/local development plumbing, and updated the libraries to work with the node 15 module resolution rules. We fixed 43 reported bugs, answered 131 questions, and made 55 suggested enhancements to the library - all driven by feedback from users and the community. Planned for release in January 2021 we also undertook the work to enable isolated runtimes, a long requested feature. This allows you to operate on multiple independently configured \"roots\" such as \"sp\" or \"graph\" from the same application. Previously the library was configured globally, so this opens new possibilities for both client and server side scenarios. Finally we made many tooling and project improvements such as moving to GitHub actions, updating the tests to use MSAL, and exploring ways to enhance the developer experience.","title":"2020 Year End Report"},{"location":"news/2020-year-in-review/#usage","text":"In 2020 we tracked steady month/month growth in raw usage measured by requests as well as in the number of tenants deploying the library. Starting the year we were used in 14605 tenants and by December that number grew to 21,227. These tenants generated 6.1 billion requests to the service in January growing to 9.2 billion by December, peaking at 10.1 billion requests in November. 1) There was a data glitch in October so the numbers do not fully represent usage. 2) These numbers only include public cloud SPO usage, true usage is higher than we can track due to on-premesis and gov/sovereign clouds","title":"Usage"},{"location":"news/2020-year-in-review/#releases","text":"We continued our monthly release cadence as it represents a good pace for addressing issues while not expecting folks to update too often and keeping each update to a reasonable size. All changes can be tracked in our change log , updated with each release. You can check our scheduled releases through project milestones , understanding there are occasionally delays. Monthly releases allows us to ensure bugs do not linger and we continually improve and expand the capabilities of the libraries.","title":"Releases"},{"location":"news/2020-year-in-review/#npm-package-download-statistics-pnpsp","text":"Month Count * Month Count January 100,686 * July 36,805 February 34,437 * August 38,897 March 34,574 * September 45,968 April 32,436 * October 46,655 May 34,482 * November 45,511 June 34,408 * December 58,977 Grand Total 543,836 With 2020 our total all time downloads of @pnp/sp is now at: 949,638 Stats from https://npm-stat.com/","title":"NPM Package download statistics (@pnp/sp):"},{"location":"news/2020-year-in-review/#future-plans","text":"Looking to the future we will continue to actively grow and improve v2 of the library, guided by feedback and reported issues. Additionally, we are beginning to discuss v3 and doing initial planning and prototyping. The v3 work will continue through 2021 with no currently set release date, though we will keep everyone up to date. Additionally in 2021 there will be a general focus on improving not just the code but our tooling, build pipeline, and library contributor experience. We will also look at automatic canary releases with each merge, and other improvements.","title":"Future Plans"},{"location":"news/2020-year-in-review/#new-lead-maintainer","text":"With the close of 2020 we are very excited to announce a new lead maintainer for PnPjs, Julie Turner ! Julie brings deep expertise with SharePoint Framework, TypeScript, and SharePoint development to the team, coupled with dedication and care in the work. Over the last year she has gotten more involved with handling releases, responding to issues, and helping to keep the code updated and clean. We are very lucky to have her working on the project and look forward to seeing her lead the growth and direction for years to come.","title":"New Lead Maintainer"},{"location":"news/2020-year-in-review/#contributors","text":"As always we have abundant thanks and appreciation for your contributors. Taking your time to help improve PnPjs for the community is massive and valuable to ensure our sustainability. Thank you for all your help in 2020! If you are interested in becoming a contributor check out our guide on ways to get started.","title":"Contributors"},{"location":"news/2020-year-in-review/#sponsors","text":"We want to thank our sponsors for their support in 2020! This year we put the money towards helping offset the cost and shipping of hoodies to contributors and sponsors. Your continued generosity makes a big difference in our ability to recognize and reward the folks building PnPjs. Thank You","title":"Sponsors"},{"location":"news/2020-year-in-review/#closing","text":"In closing we want say Thank You to everyone who uses, contributes to, and participates in PnPjs and the SharePoint Patterns and Practices program. Wishing you the very best for 2021, The PnPjs Team","title":"Closing"},{"location":"nodejs/","text":"@pnp/nodejs \u00b6 This package supplies helper code when using the @pnp libraries within the context of nodejs. Primarily these consist of clients to enable use of the libraries in nodejs. Getting Started \u00b6 Install the library and required dependencies. You will also need to install other libraries such as @pnp/sp or @pnp/graph to use the exported functionality. npm install @pnp/sp @pnp/nodejs --save AdalFetchClient SPFetchClient BearerTokenFetchClient Proxy SP Extensions \u00b6 Added in 2.0.9 A set of nodejs specific extensions for the @pnp/sp library. SP Extensions","title":"nodejs"},{"location":"nodejs/#pnpnodejs","text":"This package supplies helper code when using the @pnp libraries within the context of nodejs. Primarily these consist of clients to enable use of the libraries in nodejs.","title":"@pnp/nodejs"},{"location":"nodejs/#getting-started","text":"Install the library and required dependencies. You will also need to install other libraries such as @pnp/sp or @pnp/graph to use the exported functionality. npm install @pnp/sp @pnp/nodejs --save AdalFetchClient SPFetchClient BearerTokenFetchClient Proxy","title":"Getting Started"},{"location":"nodejs/#sp-extensions","text":"Added in 2.0.9 A set of nodejs specific extensions for the @pnp/sp library. SP Extensions","title":"SP Extensions"},{"location":"nodejs/adal-fetch-client/","text":"@pnp/nodejs/adalfetchclient \u00b6 The AdalFetchClient class depends on the adal-node package to authenticate against Azure AD. The example below outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected. import { AdalFetchClient } from \"@pnp/nodejs\"; import { graph } from \"@pnp/graph/presets/all\"; // setup the client using graph setup function graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{tenant}\", \"{app id}\", \"{app secret}\"); }, }, }); // execute a library request as normal const g = await graph.groups(); console.log(JSON.stringify(g, null, 4));","title":"AdalFetchClient"},{"location":"nodejs/adal-fetch-client/#pnpnodejsadalfetchclient","text":"The AdalFetchClient class depends on the adal-node package to authenticate against Azure AD. The example below outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected. import { AdalFetchClient } from \"@pnp/nodejs\"; import { graph } from \"@pnp/graph/presets/all\"; // setup the client using graph setup function graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{tenant}\", \"{app id}\", \"{app secret}\"); }, }, }); // execute a library request as normal const g = await graph.groups(); console.log(JSON.stringify(g, null, 4));","title":"@pnp/nodejs/adalfetchclient"},{"location":"nodejs/bearer-token-fetch-client/","text":"@pnp/nodejs/BearerTokenFetchClient \u00b6 The BearerTokenFetchClient class allows you to easily specify your own Bearer tokens to be used in the requests. How you derive the token is up to you. import { BearerTokenFetchClient } from \"@pnp/nodejs\"; import { graph } from \"@pnp/graph/presets/all\"; // setup the client using graph setup function graph.setup({ graph: { fetchClientFactory: () => { return new BearerTokenFetchClient(\"{Bearer Token}\"); }, }, }); // execute a library request as normal const g = await graph.groups(); console.log(JSON.stringify(g, null, 4));","title":"BearerTokenFetchClient"},{"location":"nodejs/bearer-token-fetch-client/#pnpnodejsbearertokenfetchclient","text":"The BearerTokenFetchClient class allows you to easily specify your own Bearer tokens to be used in the requests. How you derive the token is up to you. import { BearerTokenFetchClient } from \"@pnp/nodejs\"; import { graph } from \"@pnp/graph/presets/all\"; // setup the client using graph setup function graph.setup({ graph: { fetchClientFactory: () => { return new BearerTokenFetchClient(\"{Bearer Token}\"); }, }, }); // execute a library request as normal const g = await graph.groups(); console.log(JSON.stringify(g, null, 4));","title":"@pnp/nodejs/BearerTokenFetchClient"},{"location":"nodejs/provider-hosted-app/","text":"@pnp/nodejs/providerhostedrequestcontext \u00b6 The ProviderHostedRequestContext enables the creation of provider-hosted add-ins built in node.js to use pnpjs to interact with SharePoint. The context is associated to a SharePoint user, allowing requests to be made by the add-in on the behalf of the user. The usage of this class assumes the provider-hosted add-in is called from SharePoint with a valid SPAppToken. This is typically done by means of accessing /_layouts/15/AppRedirect.aspx with the app's client ID and app's redirect URI. Note : To support concurrent requests by different users and/or add-ins on different tenants, do not use the SPFetchClient class. Instead, use the more generic NodeFetchClient class. The downside is that you have to manually configure each request to use the desired user/app context. import { sp, SPRest } from \"@pnp/sp/presets/all\"; import { NodeFetchClient, ProviderHostedRequestContext } from \"@pnp/nodejs\"; // configure your node options sp.setup({ sp: { fetchClientFactory: () => { return new NodeFetchClient(); }, }, }); // get request data generated by /_layouts/15/AppRedirect.aspx const spAppToken = request.body.SPAppToken; const spSiteUrl = request.body.SPSiteUrl; // create a context based on the add-in details and SPAppToken const ctx = await ProviderHostedRequestContext.create(spSiteUrl, \"{client id}\", \"{client secret}\", spAppToken); // create an SPRest object configured to use our context // this is used in place of the global sp object const userSP = new SPRest().configure(await ctx.getUserConfig(), spSiteUrl); const addinSP = new SPRest().configure(await ctx.getAddInOnlyConfig(), spSiteUrl); // make a request on behalf of the user const user = await userSP.web.currentUser(); console.log(`Hello ${user.Title}`); // make an add-in only request const app = await addinSP.web.currentUser(); console.log(`Add-in principal: ${app.Title}`);","title":"ProviderHostedRequestContext"},{"location":"nodejs/provider-hosted-app/#pnpnodejsproviderhostedrequestcontext","text":"The ProviderHostedRequestContext enables the creation of provider-hosted add-ins built in node.js to use pnpjs to interact with SharePoint. The context is associated to a SharePoint user, allowing requests to be made by the add-in on the behalf of the user. The usage of this class assumes the provider-hosted add-in is called from SharePoint with a valid SPAppToken. This is typically done by means of accessing /_layouts/15/AppRedirect.aspx with the app's client ID and app's redirect URI. Note : To support concurrent requests by different users and/or add-ins on different tenants, do not use the SPFetchClient class. Instead, use the more generic NodeFetchClient class. The downside is that you have to manually configure each request to use the desired user/app context. import { sp, SPRest } from \"@pnp/sp/presets/all\"; import { NodeFetchClient, ProviderHostedRequestContext } from \"@pnp/nodejs\"; // configure your node options sp.setup({ sp: { fetchClientFactory: () => { return new NodeFetchClient(); }, }, }); // get request data generated by /_layouts/15/AppRedirect.aspx const spAppToken = request.body.SPAppToken; const spSiteUrl = request.body.SPSiteUrl; // create a context based on the add-in details and SPAppToken const ctx = await ProviderHostedRequestContext.create(spSiteUrl, \"{client id}\", \"{client secret}\", spAppToken); // create an SPRest object configured to use our context // this is used in place of the global sp object const userSP = new SPRest().configure(await ctx.getUserConfig(), spSiteUrl); const addinSP = new SPRest().configure(await ctx.getAddInOnlyConfig(), spSiteUrl); // make a request on behalf of the user const user = await userSP.web.currentUser(); console.log(`Hello ${user.Title}`); // make an add-in only request const app = await addinSP.web.currentUser(); console.log(`Add-in principal: ${app.Title}`);","title":"@pnp/nodejs/providerhostedrequestcontext"},{"location":"nodejs/proxy/","text":"@pnp/nodejs/proxy \u00b6 In some cases when deploying on node you may need to use a proxy as governed by corporate policy, or perhaps you want to examine the traffic using a tool such as Fiddler. setProxyUrl \u00b6 Basic Usage \u00b6 You need to import the setProxyUrl function from @pnp/nodejs library and call it with your proxy url. Once done an https-proxy-agent will be used with each request. This works across all clients within the @pnp/nodejs library. import { SPFetchClient, SPOAuthEnv, setProxyUrl } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { // call the set proxy url function and it will be used for all requests regardless of client setProxyUrl(\"{your proxy url}\"); return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO); }, }, }); Use with Fiddler \u00b6 To get Fiddler to work you may need to set an environment variable. This should only be done for testing! import { SPFetchClient, SPOAuthEnv, setProxyUrl } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { // ignore certificate errors: ONLY FOR TESTING!! process.env.NODE_TLS_REJECT_UNAUTHORIZED = \"0\"; // this is my fiddler url locally setProxyUrl(\"http://127.0.0.1:8888\"); return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO); }, }, }); setProxyAgent \u00b6 Added in 2.0.11 You need to import the setProxyAgent function from @pnp/nodejs library and call it with your proxy url. You can supply any valid proxy and it will be used. import { SPFetchClient, SPOAuthEnv, setProxyAgent } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { const myAgent = new MyAgentOfSomeType({}); // call the set proxy agent function and it will be used for all requests regardless of client setProxyAgent(myAgent); return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO); }, }, });","title":"proxy"},{"location":"nodejs/proxy/#pnpnodejsproxy","text":"In some cases when deploying on node you may need to use a proxy as governed by corporate policy, or perhaps you want to examine the traffic using a tool such as Fiddler.","title":"@pnp/nodejs/proxy"},{"location":"nodejs/proxy/#setproxyurl","text":"","title":"setProxyUrl"},{"location":"nodejs/proxy/#basic-usage","text":"You need to import the setProxyUrl function from @pnp/nodejs library and call it with your proxy url. Once done an https-proxy-agent will be used with each request. This works across all clients within the @pnp/nodejs library. import { SPFetchClient, SPOAuthEnv, setProxyUrl } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { // call the set proxy url function and it will be used for all requests regardless of client setProxyUrl(\"{your proxy url}\"); return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO); }, }, });","title":"Basic Usage"},{"location":"nodejs/proxy/#use-with-fiddler","text":"To get Fiddler to work you may need to set an environment variable. This should only be done for testing! import { SPFetchClient, SPOAuthEnv, setProxyUrl } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { // ignore certificate errors: ONLY FOR TESTING!! process.env.NODE_TLS_REJECT_UNAUTHORIZED = \"0\"; // this is my fiddler url locally setProxyUrl(\"http://127.0.0.1:8888\"); return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO); }, }, });","title":"Use with Fiddler"},{"location":"nodejs/proxy/#setproxyagent","text":"Added in 2.0.11 You need to import the setProxyAgent function from @pnp/nodejs library and call it with your proxy url. You can supply any valid proxy and it will be used. import { SPFetchClient, SPOAuthEnv, setProxyAgent } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { const myAgent = new MyAgentOfSomeType({}); // call the set proxy agent function and it will be used for all requests regardless of client setProxyAgent(myAgent); return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO); }, }, });","title":"setProxyAgent"},{"location":"nodejs/sp-extensions/","text":"@pnp/nodejs - sp extensions \u00b6 By importing anything from the @pnp/nodejs library you automatically get nodejs specific extension methods added into the sp fluent api. This article describes them. These examples use the *-commonjs version of the libraries as they target node, you can read more about the differences . IFile.getStream \u00b6 Allows you to read a response body as a nodejs PassThrough stream. // by importing the the library the node specific extensions are automatically applied import { SPFetchClient, SPNS } from \"@pnp/nodejs-commonjs\"; import { sp } from \"@pnp/sp-commonjs\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{url}\", \"{id}\", \"{secret}\"); }, }, }); // get the stream const streamResult: SPNS.IResponseBodyStream = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/file.txt\").getStream(); // see if we have a known length console.log(streamResult.knownLength); // read the stream // this is a very basic example - you can do tons more with streams in node const txt = await new Promise((resolve) => { let data = \"\"; stream.body.on(\"data\", (chunk) => data += chunk); stream.body.on(\"end\", () => resolve(data)); }); IFiles.addChunked \u00b6 Added in 2.1.0 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/files/web\"; import \"@pnp/sp/files/folder\"; import * as fs from \"fs\"; const stream = fs.createReadStream(\"{file path}\"); const files = sp.web.defaultDocumentLibrary.rootFolder.files; await files.addChunked(name, stream, null, true, 10); IFile.setStreamContentChunked \u00b6 Added in 2.1.0 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/files/web\"; import \"@pnp/sp/files/folder\"; import * as fs from \"fs\"; const stream = fs.createReadStream(\"{file path}\"); const file = sp.web.defaultDocumentLibrary.rootFolder.files..getByName(\"file-name.txt\"); await file.setStreamContentChunked(stream); Explicit import \u00b6 If you don't need to import anything from the library, but would like to include the extensions just import the library as shown. // ES Modules: import \"@pnp/nodejs\"; import \"@pnp/nodejs-commonjs\"; // get the stream const streamResult = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/file.txt\").getStream(); Accessing SP Extension Namespace \u00b6 There are classes and interfaces included in extension modules, which you can access through a namespace, \"SPNS\". import { SPNS } from \"@pnp/nodejs-commonjs\"; const parser = new SPNS.StreamParser();","title":"sp Extensions"},{"location":"nodejs/sp-extensions/#pnpnodejs-sp-extensions","text":"By importing anything from the @pnp/nodejs library you automatically get nodejs specific extension methods added into the sp fluent api. This article describes them. These examples use the *-commonjs version of the libraries as they target node, you can read more about the differences .","title":"@pnp/nodejs - sp extensions"},{"location":"nodejs/sp-extensions/#ifilegetstream","text":"Allows you to read a response body as a nodejs PassThrough stream. // by importing the the library the node specific extensions are automatically applied import { SPFetchClient, SPNS } from \"@pnp/nodejs-commonjs\"; import { sp } from \"@pnp/sp-commonjs\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{url}\", \"{id}\", \"{secret}\"); }, }, }); // get the stream const streamResult: SPNS.IResponseBodyStream = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/file.txt\").getStream(); // see if we have a known length console.log(streamResult.knownLength); // read the stream // this is a very basic example - you can do tons more with streams in node const txt = await new Promise((resolve) => { let data = \"\"; stream.body.on(\"data\", (chunk) => data += chunk); stream.body.on(\"end\", () => resolve(data)); });","title":"IFile.getStream"},{"location":"nodejs/sp-extensions/#ifilesaddchunked","text":"Added in 2.1.0 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/files/web\"; import \"@pnp/sp/files/folder\"; import * as fs from \"fs\"; const stream = fs.createReadStream(\"{file path}\"); const files = sp.web.defaultDocumentLibrary.rootFolder.files; await files.addChunked(name, stream, null, true, 10);","title":"IFiles.addChunked"},{"location":"nodejs/sp-extensions/#ifilesetstreamcontentchunked","text":"Added in 2.1.0 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/files/web\"; import \"@pnp/sp/files/folder\"; import * as fs from \"fs\"; const stream = fs.createReadStream(\"{file path}\"); const file = sp.web.defaultDocumentLibrary.rootFolder.files..getByName(\"file-name.txt\"); await file.setStreamContentChunked(stream);","title":"IFile.setStreamContentChunked"},{"location":"nodejs/sp-extensions/#explicit-import","text":"If you don't need to import anything from the library, but would like to include the extensions just import the library as shown. // ES Modules: import \"@pnp/nodejs\"; import \"@pnp/nodejs-commonjs\"; // get the stream const streamResult = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/file.txt\").getStream();","title":"Explicit import"},{"location":"nodejs/sp-extensions/#accessing-sp-extension-namespace","text":"There are classes and interfaces included in extension modules, which you can access through a namespace, \"SPNS\". import { SPNS } from \"@pnp/nodejs-commonjs\"; const parser = new SPNS.StreamParser();","title":"Accessing SP Extension Namespace"},{"location":"nodejs/sp-fetch-client/","text":"@pnp/nodejs/spfetchclient \u00b6 The SPFetchClient is used to authentication to SharePoint as a provider hosted add-in using a client and secret in nodejs. Remember it is not a good practice to expose client ids and secrets on the client and use of this class is intended for nodejs exclusively. See: How to register a legacy SharePoint application import { SPFetchClient } from \"@pnp/nodejs\"; import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\"); }, }, }); // execute a library request as normal const w = await sp.web(); console.log(JSON.stringify(w, null, 4)); Set Authentication Environment \u00b6 For some areas such as Germany, China, and US Gov clouds you need to specify a different authentication url to the service. This is done by specifying the correct SPOAuthEnv enumeration to the SPFetchClient constructor. The options are listed below. If you are not sure which option to specify the default is likely OK. SPO : (default) for all *.sharepoint.com urls China: for China hosted cloud Germany: for Germany local cloud USDef: USA Defense cloud USGov: USA Government cloud import { sp } from \"@pnp/sp/presets/all\"; import { SPFetchClient, SPOAuthEnv } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\", SPOAuthEnv.China); }, }, }); Set Realm \u00b6 In some cases automatically resolving the realm may not work. In this case you can set the realm parameter in the SPFetchClient constructor. You can determine the correct value for the realm by navigating to https://{site name}-admin.sharepoint.com/_layouts/15/TA_AllAppPrincipals.aspx and copying the GUID value that appears after the \"@\" - this is the realm id. import { sp } from \"@pnp/sp/presets/all\"; import { SPFetchClient, SPOAuthEnv } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\", SPOAuthEnv.SPO, \"{realm}\"); }, }, });","title":"SPFetchClient"},{"location":"nodejs/sp-fetch-client/#pnpnodejsspfetchclient","text":"The SPFetchClient is used to authentication to SharePoint as a provider hosted add-in using a client and secret in nodejs. Remember it is not a good practice to expose client ids and secrets on the client and use of this class is intended for nodejs exclusively. See: How to register a legacy SharePoint application import { SPFetchClient } from \"@pnp/nodejs\"; import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\"); }, }, }); // execute a library request as normal const w = await sp.web(); console.log(JSON.stringify(w, null, 4));","title":"@pnp/nodejs/spfetchclient"},{"location":"nodejs/sp-fetch-client/#set-authentication-environment","text":"For some areas such as Germany, China, and US Gov clouds you need to specify a different authentication url to the service. This is done by specifying the correct SPOAuthEnv enumeration to the SPFetchClient constructor. The options are listed below. If you are not sure which option to specify the default is likely OK. SPO : (default) for all *.sharepoint.com urls China: for China hosted cloud Germany: for Germany local cloud USDef: USA Defense cloud USGov: USA Government cloud import { sp } from \"@pnp/sp/presets/all\"; import { SPFetchClient, SPOAuthEnv } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\", SPOAuthEnv.China); }, }, });","title":"Set Authentication Environment"},{"location":"nodejs/sp-fetch-client/#set-realm","text":"In some cases automatically resolving the realm may not work. In this case you can set the realm parameter in the SPFetchClient constructor. You can determine the correct value for the realm by navigating to https://{site name}-admin.sharepoint.com/_layouts/15/TA_AllAppPrincipals.aspx and copying the GUID value that appears after the \"@\" - this is the realm id. import { sp } from \"@pnp/sp/presets/all\"; import { SPFetchClient, SPOAuthEnv } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\", SPOAuthEnv.SPO, \"{realm}\"); }, }, });","title":"Set Realm"},{"location":"odata/","text":"@pnp/queryable \u00b6 This modules contains the abstract core classes used to process odata requests. They can also be used to build your own odata library should you wish to. By sharing the core functionality across libraries we can provide a consistent API as well as ensure the core code is solid and well tested, with any updates benefitting all inheriting libraries. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable --save Library Topics \u00b6 caching core OData Batching Parsers Pipeline Queryable","title":"odata"},{"location":"odata/#pnpqueryable","text":"This modules contains the abstract core classes used to process odata requests. They can also be used to build your own odata library should you wish to. By sharing the core functionality across libraries we can provide a consistent API as well as ensure the core code is solid and well tested, with any updates benefitting all inheriting libraries.","title":"@pnp/queryable"},{"location":"odata/#getting-started","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable --save","title":"Getting Started"},{"location":"odata/#library-topics","text":"caching core OData Batching Parsers Pipeline Queryable","title":"Library Topics"},{"location":"odata/caching/","text":"@pnp/queryable/caching \u00b6 Often times data doesn't change that quickly, especially in the case of rolling up corporate news or upcoming events. These types of things can be cached for minutes if not hours. To help make caching easy you just need to insert the usingCaching method in your chain. This only applies to get requests. The usingCaching method can be used with the inBatch method as well to cache the results of batched requests. The below examples uses the @pnp/sp library as the example - but this works equally well for any library making use of the @pnp/queryable base classes, such as @pnp/graph. Basic example \u00b6 You can use the method without any additional configuration. We have made some default choices for you and will discuss ways to override them later. The code below will get items from a list, first checking the cache for the value. You can also use it with OData operators such as top and orderBy. The usingCaching() method should always be the last method in the chain before the get() (OR if you are using batching these methods can be transposed, more details below). import { sp } from \"@pnp/sp\"; const r = await sp.web.lists.getByTitle(\"Tasks\").items.usingCaching()(); console.log(r); const r2 = await sp.web.lists.getByTitle(\"Tasks\").items.top(5).orderBy(\"Modified\").usingCaching()(); console.log(r2); Globally Configure Cache Settings \u00b6 If you would not like to use the default values, but don't want to clutter your code by setting the caching values on each request you can configure custom options globally. These will be applied to all calls to usingCaching() throughout your application. import { sp } from \"@pnp/sp\"; sp.setup({ defaultCachingStore: \"session\", // or \"local\" defaultCachingTimeoutSeconds: 30, globalCacheDisable: false // or true to disable caching in case of debugging/testing }); const r = await sp.web.lists.getByTitle(\"Tasks\").items.top(5).orderBy(\"Modified\").usingCaching()(); console.log(r); Per Call Configuration \u00b6 If you prefer more verbose code or have a need to manage the cache settings on a per request basis you can include individual caching settings for each request. These settings are passed to the usingCaching method call and are defined in the following interface. If you want to use the per-request options you must include the key. export interface ICachingOptions { expiration?: Date; storeName?: \"session\" | \"local\"; key: string; } import { sp } from \"@pnp/sp\"; import { dateAdd } from \"@pnp/core\"; const r = await sp.web.lists.getByTitle(\"Tasks\").items.top(5).orderBy(\"Modified\").usingCaching({ expiration: dateAdd(new Date(), \"minute\", 20), key: \"My Key\", storeName: \"local\" })(); console.log(r); Using Batching with Caching \u00b6 You can use batching and caching together, but remember caching is only applied to get requests. When you use them together the methods can be transposed, the below example is valid. import { sp } from \"@pnp/sp\"; let batch = sp.createBatch(); sp.web.lists.inBatch(batch).usingCaching()().then(r => { console.log(r) }); sp.web.lists.getByTitle(\"Tasks\").items.usingCaching().inBatch(batch)().then(r => { console.log(r) }); batch.execute().then(() => console.log(\"All done!\")); Implement Custom Caching \u00b6 You may desire to use a different caching strategy than the one we implemented within the library. The easiest way to achieve this is to wrap the request in your custom caching functionality using the unresolved promise as needed. Here we show how to implement the Stale While Revalidate pattern as discussed here . Implement caching helper method \u00b6 We create a map to act as our cache storage and a function to wrap the request caching logic const map = new Map(); async function staleWhileRevalidate(key: string, p: Promise): Promise { if (map.has(key)) { // In Cache p.then(u => { // Update Cache once we have a result map.set(key, u); }); // Return from Cache return map.get(key); } // Not In Cache so we need to wait for the value const r = await p; // Set Cache map.set(key, r); // Return from Promise return r; } Usage \u00b6 Don't call usingCaching just apply the helper method // this one will wait for the request to finish const r1 = await staleWhileRevalidate(\"test1\", sp.web.select(\"Title\", \"Description\")()); console.log(JSON.stringify(r1, null, 2)); // this one will return the result from cache and then update the cache in the background const r2 = await staleWhileRevalidate(\"test1\", sp.web.select(\"Title\", \"Description\")()); console.log(JSON.stringify(r2, null, 2)); Wrapper Function \u00b6 You can wrap this call into a single function you can reuse within your application each time you need the web data for example. You can update the select and interface to match your needs as well. interface WebData { Title: string; Description: string; } function getWebData(): Promise { return staleWhileRevalidate(\"test1\", sp.web.select(\"Title\", \"Description\")()); } // this one will wait for the request to finish const r1 = await getWebData(); console.log(JSON.stringify(r1, null, 2)); // this one will return the result from cache and then update the cache in the background const r2 = await getWebData(); console.log(JSON.stringify(r2, null, 2));","title":"caching"},{"location":"odata/caching/#pnpqueryablecaching","text":"Often times data doesn't change that quickly, especially in the case of rolling up corporate news or upcoming events. These types of things can be cached for minutes if not hours. To help make caching easy you just need to insert the usingCaching method in your chain. This only applies to get requests. The usingCaching method can be used with the inBatch method as well to cache the results of batched requests. The below examples uses the @pnp/sp library as the example - but this works equally well for any library making use of the @pnp/queryable base classes, such as @pnp/graph.","title":"@pnp/queryable/caching"},{"location":"odata/caching/#basic-example","text":"You can use the method without any additional configuration. We have made some default choices for you and will discuss ways to override them later. The code below will get items from a list, first checking the cache for the value. You can also use it with OData operators such as top and orderBy. The usingCaching() method should always be the last method in the chain before the get() (OR if you are using batching these methods can be transposed, more details below). import { sp } from \"@pnp/sp\"; const r = await sp.web.lists.getByTitle(\"Tasks\").items.usingCaching()(); console.log(r); const r2 = await sp.web.lists.getByTitle(\"Tasks\").items.top(5).orderBy(\"Modified\").usingCaching()(); console.log(r2);","title":"Basic example"},{"location":"odata/caching/#globally-configure-cache-settings","text":"If you would not like to use the default values, but don't want to clutter your code by setting the caching values on each request you can configure custom options globally. These will be applied to all calls to usingCaching() throughout your application. import { sp } from \"@pnp/sp\"; sp.setup({ defaultCachingStore: \"session\", // or \"local\" defaultCachingTimeoutSeconds: 30, globalCacheDisable: false // or true to disable caching in case of debugging/testing }); const r = await sp.web.lists.getByTitle(\"Tasks\").items.top(5).orderBy(\"Modified\").usingCaching()(); console.log(r);","title":"Globally Configure Cache Settings"},{"location":"odata/caching/#per-call-configuration","text":"If you prefer more verbose code or have a need to manage the cache settings on a per request basis you can include individual caching settings for each request. These settings are passed to the usingCaching method call and are defined in the following interface. If you want to use the per-request options you must include the key. export interface ICachingOptions { expiration?: Date; storeName?: \"session\" | \"local\"; key: string; } import { sp } from \"@pnp/sp\"; import { dateAdd } from \"@pnp/core\"; const r = await sp.web.lists.getByTitle(\"Tasks\").items.top(5).orderBy(\"Modified\").usingCaching({ expiration: dateAdd(new Date(), \"minute\", 20), key: \"My Key\", storeName: \"local\" })(); console.log(r);","title":"Per Call Configuration"},{"location":"odata/caching/#using-batching-with-caching","text":"You can use batching and caching together, but remember caching is only applied to get requests. When you use them together the methods can be transposed, the below example is valid. import { sp } from \"@pnp/sp\"; let batch = sp.createBatch(); sp.web.lists.inBatch(batch).usingCaching()().then(r => { console.log(r) }); sp.web.lists.getByTitle(\"Tasks\").items.usingCaching().inBatch(batch)().then(r => { console.log(r) }); batch.execute().then(() => console.log(\"All done!\"));","title":"Using Batching with Caching"},{"location":"odata/caching/#implement-custom-caching","text":"You may desire to use a different caching strategy than the one we implemented within the library. The easiest way to achieve this is to wrap the request in your custom caching functionality using the unresolved promise as needed. Here we show how to implement the Stale While Revalidate pattern as discussed here .","title":"Implement Custom Caching"},{"location":"odata/caching/#implement-caching-helper-method","text":"We create a map to act as our cache storage and a function to wrap the request caching logic const map = new Map(); async function staleWhileRevalidate(key: string, p: Promise): Promise { if (map.has(key)) { // In Cache p.then(u => { // Update Cache once we have a result map.set(key, u); }); // Return from Cache return map.get(key); } // Not In Cache so we need to wait for the value const r = await p; // Set Cache map.set(key, r); // Return from Promise return r; }","title":"Implement caching helper method"},{"location":"odata/caching/#usage","text":"Don't call usingCaching just apply the helper method // this one will wait for the request to finish const r1 = await staleWhileRevalidate(\"test1\", sp.web.select(\"Title\", \"Description\")()); console.log(JSON.stringify(r1, null, 2)); // this one will return the result from cache and then update the cache in the background const r2 = await staleWhileRevalidate(\"test1\", sp.web.select(\"Title\", \"Description\")()); console.log(JSON.stringify(r2, null, 2));","title":"Usage"},{"location":"odata/caching/#wrapper-function","text":"You can wrap this call into a single function you can reuse within your application each time you need the web data for example. You can update the select and interface to match your needs as well. interface WebData { Title: string; Description: string; } function getWebData(): Promise { return staleWhileRevalidate(\"test1\", sp.web.select(\"Title\", \"Description\")()); } // this one will wait for the request to finish const r1 = await getWebData(); console.log(JSON.stringify(r1, null, 2)); // this one will return the result from cache and then update the cache in the background const r2 = await getWebData(); console.log(JSON.stringify(r2, null, 2));","title":"Wrapper Function"},{"location":"odata/core/","text":"@pnp/queryable/core \u00b6 This module contains shared interfaces and abstract classes used within the @pnp/queryable package and those items that inherit from it. ProcessHttpClientResponseException \u00b6 The exception thrown when a response is returned and cannot be processed. interface ODataParser \u00b6 Base interface used to describe a class that that will parse incoming responses. It takes a single type parameter representing the type of the value to be returned. It has two methods, one is optional: parse(r: Response): Promise - main method use to parse a response and return a Promise resolving to an object of type T hydrate?: (d: any) => T - optional method used when getting an object from the cache if it requires calling a constructor ODataParserBase \u00b6 The base class used by all parsers in the @pnp libraries. It is optional to use when creating your own custom parsers, but does contain several helper methods. Create a custom parser from ODataParserBase \u00b6 You can always create custom parsers for your projects, however it is likely you will not require this step as the default parsers should work for most cases. class MyParser extends ODataParserBase { // we need to override the parse method to do our custom stuff public parse(r: Response): Promise { // we wrap everything in a promise return new Promise((resolve, reject) => { // lets use the default error handling which returns true for no error // and will call reject with an error if one exists if (this.handleError(r, reject)) { // now we add our custom parsing here r.text().then(txt => { // here we call a made up function to parse the result // this is where we would do our parsing as required myCustomerUnencode(txt).then(v => { resolve(v); }); }); } }); } }","title":"core"},{"location":"odata/core/#pnpqueryablecore","text":"This module contains shared interfaces and abstract classes used within the @pnp/queryable package and those items that inherit from it.","title":"@pnp/queryable/core"},{"location":"odata/core/#processhttpclientresponseexception","text":"The exception thrown when a response is returned and cannot be processed.","title":"ProcessHttpClientResponseException"},{"location":"odata/core/#interface-odataparser","text":"Base interface used to describe a class that that will parse incoming responses. It takes a single type parameter representing the type of the value to be returned. It has two methods, one is optional: parse(r: Response): Promise - main method use to parse a response and return a Promise resolving to an object of type T hydrate?: (d: any) => T - optional method used when getting an object from the cache if it requires calling a constructor","title":"interface ODataParser"},{"location":"odata/core/#odataparserbase","text":"The base class used by all parsers in the @pnp libraries. It is optional to use when creating your own custom parsers, but does contain several helper methods.","title":"ODataParserBase"},{"location":"odata/core/#create-a-custom-parser-from-odataparserbase","text":"You can always create custom parsers for your projects, however it is likely you will not require this step as the default parsers should work for most cases. class MyParser extends ODataParserBase { // we need to override the parse method to do our custom stuff public parse(r: Response): Promise { // we wrap everything in a promise return new Promise((resolve, reject) => { // lets use the default error handling which returns true for no error // and will call reject with an error if one exists if (this.handleError(r, reject)) { // now we add our custom parsing here r.text().then(txt => { // here we call a made up function to parse the result // this is where we would do our parsing as required myCustomerUnencode(txt).then(v => { resolve(v); }); }); } }); } }","title":"Create a custom parser from ODataParserBase"},{"location":"odata/debug/","text":"Debugging Proxy Objects \u00b6 Because all queryables are now represented as Proxy objects you can't immediately see the properties/method of the object or the data stored about the request. In certain debugging scenarios it can help to get visibility into the object that is wrapped by the proxy. To enable this we provide a set of extensions to help. The debug extensions are added by including the import \"@pnp/queryable/debug\"; statement in your project. It should be removed for production. This module provides several methods to help with debugging Queryable Proxy objects. Unwrap \u00b6 The __unwrap() method returns the concrete Queryable instance wrapped by the Proxy. You can then examine this object in various ways or dump it to the console for debugging. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; // unwrap the underlying concrete queryable instance const unwrapped = sp.web.__unwrap(); console.log(JSON.stringify(unwrapped, null, 2)); Note: It is not supported to unwrap objects and then use them. It may work in some cases, but this behavior may change as what is contained with the Proxy is an implementation detail and should not be relied upon. Without the Proxy wrapper we make no guarantees. Data \u00b6 All of the information related to a queryable's request is contained within the \"data\" property. If you need to grab that information you can use the __data property. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; // get the underlying queryable's data const data = sp.web.__data; console.log(JSON.stringify(data, null, 2)); JSON \u00b6 You can also get a representation of the wrapped instance in JSON format consisting of all its own properties and values. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; // get the underlying queryable's as JSON const data = sp.web.__json(); console.log(JSON.stringify(data, null, 2)); Deep Trace \u00b6 Deep tracing is the ability to write every property and method access to the log. This produces VERY verbose output but can be helpful in situations where you need to trace how things are called and when within the Proxy. You enable deep tracing using the __enableDeepTrace method and disable using __disableDeepTrace . import { Logger, ConsoleListener } from \"@pnp/logging\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; Logger.subscribe(new ConsoleListener()); // grab an instance to enable deep trace const web = sp.web; // enable deep trace on the instance web.__enableDeepTrace(); const y = await web.lists(); // disable deep trace web.__disableDeepTrace(); The example above produces the following output: Message: get ::> lists Message: get ::> lists Message: get ::> toUrl Message: get ::> toUrl Message: get ::> data Message: get ::> data Message: get ::> _data Message: get ::> query Message: get ::> query Message: get ::> data Message: get ::> data Message: get ::> _data Message: get ::> _data Message: get ::> data Message: get ::> data Message: get ::> _data Message: get ::> _data Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122352) Beginning GET request (_api/web/lists) Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122352) Beginning GET request (_api/web/lists) Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122354) Sending request. Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122354) Sending request. Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124099) Completing GET request. Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124099) Completing GET request. Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124102) Returning result from pipeline. Set logging to verbose to see data. Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124102) Returning result from pipeline. Set logging to verbose to see data. Data: {} Message: get ::> __disableDeepTrace Message: get ::> __disableDeepTrace","title":"Debugging Proxy Objects"},{"location":"odata/debug/#debugging-proxy-objects","text":"Because all queryables are now represented as Proxy objects you can't immediately see the properties/method of the object or the data stored about the request. In certain debugging scenarios it can help to get visibility into the object that is wrapped by the proxy. To enable this we provide a set of extensions to help. The debug extensions are added by including the import \"@pnp/queryable/debug\"; statement in your project. It should be removed for production. This module provides several methods to help with debugging Queryable Proxy objects.","title":"Debugging Proxy Objects"},{"location":"odata/debug/#unwrap","text":"The __unwrap() method returns the concrete Queryable instance wrapped by the Proxy. You can then examine this object in various ways or dump it to the console for debugging. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; // unwrap the underlying concrete queryable instance const unwrapped = sp.web.__unwrap(); console.log(JSON.stringify(unwrapped, null, 2)); Note: It is not supported to unwrap objects and then use them. It may work in some cases, but this behavior may change as what is contained with the Proxy is an implementation detail and should not be relied upon. Without the Proxy wrapper we make no guarantees.","title":"Unwrap"},{"location":"odata/debug/#data","text":"All of the information related to a queryable's request is contained within the \"data\" property. If you need to grab that information you can use the __data property. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; // get the underlying queryable's data const data = sp.web.__data; console.log(JSON.stringify(data, null, 2));","title":"Data"},{"location":"odata/debug/#json","text":"You can also get a representation of the wrapped instance in JSON format consisting of all its own properties and values. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; // get the underlying queryable's as JSON const data = sp.web.__json(); console.log(JSON.stringify(data, null, 2));","title":"JSON"},{"location":"odata/debug/#deep-trace","text":"Deep tracing is the ability to write every property and method access to the log. This produces VERY verbose output but can be helpful in situations where you need to trace how things are called and when within the Proxy. You enable deep tracing using the __enableDeepTrace method and disable using __disableDeepTrace . import { Logger, ConsoleListener } from \"@pnp/logging\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; Logger.subscribe(new ConsoleListener()); // grab an instance to enable deep trace const web = sp.web; // enable deep trace on the instance web.__enableDeepTrace(); const y = await web.lists(); // disable deep trace web.__disableDeepTrace(); The example above produces the following output: Message: get ::> lists Message: get ::> lists Message: get ::> toUrl Message: get ::> toUrl Message: get ::> data Message: get ::> data Message: get ::> _data Message: get ::> query Message: get ::> query Message: get ::> data Message: get ::> data Message: get ::> _data Message: get ::> _data Message: get ::> data Message: get ::> data Message: get ::> _data Message: get ::> _data Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122352) Beginning GET request (_api/web/lists) Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122352) Beginning GET request (_api/web/lists) Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122354) Sending request. Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122354) Sending request. Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124099) Completing GET request. Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124099) Completing GET request. Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124102) Returning result from pipeline. Set logging to verbose to see data. Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124102) Returning result from pipeline. Set logging to verbose to see data. Data: {} Message: get ::> __disableDeepTrace Message: get ::> __disableDeepTrace","title":"Deep Trace"},{"location":"odata/extensions/","text":"Extensions \u00b6 introduced in 2.0.0 Extending is the concept of overriding or adding functionality into an object or environment without altering the underlying class instances. This can be useful for debugging, testing, or injecting custom functionality. Extensions work with any invocable . You can control any behavior of the library with extensions. Extensions do not work in ie11 compatibility mode. This is by design. Types of Extensions \u00b6 There are three types of Extensions available as well as three methods for registration. You can register any type of extension with any of the registration options. Function Extensions \u00b6 The first type is a simple function with a signature: (op: \"apply\" | \"get\" | \"has\" | \"set\", target: T, ...rest: any[]): void This function is passed the current operation as the first argument, currently one of \"apply\", \"get\", \"has\", or \"set\". The second argument is the target instance upon which the operation is being invoked. The remaining parameters vary by the operation being performed, but will match their respective ProxyHandler method signatures. Named Extensions \u00b6 Named extensions are designed to add or replace a single property or method, though you can register multiple using the same object. These extensions are defined by using an object which has the property/methods you want to override described. Registering named extensions globally will override that operation to all invokables. import { extendFactory } from \"@pnp/queryable\"; import { sp, List, Lists, IWeb, ILists, List, IList, Web } from \"@pnp/sp/presets/all\"; import { escapeQueryStrValue } from \"@pnp/sp/utils/escapeQueryStrValue\"; // create a plain object with the props and methods we want to add/change const myExtensions = { // override the lists property get lists(this: IWeb): ILists { // we will always order our lists by title and select just the Title for ALL calls (just as an example) return Lists(this).orderBy(\"Title\").select(\"Title\"); }, // override the getByTitle method getByTitle: function (this: ILists, title: string): IList { // in our example our list has moved, so we rewrite the request on the fly if (title === \"List1\") { return List(this, `getByTitle('List2')`); } else { // you can't at this point call the \"base\" method as you will end up in loop within the proxy // so you need to ensure you patch/include any original functionality you need return List(this, `getByTitle('${escapeQueryStrValue(title)}')`); } }, }; // register all the named Extensions extendFactory(Web, myExtensions); // this will use our extension to ensure the lists are ordered const lists = await sp.web.lists(); console.log(JSON.stringify(lists, null, 2)); // we will get the items from List1 but within the extension it is rewritten as List2 const items = await sp.web.lists.getByTitle(\"List1\").items(); console.log(JSON.stringify(items.length, null, 2)); ProxyHandler Extensions \u00b6 You can also register a partial ProxyHandler implementation as an extension. You can implement one or more of the ProxyHandler methods as needed. Here we implement the same override of getByTitle globally. This is the most complicated method of creating an extension and assumes an understanding of how ProxyHandlers work. import { extendFactory } from \"@pnp/queryable\"; import { sp, Lists, IWeb, ILists, Web } from \"@pnp/sp/presets/all\"; import { escapeQueryStrValue } from \"@pnp/sp/utils/escapeSingleQuote\"; const myExtensions = { get: (target, p: string | number | symbol, _receiver: any) => { switch (p) { case \"getByTitle\": return (title: string) => { // in our example our list has moved, so we rewrite the request on the fly if (title === \"LookupList\") { return List(target, `getByTitle('OrderByList')`); } else { // you can't at this point call the \"base\" method as you will end up in loop within the proxy // so you need to ensure you patch/include any original functionality you need return List(target, `getByTitle('${escapeQueryStrValue(title)}')`); } }; } }, }; extendFactory(Web, myExtensions); const lists = sp.web.lists; const items = await lists.getByTitle(\"LookupList\").items(); console.log(JSON.stringify(items.length, null, 2)); Registering Extensions \u00b6 You can register Extensions either globally, on an invocable factory, or on a per-object basis, and you can register a single extension or an array of Extensions. Global Registration \u00b6 Globally registering an extension allows you to inject functionality into every invocable that is instantiated within your application. It is important to remember that processing extensions happens on ALL property access and method invocation operations - so global extensions should be used sparingly. import { extendGlobal } from \"@pnp/queryable\"; // we can add a logging method to very verbosely track what things are called in our application extendGlobal((op: string, _target: any, ...rest: any[]): void => { switch (op) { case \"apply\": Logger.write(`${op} ::> ()`, LogLevel.Info); break; case \"has\": case \"get\": case \"set\": Logger.write(`${op} ::> ${rest[0]}`, LogLevel.Info); break; default: Logger.write(`unknown ${op}`, LogLevel.Info); } }); Factory Registration \u00b6 The pattern you will likely find most useful is the ability to extend an invocable factory. This will apply your extensions to all instances created with that factory, meaning all IWebs or ILists will have the extension methods. The example below shows how to add a property to IWeb as well as a method to IList. import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import { IWeb, Web } from \"@pnp/sp/webs\"; import { ILists, Lists } from \"@pnp/sp/lists\"; import { extendFactory } from \"@pnp/queryable\"; import { sp } from \"@pnp/sp\"; // sets up the types correctly when importing across your application declare module \"@pnp/sp/webs/types\" { // we need to extend the interface interface IWeb { orderedLists: ILists; } } // sets up the types correctly when importing across your application declare module \"@pnp/sp/lists/types\" { // we need to extend the interface interface ILists { getOrderedListsQuery: (this: ILists) => ILists; } } extendFactory(Web, { // add an ordered lists property get orderedLists(this: IWeb): ILists { return this.lists.getOrderedListsQuery(); }, }); extendFactory(Lists, { // add an ordered lists property getOrderedListsQuery(this: ILists): ILists { return this.top(10).orderBy(\"Title\").select(\"Title\"); }, }); // regardless of how we access the web and lists collections our extensions remain with all new instance based on const web = Web(\"https://tenant.sharepoint.com/sites/dev/\"); const lists1 = await web.orderedLists(); console.log(JSON.stringify(lists1, null, 2)); const lists2 = await Web(\"https://tenant.sharepoint.com/sites/dev/\").orderedLists(); console.log(JSON.stringify(lists2, null, 2)); const lists3 = await sp.web.orderedLists(); console.log(JSON.stringify(lists3, null, 2)); Instance Registration \u00b6 You can also register Extensions on a single object instance, which is often the preferred approach as it will have less of a performance impact across your whole application. This is useful for debugging, overriding methods/properties, or controlling the behavior of specific object instances. Extensions are not transferred to child objects in a fluent chain, be sure you are extending the instance you think you are. Here we show the same override operation of getByTitle on the lists collection, but safely only overriding the single instance. import { extendObj } from \"@pnp/queryable\"; import { sp, List, ILists } from \"@pnp/sp/presets/all\"; const myExtensions = { getByTitle: function (this: ILists, title: string) { // in our example our list has moved, so we rewrite the request on the fly if (title === \"List1\") { return List(this, \"getByTitle('List2')\"); } else { // you can't at this point call the \"base\" method as you will end up in loop within the proxy // so you need to ensure you patch/include any original functionality you need return List(this, `getByTitle('${escapeQueryStrValue(title)}')`); } }, }; const lists = extendObj(sp.web.lists, myExtensions); const items = await lists.getByTitle(\"LookupList\").items(); console.log(JSON.stringify(items.length, null, 2)); Enable & Disable Extensions and Clear Global Extensions \u00b6 Extensions are automatically enabled when you set an extension through any of the above outlined methods. You can disable and enable extensions on demand if needed. import { enableExtensions, disableExtensions, clearGlobalExtensions } from \"@pnp/queryable\"; // disable Extensions disableExtensions(); // enable Extensions enableExtensions(); // clear all the globally registered extensions clearGlobalExtensions(); Order of Operations \u00b6 It is important to understand the order in which extensions are executed and when a value is returned. Instance extensions* are always called first, followed by global Extensions - in both cases they are called in the order they were registered. This allows you to perhaps have some global functionality while maintaining the ability to override it again at the instance level. IF an extension returns a value other than undefined that value is returned and no other extensions are processed. *extensions applied via an extended factory are considered instance extensions","title":"Extending an OData library"},{"location":"odata/extensions/#extensions","text":"introduced in 2.0.0 Extending is the concept of overriding or adding functionality into an object or environment without altering the underlying class instances. This can be useful for debugging, testing, or injecting custom functionality. Extensions work with any invocable . You can control any behavior of the library with extensions. Extensions do not work in ie11 compatibility mode. This is by design.","title":"Extensions"},{"location":"odata/extensions/#types-of-extensions","text":"There are three types of Extensions available as well as three methods for registration. You can register any type of extension with any of the registration options.","title":"Types of Extensions"},{"location":"odata/extensions/#function-extensions","text":"The first type is a simple function with a signature: (op: \"apply\" | \"get\" | \"has\" | \"set\", target: T, ...rest: any[]): void This function is passed the current operation as the first argument, currently one of \"apply\", \"get\", \"has\", or \"set\". The second argument is the target instance upon which the operation is being invoked. The remaining parameters vary by the operation being performed, but will match their respective ProxyHandler method signatures.","title":"Function Extensions"},{"location":"odata/extensions/#named-extensions","text":"Named extensions are designed to add or replace a single property or method, though you can register multiple using the same object. These extensions are defined by using an object which has the property/methods you want to override described. Registering named extensions globally will override that operation to all invokables. import { extendFactory } from \"@pnp/queryable\"; import { sp, List, Lists, IWeb, ILists, List, IList, Web } from \"@pnp/sp/presets/all\"; import { escapeQueryStrValue } from \"@pnp/sp/utils/escapeQueryStrValue\"; // create a plain object with the props and methods we want to add/change const myExtensions = { // override the lists property get lists(this: IWeb): ILists { // we will always order our lists by title and select just the Title for ALL calls (just as an example) return Lists(this).orderBy(\"Title\").select(\"Title\"); }, // override the getByTitle method getByTitle: function (this: ILists, title: string): IList { // in our example our list has moved, so we rewrite the request on the fly if (title === \"List1\") { return List(this, `getByTitle('List2')`); } else { // you can't at this point call the \"base\" method as you will end up in loop within the proxy // so you need to ensure you patch/include any original functionality you need return List(this, `getByTitle('${escapeQueryStrValue(title)}')`); } }, }; // register all the named Extensions extendFactory(Web, myExtensions); // this will use our extension to ensure the lists are ordered const lists = await sp.web.lists(); console.log(JSON.stringify(lists, null, 2)); // we will get the items from List1 but within the extension it is rewritten as List2 const items = await sp.web.lists.getByTitle(\"List1\").items(); console.log(JSON.stringify(items.length, null, 2));","title":"Named Extensions"},{"location":"odata/extensions/#proxyhandler-extensions","text":"You can also register a partial ProxyHandler implementation as an extension. You can implement one or more of the ProxyHandler methods as needed. Here we implement the same override of getByTitle globally. This is the most complicated method of creating an extension and assumes an understanding of how ProxyHandlers work. import { extendFactory } from \"@pnp/queryable\"; import { sp, Lists, IWeb, ILists, Web } from \"@pnp/sp/presets/all\"; import { escapeQueryStrValue } from \"@pnp/sp/utils/escapeSingleQuote\"; const myExtensions = { get: (target, p: string | number | symbol, _receiver: any) => { switch (p) { case \"getByTitle\": return (title: string) => { // in our example our list has moved, so we rewrite the request on the fly if (title === \"LookupList\") { return List(target, `getByTitle('OrderByList')`); } else { // you can't at this point call the \"base\" method as you will end up in loop within the proxy // so you need to ensure you patch/include any original functionality you need return List(target, `getByTitle('${escapeQueryStrValue(title)}')`); } }; } }, }; extendFactory(Web, myExtensions); const lists = sp.web.lists; const items = await lists.getByTitle(\"LookupList\").items(); console.log(JSON.stringify(items.length, null, 2));","title":"ProxyHandler Extensions"},{"location":"odata/extensions/#registering-extensions","text":"You can register Extensions either globally, on an invocable factory, or on a per-object basis, and you can register a single extension or an array of Extensions.","title":"Registering Extensions"},{"location":"odata/extensions/#global-registration","text":"Globally registering an extension allows you to inject functionality into every invocable that is instantiated within your application. It is important to remember that processing extensions happens on ALL property access and method invocation operations - so global extensions should be used sparingly. import { extendGlobal } from \"@pnp/queryable\"; // we can add a logging method to very verbosely track what things are called in our application extendGlobal((op: string, _target: any, ...rest: any[]): void => { switch (op) { case \"apply\": Logger.write(`${op} ::> ()`, LogLevel.Info); break; case \"has\": case \"get\": case \"set\": Logger.write(`${op} ::> ${rest[0]}`, LogLevel.Info); break; default: Logger.write(`unknown ${op}`, LogLevel.Info); } });","title":"Global Registration"},{"location":"odata/extensions/#factory-registration","text":"The pattern you will likely find most useful is the ability to extend an invocable factory. This will apply your extensions to all instances created with that factory, meaning all IWebs or ILists will have the extension methods. The example below shows how to add a property to IWeb as well as a method to IList. import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import { IWeb, Web } from \"@pnp/sp/webs\"; import { ILists, Lists } from \"@pnp/sp/lists\"; import { extendFactory } from \"@pnp/queryable\"; import { sp } from \"@pnp/sp\"; // sets up the types correctly when importing across your application declare module \"@pnp/sp/webs/types\" { // we need to extend the interface interface IWeb { orderedLists: ILists; } } // sets up the types correctly when importing across your application declare module \"@pnp/sp/lists/types\" { // we need to extend the interface interface ILists { getOrderedListsQuery: (this: ILists) => ILists; } } extendFactory(Web, { // add an ordered lists property get orderedLists(this: IWeb): ILists { return this.lists.getOrderedListsQuery(); }, }); extendFactory(Lists, { // add an ordered lists property getOrderedListsQuery(this: ILists): ILists { return this.top(10).orderBy(\"Title\").select(\"Title\"); }, }); // regardless of how we access the web and lists collections our extensions remain with all new instance based on const web = Web(\"https://tenant.sharepoint.com/sites/dev/\"); const lists1 = await web.orderedLists(); console.log(JSON.stringify(lists1, null, 2)); const lists2 = await Web(\"https://tenant.sharepoint.com/sites/dev/\").orderedLists(); console.log(JSON.stringify(lists2, null, 2)); const lists3 = await sp.web.orderedLists(); console.log(JSON.stringify(lists3, null, 2));","title":"Factory Registration"},{"location":"odata/extensions/#instance-registration","text":"You can also register Extensions on a single object instance, which is often the preferred approach as it will have less of a performance impact across your whole application. This is useful for debugging, overriding methods/properties, or controlling the behavior of specific object instances. Extensions are not transferred to child objects in a fluent chain, be sure you are extending the instance you think you are. Here we show the same override operation of getByTitle on the lists collection, but safely only overriding the single instance. import { extendObj } from \"@pnp/queryable\"; import { sp, List, ILists } from \"@pnp/sp/presets/all\"; const myExtensions = { getByTitle: function (this: ILists, title: string) { // in our example our list has moved, so we rewrite the request on the fly if (title === \"List1\") { return List(this, \"getByTitle('List2')\"); } else { // you can't at this point call the \"base\" method as you will end up in loop within the proxy // so you need to ensure you patch/include any original functionality you need return List(this, `getByTitle('${escapeQueryStrValue(title)}')`); } }, }; const lists = extendObj(sp.web.lists, myExtensions); const items = await lists.getByTitle(\"LookupList\").items(); console.log(JSON.stringify(items.length, null, 2));","title":"Instance Registration"},{"location":"odata/extensions/#enable-disable-extensions-and-clear-global-extensions","text":"Extensions are automatically enabled when you set an extension through any of the above outlined methods. You can disable and enable extensions on demand if needed. import { enableExtensions, disableExtensions, clearGlobalExtensions } from \"@pnp/queryable\"; // disable Extensions disableExtensions(); // enable Extensions enableExtensions(); // clear all the globally registered extensions clearGlobalExtensions();","title":"Enable & Disable Extensions and Clear Global Extensions"},{"location":"odata/extensions/#order-of-operations","text":"It is important to understand the order in which extensions are executed and when a value is returned. Instance extensions* are always called first, followed by global Extensions - in both cases they are called in the order they were registered. This allows you to perhaps have some global functionality while maintaining the ability to override it again at the instance level. IF an extension returns a value other than undefined that value is returned and no other extensions are processed. *extensions applied via an extended factory are considered instance extensions","title":"Order of Operations"},{"location":"odata/odata-batch/","text":"@pnp/queryable/odatabatch \u00b6 This module contains an abstract class used as a base when inheriting libraries support batching. ODataBatchRequestInfo \u00b6 This interface defines what each batch needs to know about each request. It is generic in that any library can provide the information but will be responsible for processing that info by implementing the abstract executeImpl method. ODataBatch \u00b6 Base class for building batching support for a library inheriting from @pnp/queryable. You can see implementations of this abstract class in the @pnp/sp and @pnp/graph modules.","title":"OData Batching"},{"location":"odata/odata-batch/#pnpqueryableodatabatch","text":"This module contains an abstract class used as a base when inheriting libraries support batching.","title":"@pnp/queryable/odatabatch"},{"location":"odata/odata-batch/#odatabatchrequestinfo","text":"This interface defines what each batch needs to know about each request. It is generic in that any library can provide the information but will be responsible for processing that info by implementing the abstract executeImpl method.","title":"ODataBatchRequestInfo"},{"location":"odata/odata-batch/#odatabatch","text":"Base class for building batching support for a library inheriting from @pnp/queryable. You can see implementations of this abstract class in the @pnp/sp and @pnp/graph modules.","title":"ODataBatch"},{"location":"odata/parsers/","text":"@pnp/queryable/parsers \u00b6 This modules contains a set of generic parsers. These can be used or extended as needed, though it is likely in most cases the default parser will be all you need. ODataDefaultParser \u00b6 The simplest parser used to transform a Response into its JSON representation. The default parser will handle errors in a consistent manner throwing an HttpRequestError instance. This class extends Error and adds the response, status, and statusText properties. The response object is unread. You can use this custom error as shown below to gather more information about what went wrong in the request. import { sp } from \"@pnp/sp\"; import { JSONParser } from \"@pnp/queryable\"; try { const parser = new JSONParser(); // this always throws a 404 error await sp.web.getList(\"doesn't exist\").get(parser); } catch (e) { // we can check for the property \"isHttpRequestError\" to see if this is an instance of our class // this gets by all the many limitations of subclassing Error and type detection in JavaScript if (e.hasOwnProperty(\"isHttpRequestError\")) { console.log(\"e is HttpRequestError\"); // now we can access the various properties and make use of the response object. // at this point the body is unread console.log(`status: ${e.status}`); console.log(`statusText: ${e.statusText}`); const json = await e.response.clone().json(); console.log(JSON.stringify(json)); const text = await e.response.clone().text(); console.log(text); const headers = e.response.headers; } console.error(e); } TextParser \u00b6 Specialized parser used to parse the response using the .text() method with no other processing. Used primarily for files. BlobParser \u00b6 Specialized parser used to parse the response using the .blob() method with no other processing. Used primarily for files. JSONParser \u00b6 Specialized parser used to parse the response using the .json() method with no other processing. Used primarily for files. BufferParser \u00b6 Specialized parser used to parse the response using the .arrayBuffer() [node] for .buffer() [browser] method with no other processing. Used primarily for files. LambdaParser \u00b6 Allows you to pass in any handler function you want, called if the request does not result in an error that transforms the raw, unread request into the result type. import { LambdaParser } from \"@pnp/queryable\"; import { sp } from \"@pnp/sp\"; // here a simple parser duplicating the functionality of the JSONParser const parser = new LambdaParser((r: Response) => r.json()); const webDataJson = await sp.web.get(parser); console.log(webDataJson);","title":"Parsers"},{"location":"odata/parsers/#pnpqueryableparsers","text":"This modules contains a set of generic parsers. These can be used or extended as needed, though it is likely in most cases the default parser will be all you need.","title":"@pnp/queryable/parsers"},{"location":"odata/parsers/#odatadefaultparser","text":"The simplest parser used to transform a Response into its JSON representation. The default parser will handle errors in a consistent manner throwing an HttpRequestError instance. This class extends Error and adds the response, status, and statusText properties. The response object is unread. You can use this custom error as shown below to gather more information about what went wrong in the request. import { sp } from \"@pnp/sp\"; import { JSONParser } from \"@pnp/queryable\"; try { const parser = new JSONParser(); // this always throws a 404 error await sp.web.getList(\"doesn't exist\").get(parser); } catch (e) { // we can check for the property \"isHttpRequestError\" to see if this is an instance of our class // this gets by all the many limitations of subclassing Error and type detection in JavaScript if (e.hasOwnProperty(\"isHttpRequestError\")) { console.log(\"e is HttpRequestError\"); // now we can access the various properties and make use of the response object. // at this point the body is unread console.log(`status: ${e.status}`); console.log(`statusText: ${e.statusText}`); const json = await e.response.clone().json(); console.log(JSON.stringify(json)); const text = await e.response.clone().text(); console.log(text); const headers = e.response.headers; } console.error(e); }","title":"ODataDefaultParser"},{"location":"odata/parsers/#textparser","text":"Specialized parser used to parse the response using the .text() method with no other processing. Used primarily for files.","title":"TextParser"},{"location":"odata/parsers/#blobparser","text":"Specialized parser used to parse the response using the .blob() method with no other processing. Used primarily for files.","title":"BlobParser"},{"location":"odata/parsers/#jsonparser","text":"Specialized parser used to parse the response using the .json() method with no other processing. Used primarily for files.","title":"JSONParser"},{"location":"odata/parsers/#bufferparser","text":"Specialized parser used to parse the response using the .arrayBuffer() [node] for .buffer() [browser] method with no other processing. Used primarily for files.","title":"BufferParser"},{"location":"odata/parsers/#lambdaparser","text":"Allows you to pass in any handler function you want, called if the request does not result in an error that transforms the raw, unread request into the result type. import { LambdaParser } from \"@pnp/queryable\"; import { sp } from \"@pnp/sp\"; // here a simple parser duplicating the functionality of the JSONParser const parser = new LambdaParser((r: Response) => r.json()); const webDataJson = await sp.web.get(parser); console.log(webDataJson);","title":"LambdaParser"},{"location":"odata/pipeline/","text":"@pnp/queryable/pipeline \u00b6 All of the odata requests processed by @pnp/queryable pass through an extensible request pipeline. Each request is executed in a specific request context defined by the RequestContext interface with the type parameter representing the type ultimately returned at the end a successful processing through the pipeline. Unless you are writing a pipeline method it is unlikely you will ever interact directly with the request pipeline. interface RequestContext \u00b6 The interface that defines the context within which all requests are executed. Note that the pipeline methods to be executed are part of the context. This allows full control over the methods called during a request, and allows for the insertion of any custom methods required. interface RequestContext { batch: ODataBatch; batchDependency: () => void; cachingOptions: ICachingOptions; hasResult?: boolean; isBatched: boolean; isCached: boolean; options: FetchOptions; parser: ODataParser; pipeline: Array<(c: RequestContext) => Promise>>; requestAbsoluteUrl: string; requestId: string; result?: T; verb: string; clientFactory: () => RequestClient; } requestPipelineMethod decorator \u00b6 The requestPipelineMethod decorator is used to tag a pipeline method and add functionality to bypass processing if a result is already present in the pipeline. If you would like your method to always run regardless of the existence of a result you can pass true to ensure it will always run. Each pipeline method takes a single argument of the current RequestContext and returns a promise resolving to the RequestContext updated as needed. @requestPipelineMethod(true) public static myPipelineMethod(context: RequestContext): Promise> { return new Promise>(resolve => { // do something resolve(context); }); } Default Pipeline \u00b6 logs the start of the request checks the cache for a value based on the context's cache settings sends the request if no value from found in the cache logs the end of the request","title":"Pipeline"},{"location":"odata/pipeline/#pnpqueryablepipeline","text":"All of the odata requests processed by @pnp/queryable pass through an extensible request pipeline. Each request is executed in a specific request context defined by the RequestContext interface with the type parameter representing the type ultimately returned at the end a successful processing through the pipeline. Unless you are writing a pipeline method it is unlikely you will ever interact directly with the request pipeline.","title":"@pnp/queryable/pipeline"},{"location":"odata/pipeline/#interface-requestcontextt","text":"The interface that defines the context within which all requests are executed. Note that the pipeline methods to be executed are part of the context. This allows full control over the methods called during a request, and allows for the insertion of any custom methods required. interface RequestContext { batch: ODataBatch; batchDependency: () => void; cachingOptions: ICachingOptions; hasResult?: boolean; isBatched: boolean; isCached: boolean; options: FetchOptions; parser: ODataParser; pipeline: Array<(c: RequestContext) => Promise>>; requestAbsoluteUrl: string; requestId: string; result?: T; verb: string; clientFactory: () => RequestClient; }","title":"interface RequestContext<T>"},{"location":"odata/pipeline/#requestpipelinemethod-decorator","text":"The requestPipelineMethod decorator is used to tag a pipeline method and add functionality to bypass processing if a result is already present in the pipeline. If you would like your method to always run regardless of the existence of a result you can pass true to ensure it will always run. Each pipeline method takes a single argument of the current RequestContext and returns a promise resolving to the RequestContext updated as needed. @requestPipelineMethod(true) public static myPipelineMethod(context: RequestContext): Promise> { return new Promise>(resolve => { // do something resolve(context); }); }","title":"requestPipelineMethod decorator"},{"location":"odata/pipeline/#default-pipeline","text":"logs the start of the request checks the cache for a value based on the context's cache settings sends the request if no value from found in the cache logs the end of the request","title":"Default Pipeline"},{"location":"odata/queryable/","text":"@pnp/queryable/queryable \u00b6 The Queryable class is the base class for all of the libraries building fluent request apis. abstract class ODataQueryable \u00b6 This class takes a single type parameter representing the type of the batch implementation object. If your api will not support batching you can create a dummy class here and simply not use the batching calls. Properties \u00b6 query \u00b6 Provides access to the query string builder for this url Public Methods \u00b6 concat \u00b6 Directly concatenates the supplied string to the current url, not normalizing \"/\" chars configure \u00b6 Sets custom options for current object and all derived objects accessible via chaining import { ConfigOptions } from \"@pnp/queryable\"; import { sp } from \"@pnp/sp\"; const headers: ConfigOptions = { Accept: 'application/json;odata=nometadata' }; // here we use configure to set the headers value for all child requests of the list instance const list = sp.web.lists.getByTitle(\"List1\").configure({ headers }); // this will use the values set in configure list.items().then(items => console.log(JSON.stringify(items, null, 2)); For reference the ConfigOptions interface is shown below: export interface ConfigOptions { headers?: string[][] | { [key: string]: string } | Headers; mode?: \"navigate\" | \"same-origin\" | \"no-cors\" | \"cors\"; credentials?: \"omit\" | \"same-origin\" | \"include\"; cache?: \"default\" | \"no-store\" | \"reload\" | \"no-cache\" | \"force-cache\" | \"only-if-cached\"; } configureFrom \u00b6 Sets custom options from another queryable instance's options. Identical to configure except the options are derived from the supplied instance. usingCaching \u00b6 Enables caching for this request. See caching for more details. import { sp } from \"@pnp/sp\" sp.web.usingCaching()().then(...); inBatch \u00b6 Adds this query to the supplied batch toUrl \u00b6 Gets the current url abstract toUrlAndQuery() \u00b6 When implemented by an inheriting class will build the full url with appropriate query string used to make the actual request get \u00b6 Execute the current request. Takes an optional type parameter allowing for the typing of the value or the user of parsers that will create specific object instances.","title":"Queryable"},{"location":"odata/queryable/#pnpqueryablequeryable","text":"The Queryable class is the base class for all of the libraries building fluent request apis.","title":"@pnp/queryable/queryable"},{"location":"odata/queryable/#abstract-class-odataqueryablebatchtype-extends-odatabatch","text":"This class takes a single type parameter representing the type of the batch implementation object. If your api will not support batching you can create a dummy class here and simply not use the batching calls.","title":"abstract class ODataQueryable<BatchType extends ODataBatch>"},{"location":"odata/queryable/#properties","text":"","title":"Properties"},{"location":"odata/queryable/#query","text":"Provides access to the query string builder for this url","title":"query"},{"location":"odata/queryable/#public-methods","text":"","title":"Public Methods"},{"location":"odata/queryable/#concat","text":"Directly concatenates the supplied string to the current url, not normalizing \"/\" chars","title":"concat"},{"location":"odata/queryable/#configure","text":"Sets custom options for current object and all derived objects accessible via chaining import { ConfigOptions } from \"@pnp/queryable\"; import { sp } from \"@pnp/sp\"; const headers: ConfigOptions = { Accept: 'application/json;odata=nometadata' }; // here we use configure to set the headers value for all child requests of the list instance const list = sp.web.lists.getByTitle(\"List1\").configure({ headers }); // this will use the values set in configure list.items().then(items => console.log(JSON.stringify(items, null, 2)); For reference the ConfigOptions interface is shown below: export interface ConfigOptions { headers?: string[][] | { [key: string]: string } | Headers; mode?: \"navigate\" | \"same-origin\" | \"no-cors\" | \"cors\"; credentials?: \"omit\" | \"same-origin\" | \"include\"; cache?: \"default\" | \"no-store\" | \"reload\" | \"no-cache\" | \"force-cache\" | \"only-if-cached\"; }","title":"configure"},{"location":"odata/queryable/#configurefrom","text":"Sets custom options from another queryable instance's options. Identical to configure except the options are derived from the supplied instance.","title":"configureFrom"},{"location":"odata/queryable/#usingcaching","text":"Enables caching for this request. See caching for more details. import { sp } from \"@pnp/sp\" sp.web.usingCaching()().then(...);","title":"usingCaching"},{"location":"odata/queryable/#inbatch","text":"Adds this query to the supplied batch","title":"inBatch"},{"location":"odata/queryable/#tourl","text":"Gets the current url","title":"toUrl"},{"location":"odata/queryable/#abstract-tourlandquery","text":"When implemented by an inheriting class will build the full url with appropriate query string used to make the actual request","title":"abstract toUrlAndQuery()"},{"location":"odata/queryable/#get","text":"Execute the current request. Takes an optional type parameter allowing for the typing of the value or the user of parsers that will create specific object instances.","title":"get"},{"location":"pnpjs/","text":"PnPjs \u00b6 This package is a rollup package of all the other libraries for scenarios where you would prefer to access all of the code from a single file. Examples would be importing a single file into a script editor webpart or using the library in other ways that benefit from a single file. You will not be able to take advantage of selective imports using this bundle. Our recommendation is to import the packages directly into your project, or to create a custom bundle . This package is mostly provided to help folks with backward-compatibility needs. Script Editor Webpart \u00b6 The below is an example of using the pnp.js bundle within a Script Editor webpart. This script editor example is provided for folks on older version of SharePoint - when possible your first choice is SharePoint Framework. You will need to grab the pnp.js bundle file from the dist folder of the pnpjs package and upload it to a location where you can reference it from without your script editor webparts. *This is included as a reference for backward compatibility. The script editor webpart is no longer available in SharePoint online. In addition, see our General Statement on Polyfills and IE11
      Access Library Features \u00b6 Within the bundle all of the classes and methods are exported at the root object, with the exports from sp and graph libraries contained with NS variables to avoid naming conflicts. So if you need to access say the \"Web\" factory you can do so: const web = pnp.SPNS.Web(\"https://something.sharepoint.com\"); const lists = await web.lists(); pnp.GraphNS.* Individual libraries can also be accessed for their exports: pnp.Logger.subscribe(new pnp.ConsoleListener()); pnp.log.write(\"hello\");","title":"pnpjs"},{"location":"pnpjs/#pnpjs","text":"This package is a rollup package of all the other libraries for scenarios where you would prefer to access all of the code from a single file. Examples would be importing a single file into a script editor webpart or using the library in other ways that benefit from a single file. You will not be able to take advantage of selective imports using this bundle. Our recommendation is to import the packages directly into your project, or to create a custom bundle . This package is mostly provided to help folks with backward-compatibility needs.","title":"PnPjs"},{"location":"pnpjs/#script-editor-webpart","text":"The below is an example of using the pnp.js bundle within a Script Editor webpart. This script editor example is provided for folks on older version of SharePoint - when possible your first choice is SharePoint Framework. You will need to grab the pnp.js bundle file from the dist folder of the pnpjs package and upload it to a location where you can reference it from without your script editor webparts. *This is included as a reference for backward compatibility. The script editor webpart is no longer available in SharePoint online. In addition, see our General Statement on Polyfills and IE11
      ","title":"Script Editor Webpart"},{"location":"pnpjs/#access-library-features","text":"Within the bundle all of the classes and methods are exported at the root object, with the exports from sp and graph libraries contained with NS variables to avoid naming conflicts. So if you need to access say the \"Web\" factory you can do so: const web = pnp.SPNS.Web(\"https://something.sharepoint.com\"); const lists = await web.lists(); pnp.GraphNS.* Individual libraries can also be accessed for their exports: pnp.Logger.subscribe(new pnp.ConsoleListener()); pnp.log.write(\"hello\");","title":"Access Library Features"},{"location":"sp/","text":"@pnp/sp \u00b6 This package contains the fluent api used to call the SharePoint rest services. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/sp --save Import the library into your application and access the root sp object import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; (function main() { // here we will load the current web's title const w = await sp.web.select(\"Title\")(); console.log(`Web Title: ${w.Title}`); )() Getting Started: SharePoint Framework \u00b6 Install the library and required dependencies npm install @pnp/sp --save Import the library into your application, update OnInit, and access the root sp object in render import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present sp.setup({ spfxContext: this.context }); }); } // ... public render(): void { // A simple loading message this.domElement.innerHTML = `Loading...`; const w = await sp.web.select(\"Title\")(); this.domElement.innerHTML = `Web Title: ${w.Title}`; } Getting Started: Nodejs \u00b6 Install the library and required dependencies npm install @pnp/sp @pnp/nodejs --save Import the library into your application, setup the node client, make a request import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { SPFetchClient } from \"@pnp/nodejs\"; // do this once per page load sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{your site url}\", \"{your client id}\", \"{your client secret}\"); }, }, }); // now make any calls you need using the configured client const w = await sp.web.select(\"Title\")(); console.log(`Web Title: ${w.Title}`);","title":"sp"},{"location":"sp/#pnpsp","text":"This package contains the fluent api used to call the SharePoint rest services.","title":"@pnp/sp"},{"location":"sp/#getting-started","text":"Install the library and required dependencies npm install @pnp/sp --save Import the library into your application and access the root sp object import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; (function main() { // here we will load the current web's title const w = await sp.web.select(\"Title\")(); console.log(`Web Title: ${w.Title}`); )()","title":"Getting Started"},{"location":"sp/#getting-started-sharepoint-framework","text":"Install the library and required dependencies npm install @pnp/sp --save Import the library into your application, update OnInit, and access the root sp object in render import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present sp.setup({ spfxContext: this.context }); }); } // ... public render(): void { // A simple loading message this.domElement.innerHTML = `Loading...`; const w = await sp.web.select(\"Title\")(); this.domElement.innerHTML = `Web Title: ${w.Title}`; }","title":"Getting Started: SharePoint Framework"},{"location":"sp/#getting-started-nodejs","text":"Install the library and required dependencies npm install @pnp/sp @pnp/nodejs --save Import the library into your application, setup the node client, make a request import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { SPFetchClient } from \"@pnp/nodejs\"; // do this once per page load sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{your site url}\", \"{your client id}\", \"{your client secret}\"); }, }, }); // now make any calls you need using the configured client const w = await sp.web.select(\"Title\")(); console.log(`Web Title: ${w.Title}`);","title":"Getting Started: Nodejs"},{"location":"sp/alias-parameters/","text":"@pnp/sp - Aliased Parameters \u00b6 Within the @pnp/sp api you can alias any of the parameters so they will be written into the querystring. This is most helpful if you are hitting up against the url length limits when working with files and folders. To alias a parameter you include the label name, a separator (\"::\") and the value in the string. You also need to prepend a \"!\" to the string to trigger the replacement. You can see this below, as well as the string that will be generated. Labels must start with a \"@\" followed by a letter. It is also your responsibility to ensure that the aliases you supply do not conflict, for example if you use \"@p1\" you should use \"@p2\" for a second parameter alias in the same query. Construct a parameter alias \u00b6 Pattern: !@{label name}::{value} Example: \"!@p1::\\sites\\dev\" or \"!@p2::\\text.txt\" Example without aliasing \u00b6 import { sp } from \"@pnp/sp/presets/all\"; // still works as expected, no aliasing const query = sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/\").files.select(\"Title\").top(3); console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files?$select=Title&$top=3 const r = await query(); console.log(r);; Example with aliasing \u00b6 import { sp } from \"@pnp/sp/presets/all\"; // same query with aliasing const query = sp.web.getFolderByServerRelativeUrl(\"!@p1::/sites/dev/Shared Documents/\").files.select(\"Title\").top(3); console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3 const r = await query(); console.log(r); Example with aliasing and batching \u00b6 Aliasing is supported with batching as well: import { sp } from \"@pnp/sp/presets/all\"; // same query with aliasing and batching const batch = sp.web.createBatch(); const query = sp.web.getFolderByServerRelativeUrl(\"!@p1::/sites/dev/Shared Documents/\").files.select(\"Title\").top(3); console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3 query.inBatch(batch)().then(r => { console.log(r); }); batch.execute();","title":"Alias Parameters"},{"location":"sp/alias-parameters/#pnpsp-aliased-parameters","text":"Within the @pnp/sp api you can alias any of the parameters so they will be written into the querystring. This is most helpful if you are hitting up against the url length limits when working with files and folders. To alias a parameter you include the label name, a separator (\"::\") and the value in the string. You also need to prepend a \"!\" to the string to trigger the replacement. You can see this below, as well as the string that will be generated. Labels must start with a \"@\" followed by a letter. It is also your responsibility to ensure that the aliases you supply do not conflict, for example if you use \"@p1\" you should use \"@p2\" for a second parameter alias in the same query.","title":"@pnp/sp - Aliased Parameters"},{"location":"sp/alias-parameters/#construct-a-parameter-alias","text":"Pattern: !@{label name}::{value} Example: \"!@p1::\\sites\\dev\" or \"!@p2::\\text.txt\"","title":"Construct a parameter alias"},{"location":"sp/alias-parameters/#example-without-aliasing","text":"import { sp } from \"@pnp/sp/presets/all\"; // still works as expected, no aliasing const query = sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/\").files.select(\"Title\").top(3); console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files?$select=Title&$top=3 const r = await query(); console.log(r);;","title":"Example without aliasing"},{"location":"sp/alias-parameters/#example-with-aliasing","text":"import { sp } from \"@pnp/sp/presets/all\"; // same query with aliasing const query = sp.web.getFolderByServerRelativeUrl(\"!@p1::/sites/dev/Shared Documents/\").files.select(\"Title\").top(3); console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3 const r = await query(); console.log(r);","title":"Example with aliasing"},{"location":"sp/alias-parameters/#example-with-aliasing-and-batching","text":"Aliasing is supported with batching as well: import { sp } from \"@pnp/sp/presets/all\"; // same query with aliasing and batching const batch = sp.web.createBatch(); const query = sp.web.getFolderByServerRelativeUrl(\"!@p1::/sites/dev/Shared Documents/\").files.select(\"Title\").top(3); console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3 query.inBatch(batch)().then(r => { console.log(r); }); batch.execute();","title":"Example with aliasing and batching"},{"location":"sp/alm/","text":"@pnp/sp/appcatalog \u00b6 The ALM api allows you to manage app installations both in the tenant app catalog and individual site app catalogs. Some of the methods are still in beta and as such may change in the future. This article outlines how to call this api using @pnp/sp. Remember all these actions are bound by permissions so it is likely most users will not have the rights to perform these ALM actions. Understanding the App Catalog Hierarchy \u00b6 Before you begin provisioning applications it is important to understand the relationship between a local web catalog and the tenant app catalog. Some of the methods described below only work within the context of the tenant app catalog web, such as adding an app to the catalog and the app actions retract, remove, and deploy. You can install, uninstall, and upgrade an app in any web. Read more in the official documentation . Referencing an App Catalog \u00b6 There are several ways using @pnp/sp to get a reference to an app catalog. These methods are to provide you the greatest amount of flexibility in gaining access to the app catalog. Ultimately each method produces an AppCatalog instance differentiated only by the web to which it points. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/appcatalog\"; import \"@pnp/sp/webs\"; // get the current context web's app catalog const catalog = await sp.web.getAppCatalog()(); // you can also chain off the app catalog const apps = await sp.web.getAppCatalog()(); console.log(apps); import { sp } from \"@pnp/sp\"; import \"@pnp/sp/appcatalog\"; import \"@pnp/sp/webs\"; // you can get the tenant app catalog (or any app catalog) by using the getTenantAppCatalogWeb method const appCatWeb = await sp.getTenantAppCatalogWeb()(); const appCatalog = await appCatWeb.getAppCatalog()(); // you can get the tenant app catalog (or any app catalog) by passing in a url // get the tenant app catalog const tenantCatalog = await sp.web.getAppCatalog(\"https://mytenant.sharepoint.com/sites/appcatalog\")(); // get a different app catalog const catalog = await sp.web.getAppCatalog(\"https://mytenant.sharepoint.com/sites/anothersite\")(); // alternatively you can create a new app catalog instance directly by importing the AppCatalog class import { IAppCatalog, AppCatalog } from '@pnp/sp/appcatalog'; const catalog: IAppCatalog = await AppCatalog(\"https://mytenant.sharepoint.com/sites/apps\")(); // and finally you can combine use of the Web and AppCatalog classes to create an AppCatalog instance from an existing Web import { Web } from '@pnp/sp/webs'; import { AppCatalog } from '@pnp/sp/appcatalog'; const web = Web(\"https://mytenant.sharepoint.com/sites/apps\"); const catalog = await AppCatalog(web)(); The following examples make use of a variable \"catalog\" which is assumed to represent an AppCatalog instance obtained using one of the above methods, supporting code is omitted for brevity. List Available Apps \u00b6 The AppCatalog is itself a queryable collection so you can query this object directly to get a list of available apps. Also, the odata operators work on the catalog to sort, filter, and select. // get available apps await catalog(); // get available apps selecting two fields await catalog.select(\"Title\", \"Deployed\")(); Add an App \u00b6 This action must be performed in the context of the tenant app catalog // this represents the file bytes of the app package file const blob = new Blob(); // there is an optional third argument to control overwriting existing files const r = await catalog.add(\"myapp.app\", blob); // this is at its core a file add operation so you have access to the response data as well // as a File instance representing the created file console.log(JSON.stringify(r.data, null, 4)); // all file operations are available const nameData = await r.file.select(\"Name\")(); Get an App \u00b6 You can get the details of a single app by GUID id. This is also the branch point to perform specific app actions const app = await catalog.getAppById(\"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\")(); Perform app actions \u00b6 Remember: retract, deploy, and remove only work in the context of the tenant app catalog web. All of these methods return void and you can monitor success by wrapping the call in a try/catch block. const myAppId = \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\"; // deploy await catalog.getAppById(myAppId).deploy(); // retract await catalog.getAppById(myAppId).retract(); // install await catalog.getAppById(myAppId).install(); // uninstall await catalog.getAppById(myAppId).uninstall(); // upgrade await catalog.getAppById(myAppId).upgrade(); // remove await catalog.getAppById(myAppId).remove(); Synchronize a solution/app to the Microsoft Teams App Catalog \u00b6 By default this REST call requires the SharePoint item id of the app, not the app id. PnPjs will try to fetch the SharePoint item id by default. You can still use this the second parameter useSharePointItemId to pass your own item id in the first parameter id . // Using the app id await catalog.syncSolutionToTeams(\"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\"); // Using the SharePoint apps item id await catalog.syncSolutionToTeams(\"123\", true); Notes \u00b6 The app catalog is just a document library under the hood, so you can also perform non-ALM actions on the library if needed. But you should be aware of possible side-effects to the ALM life-cycle when doing so.","title":"ALM api"},{"location":"sp/alm/#pnpspappcatalog","text":"The ALM api allows you to manage app installations both in the tenant app catalog and individual site app catalogs. Some of the methods are still in beta and as such may change in the future. This article outlines how to call this api using @pnp/sp. Remember all these actions are bound by permissions so it is likely most users will not have the rights to perform these ALM actions.","title":"@pnp/sp/appcatalog"},{"location":"sp/alm/#understanding-the-app-catalog-hierarchy","text":"Before you begin provisioning applications it is important to understand the relationship between a local web catalog and the tenant app catalog. Some of the methods described below only work within the context of the tenant app catalog web, such as adding an app to the catalog and the app actions retract, remove, and deploy. You can install, uninstall, and upgrade an app in any web. Read more in the official documentation .","title":"Understanding the App Catalog Hierarchy"},{"location":"sp/alm/#referencing-an-app-catalog","text":"There are several ways using @pnp/sp to get a reference to an app catalog. These methods are to provide you the greatest amount of flexibility in gaining access to the app catalog. Ultimately each method produces an AppCatalog instance differentiated only by the web to which it points. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/appcatalog\"; import \"@pnp/sp/webs\"; // get the current context web's app catalog const catalog = await sp.web.getAppCatalog()(); // you can also chain off the app catalog const apps = await sp.web.getAppCatalog()(); console.log(apps); import { sp } from \"@pnp/sp\"; import \"@pnp/sp/appcatalog\"; import \"@pnp/sp/webs\"; // you can get the tenant app catalog (or any app catalog) by using the getTenantAppCatalogWeb method const appCatWeb = await sp.getTenantAppCatalogWeb()(); const appCatalog = await appCatWeb.getAppCatalog()(); // you can get the tenant app catalog (or any app catalog) by passing in a url // get the tenant app catalog const tenantCatalog = await sp.web.getAppCatalog(\"https://mytenant.sharepoint.com/sites/appcatalog\")(); // get a different app catalog const catalog = await sp.web.getAppCatalog(\"https://mytenant.sharepoint.com/sites/anothersite\")(); // alternatively you can create a new app catalog instance directly by importing the AppCatalog class import { IAppCatalog, AppCatalog } from '@pnp/sp/appcatalog'; const catalog: IAppCatalog = await AppCatalog(\"https://mytenant.sharepoint.com/sites/apps\")(); // and finally you can combine use of the Web and AppCatalog classes to create an AppCatalog instance from an existing Web import { Web } from '@pnp/sp/webs'; import { AppCatalog } from '@pnp/sp/appcatalog'; const web = Web(\"https://mytenant.sharepoint.com/sites/apps\"); const catalog = await AppCatalog(web)(); The following examples make use of a variable \"catalog\" which is assumed to represent an AppCatalog instance obtained using one of the above methods, supporting code is omitted for brevity.","title":"Referencing an App Catalog"},{"location":"sp/alm/#list-available-apps","text":"The AppCatalog is itself a queryable collection so you can query this object directly to get a list of available apps. Also, the odata operators work on the catalog to sort, filter, and select. // get available apps await catalog(); // get available apps selecting two fields await catalog.select(\"Title\", \"Deployed\")();","title":"List Available Apps"},{"location":"sp/alm/#add-an-app","text":"This action must be performed in the context of the tenant app catalog // this represents the file bytes of the app package file const blob = new Blob(); // there is an optional third argument to control overwriting existing files const r = await catalog.add(\"myapp.app\", blob); // this is at its core a file add operation so you have access to the response data as well // as a File instance representing the created file console.log(JSON.stringify(r.data, null, 4)); // all file operations are available const nameData = await r.file.select(\"Name\")();","title":"Add an App"},{"location":"sp/alm/#get-an-app","text":"You can get the details of a single app by GUID id. This is also the branch point to perform specific app actions const app = await catalog.getAppById(\"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\")();","title":"Get an App"},{"location":"sp/alm/#perform-app-actions","text":"Remember: retract, deploy, and remove only work in the context of the tenant app catalog web. All of these methods return void and you can monitor success by wrapping the call in a try/catch block. const myAppId = \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\"; // deploy await catalog.getAppById(myAppId).deploy(); // retract await catalog.getAppById(myAppId).retract(); // install await catalog.getAppById(myAppId).install(); // uninstall await catalog.getAppById(myAppId).uninstall(); // upgrade await catalog.getAppById(myAppId).upgrade(); // remove await catalog.getAppById(myAppId).remove();","title":"Perform app actions"},{"location":"sp/alm/#synchronize-a-solutionapp-to-the-microsoft-teams-app-catalog","text":"By default this REST call requires the SharePoint item id of the app, not the app id. PnPjs will try to fetch the SharePoint item id by default. You can still use this the second parameter useSharePointItemId to pass your own item id in the first parameter id . // Using the app id await catalog.syncSolutionToTeams(\"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\"); // Using the SharePoint apps item id await catalog.syncSolutionToTeams(\"123\", true);","title":"Synchronize a solution/app to the Microsoft Teams App Catalog"},{"location":"sp/alm/#notes","text":"The app catalog is just a document library under the hood, so you can also perform non-ALM actions on the library if needed. But you should be aware of possible side-effects to the ALM life-cycle when doing so.","title":"Notes"},{"location":"sp/attachments/","text":"@pnp/sp/attachments \u00b6 The ability to attach file to list items allows users to track documents outside of a document library. You can use the PnP JS Core library to work with attachments as outlined below. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/attachments\"; Preset: All import { sp, IFeatures, Features } from \"@pnp/sp/presets/all\"; Get attachments \u00b6 import { sp } from \"@pnp/sp\"; import { IAttachmentInfo } from \"@pnp/sp/attachments\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); // get all the attachments const info: IAttachmentInfo[] = await item.attachmentFiles(); // get a single file by file name const info2: IAttachmentInfo = await item.attachmentFiles.getByName(\"file.txt\")(); // select specific properties using odata operators and use Pick to type the result const info3: Pick[] = await item.attachmentFiles.select(\"ServerRelativeUrl\")(); Add an Attachment \u00b6 You can add an attachment to a list item using the add method. This method takes either a string, Blob, or ArrayBuffer. import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.add(\"file2.txt\", \"Here is my content\"); Add Multiple \u00b6 This method allows you to pass an array of AttachmentFileInfo plain objects that will be added one at a time as attachments. Essentially automating the promise chaining. import { sp } from \"@pnp/sp\"; import { IList } from \"@pnp/sp/lists\"; import { IAttachmentFileInfo } from \"@pnp/sp/attachments\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const list: IList = sp.web.lists.getByTitle(\"MyList\"); let fileInfos: IAttachmentFileInfo[] = []; fileInfos.push({ name: \"My file name 1\", content: \"string, blob, or array\" }); fileInfos.push({ name: \"My file name 2\", content: \"string, blob, or array\" }); await list.items.getById(2).attachmentFiles.addMultiple(fileInfos); Delete Multiple \u00b6 import { sp } from \"@pnp/sp\"; import { IList } from \"./@pnp/sp/lists/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const list: IList = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(2).attachmentFiles.deleteMultiple(\"1.txt\", \"2.txt\"); Read Attachment Content \u00b6 You can read the content of an attachment as a string, Blob, ArrayBuffer, or json using the methods supplied. import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); const text = await item.attachmentFiles.getByName(\"file.txt\").getText(); // use this in the browser, does not work in nodejs const blob = await item.attachmentFiles.getByName(\"file.mp4\").getBlob(); // use this in nodejs const buffer = await item.attachmentFiles.getByName(\"file.mp4\").getBuffer(); // file must be valid json const json = await item.attachmentFiles.getByName(\"file.json\").getJSON(); Update Attachment Content \u00b6 You can also update the content of an attachment. This API is limited compared to the full file API - so if you need to upload large files consider using a document library. import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.getByName(\"file2.txt\").setContent(\"My new content!!!\"); Delete Attachment \u00b6 import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.getByName(\"file2.txt\").delete(); Recycle Attachment \u00b6 Delete the attachment and send it to recycle bin import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.getByName(\"file2.txt\").recycle(); Recycle Multiple Attachments \u00b6 Delete multiple attachments and send them to recycle bin import { sp } from \"@pnp/sp\"; import { IList } from \"@pnp/sp/lists/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const list: IList = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(2).attachmentFiles.recycleMultiple(\"1.txt\",\"2.txt\");","title":"Attachments"},{"location":"sp/attachments/#pnpspattachments","text":"The ability to attach file to list items allows users to track documents outside of a document library. You can use the PnP JS Core library to work with attachments as outlined below. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/attachments\"; Preset: All import { sp, IFeatures, Features } from \"@pnp/sp/presets/all\";","title":"@pnp/sp/attachments"},{"location":"sp/attachments/#get-attachments","text":"import { sp } from \"@pnp/sp\"; import { IAttachmentInfo } from \"@pnp/sp/attachments\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); // get all the attachments const info: IAttachmentInfo[] = await item.attachmentFiles(); // get a single file by file name const info2: IAttachmentInfo = await item.attachmentFiles.getByName(\"file.txt\")(); // select specific properties using odata operators and use Pick to type the result const info3: Pick[] = await item.attachmentFiles.select(\"ServerRelativeUrl\")();","title":"Get attachments"},{"location":"sp/attachments/#add-an-attachment","text":"You can add an attachment to a list item using the add method. This method takes either a string, Blob, or ArrayBuffer. import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.add(\"file2.txt\", \"Here is my content\");","title":"Add an Attachment"},{"location":"sp/attachments/#add-multiple","text":"This method allows you to pass an array of AttachmentFileInfo plain objects that will be added one at a time as attachments. Essentially automating the promise chaining. import { sp } from \"@pnp/sp\"; import { IList } from \"@pnp/sp/lists\"; import { IAttachmentFileInfo } from \"@pnp/sp/attachments\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const list: IList = sp.web.lists.getByTitle(\"MyList\"); let fileInfos: IAttachmentFileInfo[] = []; fileInfos.push({ name: \"My file name 1\", content: \"string, blob, or array\" }); fileInfos.push({ name: \"My file name 2\", content: \"string, blob, or array\" }); await list.items.getById(2).attachmentFiles.addMultiple(fileInfos);","title":"Add Multiple"},{"location":"sp/attachments/#delete-multiple","text":"import { sp } from \"@pnp/sp\"; import { IList } from \"./@pnp/sp/lists/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const list: IList = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(2).attachmentFiles.deleteMultiple(\"1.txt\", \"2.txt\");","title":"Delete Multiple"},{"location":"sp/attachments/#read-attachment-content","text":"You can read the content of an attachment as a string, Blob, ArrayBuffer, or json using the methods supplied. import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); const text = await item.attachmentFiles.getByName(\"file.txt\").getText(); // use this in the browser, does not work in nodejs const blob = await item.attachmentFiles.getByName(\"file.mp4\").getBlob(); // use this in nodejs const buffer = await item.attachmentFiles.getByName(\"file.mp4\").getBuffer(); // file must be valid json const json = await item.attachmentFiles.getByName(\"file.json\").getJSON();","title":"Read Attachment Content"},{"location":"sp/attachments/#update-attachment-content","text":"You can also update the content of an attachment. This API is limited compared to the full file API - so if you need to upload large files consider using a document library. import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.getByName(\"file2.txt\").setContent(\"My new content!!!\");","title":"Update Attachment Content"},{"location":"sp/attachments/#delete-attachment","text":"import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.getByName(\"file2.txt\").delete();","title":"Delete Attachment"},{"location":"sp/attachments/#recycle-attachment","text":"Delete the attachment and send it to recycle bin import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.getByName(\"file2.txt\").recycle();","title":"Recycle Attachment"},{"location":"sp/attachments/#recycle-multiple-attachments","text":"Delete multiple attachments and send them to recycle bin import { sp } from \"@pnp/sp\"; import { IList } from \"@pnp/sp/lists/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const list: IList = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(2).attachmentFiles.recycleMultiple(\"1.txt\",\"2.txt\");","title":"Recycle Multiple Attachments"},{"location":"sp/clientside-pages/","text":"@pnp/sp/clientside-pages \u00b6 The 'clientside-pages' module allows you to create, edit, and delete modern SharePoint pages. There are methods to update the page settings and add/remove client-side web parts. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { ClientsidePageFromFile, ClientsideText, ClientsideWebpartPropertyTypes, CreateClientsidePage, ClientsideWebpart, IClientsidePage } from \"@pnp/sp/clientside-pages\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/clientside-pages\"; Preset: All import { sp, ClientsidePageFromFile, ClientsideText, ClientsideWebpartPropertyTypes, CreateClientsidePage, ClientsideWebpart, IClientsidePage } from \"@pnp/sp/presets/all\"; Create a new Page \u00b6 You can create a new client-side page in several ways, all are equivalent. Create using IWeb.addClientsidePage \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; import { PromotedState } from \"@pnp/sp/clientside-pages\"; // Create a page providing a file name const page = await sp.web.addClientsidePage(\"mypage1\"); // ... other operations on the page as outlined below // the page is initially not published, you must publish it so it appears for others users await page.save(); // include title and page layout const page2 = await sp.web.addClientsidePage(\"mypage\", \"My Page Title\", \"Article\"); // you must publish the new page await page2.save(); // include title, page layout, and specifying the publishing status (Added in 2.0.4) const page3 = await sp.web.addClientsidePage(\"mypage\", \"My Page Title\", \"Article\", PromotedState.PromoteOnPublish); // you must publish the new page, after which the page will immediately be promoted to a news article await page3.save(); Create using CreateClientsidePage method \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { Web } from \"@pnp/sp/webs\"; import { CreateClientsidePage, PromotedState } from \"@pnp/sp/clientside-pages\"; const page1 = await CreateClientsidePage(sp.web, \"mypage2\", \"My Page Title\"); // you must publish the new page await page1.save(true); // specify the page layout type parameter const page2 = await CreateClientsidePage(sp.web, \"mypage3\", \"My Page Title\", \"Article\"); // you must publish the new page await page2.save(); // specify the page layout type parameter while also specifying the publishing status (Added in 2.0.4) const page2half = await CreateClientsidePage(sp.web, \"mypage3\", \"My Page Title\", \"Article\", PromotedState.PromoteOnPublish); // you must publish the new page, after which the page will immediately be promoted to a news article await page2half.save(); // use the web factory to create a page in a specific web const page3 = await CreateClientsidePage(Web(\"https://{absolute web url}\"), \"mypage4\", \"My Page Title\"); // you must publish the new page await page3.save(); Load Pages \u00b6 There are a few ways to load pages, each of which results in an IClientsidePage instance being returned. Load using IWeb.loadClientsidePage \u00b6 This method takes a server relative path to the page to load. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { Web } from \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; // use from the sp.web fluent chain const page = await sp.web.loadClientsidePage(\"/sites/dev/sitepages/mypage3.aspx\"); // use the web factory to target a specific web const page2 = await Web(\"https://{absolute web url}\").loadClientsidePage(\"/sites/dev/sitepages/mypage3.aspx\"); Load using ClientsidePageFromFile \u00b6 This method takes an IFile instance and loads an IClientsidePage instance. import { sp } from \"@pnp/sp\"; import { ClientsidePageFromFile } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files/web\"; const page = await ClientsidePageFromFile(sp.web.getFileByServerRelativePath(\"/sites/dev/sitepages/mypage3.aspx\")); Edit Sections and Columns \u00b6 Client-side pages are made up of sections, columns, and controls. Sections contain columns which contain controls. There are methods to operate on these within the page, in addition to the standard array methods available in JavaScript. These samples use a variable page that is understood to be an IClientsidePage instance which is either created or loaded as outlined in previous sections. // our page instance const page: IClientsidePage; // add two columns with factor 6 - this is a two column layout as the total factor in a section should add up to 12 const section1 = page.addSection(); section1.addColumn(6); section1.addColumn(6); // create a three column layout in a new section const section2 = page.addSection(); section2.addColumn(4); section2.addColumn(4); section2.addColumn(4); // publish our changes await page.save(); Manipulate Sections and Columns \u00b6 // our page instance const page: IClientsidePage; // drop all the columns in this section // this will also DELETE all controls contained in the columns page.sections[1].columns.length = 0; // create a new column layout page.sections[1].addColumn(4); page.sections[1].addColumn(8); // publish our changes await page.save(); Vertical Section \u00b6 The vertical section, if on the page, is stored within the sections array. However, you access it slightly differently to make things easier. // our page instance const page: IClientsidePage; // add or get a vertical section (handles case where section already exists) const vertSection = page.addVerticalSection(); // **************************************************************** // if you know or want to test if a vertical section is present: if (page.hasVerticalSection) { // access the vertical section (this method will NOT create the section if it does not exist) page.verticalSection.addControl(new ClientsideText(\"hello\")); } else { const vertSection = page.addVerticalSection(); vertSection.addControl(new ClientsideText(\"hello\")); } Reorder Sections \u00b6 // our page instance const page: IClientsidePage; // swap the order of two sections // this will preserve the controls within the columns page.sections = [page.sections[1], page.sections[0]]; // publish our changes await page.save(); Reorder Columns \u00b6 The sections and columns are arrays, so normal array operations work as expected // our page instance const page: IClientsidePage; // swap the order of two columns // this will preserve the controls within the columns page.sections[1].columns = [page.sections[1].columns[1], page.sections[1].columns[0]]; // publish our changes await page.save(); Clientside Controls \u00b6 Once you have your sections and columns defined you will want to add/edit controls within those columns. Add Text Content \u00b6 import { ClientsideText } from \"@pnp/sp/clientside-pages\"; // our page instance const page: IClientsidePage; page.addSection().addControl(new ClientsideText(\"@pnp/sp is a great library!\")); await page.save(); Add Controls \u00b6 Adding controls involves loading the available client-side part definitions from the server or creating a text part. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; import { ClientsideWebpart } from \"@pnp/sp/clientside-pages\"; // this will be a ClientsidePageComponent array // this can be cached on the client in production scenarios const partDefs = await sp.web.getClientsideWebParts(); // find the definition we want, here by id const partDef = partDefs.filter(c => c.Id === \"490d7c76-1824-45b2-9de3-676421c997fa\"); // optionally ensure you found the def if (partDef.length < 1) { // we didn't find it so we throw an error throw new Error(\"Could not find the web part\"); } // create a ClientWebPart instance from the definition const part = ClientsideWebpart.fromComponentDef(partDef[0]); // set the properties on the web part. Here for the embed web part we only have to supply an embedCode - in this case a YouTube video. // the structure of the properties varies for each web part and each version of a web part, so you will need to ensure you are setting // the properties correctly part.setProperties<{ embedCode: string }>({ embedCode: \"https://www.youtube.com/watch?v=IWQFZ7Lx-rg\", }); // we add that part to a new section page.addSection().addControl(part); await page.save(); Handle Different Webpart's Settings \u00b6 There are many ways that client side web parts are implemented and we can't provide handling within the library for all possibilities. This example shows how to handle a property set within the serverProcessedContent, in this case a List part's display title. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { ClientsideWebpart } from \"@pnp/sp/clientside-pages\"; // we create a class to wrap our functionality in a reusable way class ListWebpart extends ClientsideWebpart { constructor(control: ClientsideWebpart) { super((control).json); } // add property getter/setter for what we need, in this case \"listTitle\" within searchablePlainTexts public get DisplayTitle(): string { return this.json.webPartData?.serverProcessedContent?.searchablePlainTexts?.listTitle || \"\"; } public set DisplayTitle(value: string) { this.json.webPartData.serverProcessedContent.searchablePlainTexts.listTitle = value; } } // now we load our page const page = await sp.web.loadClientsidePage(\"/sites/dev/SitePages/List-Web-Part.aspx\"); // get our part and pass it to the constructor of our wrapper class const part = new ListWebpart(page.sections[0].columns[0].getControl(0)); part.DisplayTitle = \"My New Title!\"; await page.save(); Unfortunately each webpart can be authored differently, so there isn't a way to know how the setting for a given webpart are stored without loading it and examining the properties. Page Operations \u00b6 There are other operation you can perform on a page in addition to manipulating the content. pageLayout \u00b6 You can get and set the page layout. Changing the layout after creating the page may have side effects and should be done cautiously. // our page instance const page: IClientsidePage; // get the current value const value = page.pageLayout; // set the value page.pageLayout = \"Article\"; await page.save(); bannerImageUrl \u00b6 // our page instance const page: IClientsidePage; // get the current value const value = page.bannerImageUrl; // set the value page.bannerImageUrl = \"/server/relative/path/to/image.png\"; await page.save(); Banner images need to exist within the same site collection as the page where you want to use them. thumbnailUrl \u00b6 Allows you to set the thumbnail used for the page independently of the banner. If you set the bannerImageUrl property and not thumbnailUrl the thumbnail will be reset to match the banner, mimicking the UI functionality. // our page instance const page: IClientsidePage; // get the current value const value = page.thumbnailUrl; // set the value page.thumbnailUrl = \"/server/relative/path/to/image.png\"; await page.save(); topicHeader \u00b6 // our page instance const page: IClientsidePage; // get the current value const value = page.topicHeader; // set the value page.topicHeader = \"My cool header!\"; await page.save(); // clear the topic header and hide it page.topicHeader = \"\"; await page.save(); title \u00b6 // our page instance const page: IClientsidePage; // get the current value const value = page.title; // set the value page.title = \"My page title\"; await page.save(); description \u00b6 Descriptions are limited to 255 chars // our page instance const page: IClientsidePage; // get the current value const value = page.description; // set the value page.description = \"A description\"; await page.save(); layoutType \u00b6 Sets the layout type of the page. The valid values are: \"FullWidthImage\", \"NoImage\", \"ColorBlock\", \"CutInShape\" // our page instance const page: IClientsidePage; // get the current value const value = page.layoutType; // set the value page.layoutType = \"ColorBlock\"; await page.save(); headerTextAlignment \u00b6 Sets the header text alignment to one of \"Left\" or \"Center\" // our page instance const page: IClientsidePage; // get the current value const value = page.headerTextAlignment; // set the value page.headerTextAlignment = \"Center\"; await page.save(); showTopicHeader \u00b6 Sets if the topic header is displayed on a page. // our page instance const page: IClientsidePage; // get the current value const value = page.showTopicHeader; // show the header page.showTopicHeader = true; await page.save(); // hide the header page.showTopicHeader = false; await page.save(); showPublishDate \u00b6 Sets if the publish date is displayed on a page. // our page instance const page: IClientsidePage; // get the current value const value = page.showPublishDate; // show the date page.showPublishDate = true; await page.save(); // hide the date page.showPublishDate = false; await page.save(); Get / Set author details \u00b6 Added in 2.0.4 // our page instance const page: IClientsidePage; // get the author details (string | null) const value = page.authorByLine; // set the author by user id const user = await web.currentUser.select(\"Id\", \"LoginName\")(); const userId = user.Id; const userLogin = user.LoginName; await page.setAuthorById(userId); await page.save(); await page.setAuthorByLoginName(userLogin); await page.save(); you must still save the page after setting the author to persist your changes as shown in the example. load \u00b6 Loads the page from the server. This will overwrite any local unsaved changes. // our page instance const page: IClientsidePage; await page.load(); save \u00b6 Saves any changes to the page, optionally keeping them in draft state. // our page instance const page: IClientsidePage; // changes are published await page.save(); // changes remain in draft await page.save(false); discardPageCheckout \u00b6 Discards any current checkout of the page by the current user. // our page instance const page: IClientsidePage; await page.discardPageCheckout(); promoteToNews \u00b6 Promotes the page as a news article. // our page instance const page: IClientsidePage; await page.promoteToNews(); enableComments & disableComments \u00b6 Used to control the availability of comments on a page. // you need to import the comments sub-module or use the all preset import \"@pnp/sp/comments/clientside-page\"; // our page instance const page: IClientsidePage; // turn on comments await page.enableComments(); // turn off comments await page.disableComments(); findControlById \u00b6 Finds a control within the page by id. import { ClientsideText } from \"@pnp/sp/clientside-pages\"; // our page instance const page: IClientsidePage; const control = page.findControlById(\"06d4cdf6-bce6-4200-8b93-667a1b0a6c9d\"); // you can also type the control const control = page.findControlById(\"06d4cdf6-bce6-4200-8b93-667a1b0a6c9d\"); findControl \u00b6 Finds a control within the page using the supplied delegate. Can also be used to iterate through all controls in the page. // our page instance const page: IClientsidePage; // find the first control whose order is 9 const control = page.findControl((c) => c.order === 9); // iterate all the controls and output the id to the console page.findControl((c) => { console.log(c.id); return false; }); like & unlike \u00b6 Updates the page's like value for the current user. // our page instance const page: IClientsidePage; // like this page await page.like(); // unlike this page await page.unlike(); getLikedByInformation \u00b6 Gets the likes information for this page. // our page instance const page: IClientsidePage; const info = await page.getLikedByInformation(); copy \u00b6 Creates a copy of the page, including all controls. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // our page instance const page: IClientsidePage; // creates a published copy of the page const pageCopy = await page.copy(sp.web, \"newpagename\", \"New Page Title\"); // creates a draft (unpublished) copy of the page const pageCopy2 = await page.copy(sp.web, \"newpagename\", \"New Page Title\", false); // edits to pageCopy2 ... // publish the page pageCopy2.save(); copyTo \u00b6 Copies the contents of a page to another existing page instance. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // our page instances, loaded in any of the ways shown above const source: IClientsidePage; const target: IClientsidePage; const target2: IClientsidePage; // creates a published copy of the page await source.copyTo(target); // creates a draft (unpublished) copy of the page await source.copyTo(target2, false); // edits to target2... // publish the page target2.save(); setBannerImage \u00b6 Sets the banner image url and optionally additional properties. Allows you to set additional properties if needed, if you do not need to set the additional properties they are equivalent. Banner images need to exist within the same site collection as the page where you want to use them. // our page instance const page: IClientsidePage; page.setBannerImage(\"/server/relative/path/to/image.png\"); // save the changes await page.save(); // set additional props page.setBannerImage(\"/server/relative/path/to/image.png\", { altText: \"Image description\", imageSourceType: 2, translateX: 30, translateY: 1234, }); // save the changes await page.save(); This sample shows the full process of adding a page, image file, and setting the banner image in nodejs. The same code would work in a browser with an update on how you get the file - likely from a file input or similar. import { SPFetchClient } from \"@pnp/nodejs\"; import { join } from \"path\"; import { readFileSync } from \"fs\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/clientside-pages\"; // configure your node options sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{Site Url}\", \"{Client Id}\", \"{Client Secret}\"); }, }, }); // add the banner image const dirname = join(\"C:/path/to/file\", \"img-file.jpg\"); const file: Uint8Array = new Uint8Array(readFileSync(dirname)); const far = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents\").files.add(\"banner.jpg\", file, true); // add the page const page = await sp.web.addClientsidePage(\"MyPage\", \"Page Title\"); // set the banner image page.setBannerImage(far.data.ServerRelativeUrl); // publish the page await page.save(); setBannerImageFromExternalUrl \u00b6 Added in 2.0.12 Allows you to set the banner image from a source outside the current site collection. The image file will be copied to the SiteAssets library and referenced from there. // our page instance const page: IClientsidePage; // you must await this method await page.setBannerImageFromExternalUrl(\"https://absolute.url/to/my/image.jpg\"); // save the changes await page.save(); You can optionally supply additional props for the banner image, these match the properties when calling setBannerImage // our page instance const page: IClientsidePage; // you must await this method await page.setBannerImageFromExternalUrl(\"https://absolute.url/to/my/image.jpg\", { altText: \"Image description\", imageSourceType: 2, translateX: 30, translateY: 1234, }); // save the changes await page.save();","title":"Client-side Pages"},{"location":"sp/clientside-pages/#pnpspclientside-pages","text":"The 'clientside-pages' module allows you to create, edit, and delete modern SharePoint pages. There are methods to update the page settings and add/remove client-side web parts. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { ClientsidePageFromFile, ClientsideText, ClientsideWebpartPropertyTypes, CreateClientsidePage, ClientsideWebpart, IClientsidePage } from \"@pnp/sp/clientside-pages\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/clientside-pages\"; Preset: All import { sp, ClientsidePageFromFile, ClientsideText, ClientsideWebpartPropertyTypes, CreateClientsidePage, ClientsideWebpart, IClientsidePage } from \"@pnp/sp/presets/all\";","title":"@pnp/sp/clientside-pages"},{"location":"sp/clientside-pages/#create-a-new-page","text":"You can create a new client-side page in several ways, all are equivalent.","title":"Create a new Page"},{"location":"sp/clientside-pages/#create-using-iwebaddclientsidepage","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; import { PromotedState } from \"@pnp/sp/clientside-pages\"; // Create a page providing a file name const page = await sp.web.addClientsidePage(\"mypage1\"); // ... other operations on the page as outlined below // the page is initially not published, you must publish it so it appears for others users await page.save(); // include title and page layout const page2 = await sp.web.addClientsidePage(\"mypage\", \"My Page Title\", \"Article\"); // you must publish the new page await page2.save(); // include title, page layout, and specifying the publishing status (Added in 2.0.4) const page3 = await sp.web.addClientsidePage(\"mypage\", \"My Page Title\", \"Article\", PromotedState.PromoteOnPublish); // you must publish the new page, after which the page will immediately be promoted to a news article await page3.save();","title":"Create using IWeb.addClientsidePage"},{"location":"sp/clientside-pages/#create-using-createclientsidepage-method","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { Web } from \"@pnp/sp/webs\"; import { CreateClientsidePage, PromotedState } from \"@pnp/sp/clientside-pages\"; const page1 = await CreateClientsidePage(sp.web, \"mypage2\", \"My Page Title\"); // you must publish the new page await page1.save(true); // specify the page layout type parameter const page2 = await CreateClientsidePage(sp.web, \"mypage3\", \"My Page Title\", \"Article\"); // you must publish the new page await page2.save(); // specify the page layout type parameter while also specifying the publishing status (Added in 2.0.4) const page2half = await CreateClientsidePage(sp.web, \"mypage3\", \"My Page Title\", \"Article\", PromotedState.PromoteOnPublish); // you must publish the new page, after which the page will immediately be promoted to a news article await page2half.save(); // use the web factory to create a page in a specific web const page3 = await CreateClientsidePage(Web(\"https://{absolute web url}\"), \"mypage4\", \"My Page Title\"); // you must publish the new page await page3.save();","title":"Create using CreateClientsidePage method"},{"location":"sp/clientside-pages/#load-pages","text":"There are a few ways to load pages, each of which results in an IClientsidePage instance being returned.","title":"Load Pages"},{"location":"sp/clientside-pages/#load-using-iwebloadclientsidepage","text":"This method takes a server relative path to the page to load. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { Web } from \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; // use from the sp.web fluent chain const page = await sp.web.loadClientsidePage(\"/sites/dev/sitepages/mypage3.aspx\"); // use the web factory to target a specific web const page2 = await Web(\"https://{absolute web url}\").loadClientsidePage(\"/sites/dev/sitepages/mypage3.aspx\");","title":"Load using IWeb.loadClientsidePage"},{"location":"sp/clientside-pages/#load-using-clientsidepagefromfile","text":"This method takes an IFile instance and loads an IClientsidePage instance. import { sp } from \"@pnp/sp\"; import { ClientsidePageFromFile } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files/web\"; const page = await ClientsidePageFromFile(sp.web.getFileByServerRelativePath(\"/sites/dev/sitepages/mypage3.aspx\"));","title":"Load using ClientsidePageFromFile"},{"location":"sp/clientside-pages/#edit-sections-and-columns","text":"Client-side pages are made up of sections, columns, and controls. Sections contain columns which contain controls. There are methods to operate on these within the page, in addition to the standard array methods available in JavaScript. These samples use a variable page that is understood to be an IClientsidePage instance which is either created or loaded as outlined in previous sections. // our page instance const page: IClientsidePage; // add two columns with factor 6 - this is a two column layout as the total factor in a section should add up to 12 const section1 = page.addSection(); section1.addColumn(6); section1.addColumn(6); // create a three column layout in a new section const section2 = page.addSection(); section2.addColumn(4); section2.addColumn(4); section2.addColumn(4); // publish our changes await page.save();","title":"Edit Sections and Columns"},{"location":"sp/clientside-pages/#manipulate-sections-and-columns","text":"// our page instance const page: IClientsidePage; // drop all the columns in this section // this will also DELETE all controls contained in the columns page.sections[1].columns.length = 0; // create a new column layout page.sections[1].addColumn(4); page.sections[1].addColumn(8); // publish our changes await page.save();","title":"Manipulate Sections and Columns"},{"location":"sp/clientside-pages/#vertical-section","text":"The vertical section, if on the page, is stored within the sections array. However, you access it slightly differently to make things easier. // our page instance const page: IClientsidePage; // add or get a vertical section (handles case where section already exists) const vertSection = page.addVerticalSection(); // **************************************************************** // if you know or want to test if a vertical section is present: if (page.hasVerticalSection) { // access the vertical section (this method will NOT create the section if it does not exist) page.verticalSection.addControl(new ClientsideText(\"hello\")); } else { const vertSection = page.addVerticalSection(); vertSection.addControl(new ClientsideText(\"hello\")); }","title":"Vertical Section"},{"location":"sp/clientside-pages/#reorder-sections","text":"// our page instance const page: IClientsidePage; // swap the order of two sections // this will preserve the controls within the columns page.sections = [page.sections[1], page.sections[0]]; // publish our changes await page.save();","title":"Reorder Sections"},{"location":"sp/clientside-pages/#reorder-columns","text":"The sections and columns are arrays, so normal array operations work as expected // our page instance const page: IClientsidePage; // swap the order of two columns // this will preserve the controls within the columns page.sections[1].columns = [page.sections[1].columns[1], page.sections[1].columns[0]]; // publish our changes await page.save();","title":"Reorder Columns"},{"location":"sp/clientside-pages/#clientside-controls","text":"Once you have your sections and columns defined you will want to add/edit controls within those columns.","title":"Clientside Controls"},{"location":"sp/clientside-pages/#add-text-content","text":"import { ClientsideText } from \"@pnp/sp/clientside-pages\"; // our page instance const page: IClientsidePage; page.addSection().addControl(new ClientsideText(\"@pnp/sp is a great library!\")); await page.save();","title":"Add Text Content"},{"location":"sp/clientside-pages/#add-controls","text":"Adding controls involves loading the available client-side part definitions from the server or creating a text part. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; import { ClientsideWebpart } from \"@pnp/sp/clientside-pages\"; // this will be a ClientsidePageComponent array // this can be cached on the client in production scenarios const partDefs = await sp.web.getClientsideWebParts(); // find the definition we want, here by id const partDef = partDefs.filter(c => c.Id === \"490d7c76-1824-45b2-9de3-676421c997fa\"); // optionally ensure you found the def if (partDef.length < 1) { // we didn't find it so we throw an error throw new Error(\"Could not find the web part\"); } // create a ClientWebPart instance from the definition const part = ClientsideWebpart.fromComponentDef(partDef[0]); // set the properties on the web part. Here for the embed web part we only have to supply an embedCode - in this case a YouTube video. // the structure of the properties varies for each web part and each version of a web part, so you will need to ensure you are setting // the properties correctly part.setProperties<{ embedCode: string }>({ embedCode: \"https://www.youtube.com/watch?v=IWQFZ7Lx-rg\", }); // we add that part to a new section page.addSection().addControl(part); await page.save();","title":"Add Controls"},{"location":"sp/clientside-pages/#handle-different-webparts-settings","text":"There are many ways that client side web parts are implemented and we can't provide handling within the library for all possibilities. This example shows how to handle a property set within the serverProcessedContent, in this case a List part's display title. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { ClientsideWebpart } from \"@pnp/sp/clientside-pages\"; // we create a class to wrap our functionality in a reusable way class ListWebpart extends ClientsideWebpart { constructor(control: ClientsideWebpart) { super((control).json); } // add property getter/setter for what we need, in this case \"listTitle\" within searchablePlainTexts public get DisplayTitle(): string { return this.json.webPartData?.serverProcessedContent?.searchablePlainTexts?.listTitle || \"\"; } public set DisplayTitle(value: string) { this.json.webPartData.serverProcessedContent.searchablePlainTexts.listTitle = value; } } // now we load our page const page = await sp.web.loadClientsidePage(\"/sites/dev/SitePages/List-Web-Part.aspx\"); // get our part and pass it to the constructor of our wrapper class const part = new ListWebpart(page.sections[0].columns[0].getControl(0)); part.DisplayTitle = \"My New Title!\"; await page.save(); Unfortunately each webpart can be authored differently, so there isn't a way to know how the setting for a given webpart are stored without loading it and examining the properties.","title":"Handle Different Webpart's Settings"},{"location":"sp/clientside-pages/#page-operations","text":"There are other operation you can perform on a page in addition to manipulating the content.","title":"Page Operations"},{"location":"sp/clientside-pages/#pagelayout","text":"You can get and set the page layout. Changing the layout after creating the page may have side effects and should be done cautiously. // our page instance const page: IClientsidePage; // get the current value const value = page.pageLayout; // set the value page.pageLayout = \"Article\"; await page.save();","title":"pageLayout"},{"location":"sp/clientside-pages/#bannerimageurl","text":"// our page instance const page: IClientsidePage; // get the current value const value = page.bannerImageUrl; // set the value page.bannerImageUrl = \"/server/relative/path/to/image.png\"; await page.save(); Banner images need to exist within the same site collection as the page where you want to use them.","title":"bannerImageUrl"},{"location":"sp/clientside-pages/#thumbnailurl","text":"Allows you to set the thumbnail used for the page independently of the banner. If you set the bannerImageUrl property and not thumbnailUrl the thumbnail will be reset to match the banner, mimicking the UI functionality. // our page instance const page: IClientsidePage; // get the current value const value = page.thumbnailUrl; // set the value page.thumbnailUrl = \"/server/relative/path/to/image.png\"; await page.save();","title":"thumbnailUrl"},{"location":"sp/clientside-pages/#topicheader","text":"// our page instance const page: IClientsidePage; // get the current value const value = page.topicHeader; // set the value page.topicHeader = \"My cool header!\"; await page.save(); // clear the topic header and hide it page.topicHeader = \"\"; await page.save();","title":"topicHeader"},{"location":"sp/clientside-pages/#title","text":"// our page instance const page: IClientsidePage; // get the current value const value = page.title; // set the value page.title = \"My page title\"; await page.save();","title":"title"},{"location":"sp/clientside-pages/#description","text":"Descriptions are limited to 255 chars // our page instance const page: IClientsidePage; // get the current value const value = page.description; // set the value page.description = \"A description\"; await page.save();","title":"description"},{"location":"sp/clientside-pages/#layouttype","text":"Sets the layout type of the page. The valid values are: \"FullWidthImage\", \"NoImage\", \"ColorBlock\", \"CutInShape\" // our page instance const page: IClientsidePage; // get the current value const value = page.layoutType; // set the value page.layoutType = \"ColorBlock\"; await page.save();","title":"layoutType"},{"location":"sp/clientside-pages/#headertextalignment","text":"Sets the header text alignment to one of \"Left\" or \"Center\" // our page instance const page: IClientsidePage; // get the current value const value = page.headerTextAlignment; // set the value page.headerTextAlignment = \"Center\"; await page.save();","title":"headerTextAlignment"},{"location":"sp/clientside-pages/#showtopicheader","text":"Sets if the topic header is displayed on a page. // our page instance const page: IClientsidePage; // get the current value const value = page.showTopicHeader; // show the header page.showTopicHeader = true; await page.save(); // hide the header page.showTopicHeader = false; await page.save();","title":"showTopicHeader"},{"location":"sp/clientside-pages/#showpublishdate","text":"Sets if the publish date is displayed on a page. // our page instance const page: IClientsidePage; // get the current value const value = page.showPublishDate; // show the date page.showPublishDate = true; await page.save(); // hide the date page.showPublishDate = false; await page.save();","title":"showPublishDate"},{"location":"sp/clientside-pages/#get-set-author-details","text":"Added in 2.0.4 // our page instance const page: IClientsidePage; // get the author details (string | null) const value = page.authorByLine; // set the author by user id const user = await web.currentUser.select(\"Id\", \"LoginName\")(); const userId = user.Id; const userLogin = user.LoginName; await page.setAuthorById(userId); await page.save(); await page.setAuthorByLoginName(userLogin); await page.save(); you must still save the page after setting the author to persist your changes as shown in the example.","title":"Get / Set author details"},{"location":"sp/clientside-pages/#load","text":"Loads the page from the server. This will overwrite any local unsaved changes. // our page instance const page: IClientsidePage; await page.load();","title":"load"},{"location":"sp/clientside-pages/#save","text":"Saves any changes to the page, optionally keeping them in draft state. // our page instance const page: IClientsidePage; // changes are published await page.save(); // changes remain in draft await page.save(false);","title":"save"},{"location":"sp/clientside-pages/#discardpagecheckout","text":"Discards any current checkout of the page by the current user. // our page instance const page: IClientsidePage; await page.discardPageCheckout();","title":"discardPageCheckout"},{"location":"sp/clientside-pages/#promotetonews","text":"Promotes the page as a news article. // our page instance const page: IClientsidePage; await page.promoteToNews();","title":"promoteToNews"},{"location":"sp/clientside-pages/#enablecomments-disablecomments","text":"Used to control the availability of comments on a page. // you need to import the comments sub-module or use the all preset import \"@pnp/sp/comments/clientside-page\"; // our page instance const page: IClientsidePage; // turn on comments await page.enableComments(); // turn off comments await page.disableComments();","title":"enableComments & disableComments"},{"location":"sp/clientside-pages/#findcontrolbyid","text":"Finds a control within the page by id. import { ClientsideText } from \"@pnp/sp/clientside-pages\"; // our page instance const page: IClientsidePage; const control = page.findControlById(\"06d4cdf6-bce6-4200-8b93-667a1b0a6c9d\"); // you can also type the control const control = page.findControlById(\"06d4cdf6-bce6-4200-8b93-667a1b0a6c9d\");","title":"findControlById"},{"location":"sp/clientside-pages/#findcontrol","text":"Finds a control within the page using the supplied delegate. Can also be used to iterate through all controls in the page. // our page instance const page: IClientsidePage; // find the first control whose order is 9 const control = page.findControl((c) => c.order === 9); // iterate all the controls and output the id to the console page.findControl((c) => { console.log(c.id); return false; });","title":"findControl"},{"location":"sp/clientside-pages/#like-unlike","text":"Updates the page's like value for the current user. // our page instance const page: IClientsidePage; // like this page await page.like(); // unlike this page await page.unlike();","title":"like & unlike"},{"location":"sp/clientside-pages/#getlikedbyinformation","text":"Gets the likes information for this page. // our page instance const page: IClientsidePage; const info = await page.getLikedByInformation();","title":"getLikedByInformation"},{"location":"sp/clientside-pages/#copy","text":"Creates a copy of the page, including all controls. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // our page instance const page: IClientsidePage; // creates a published copy of the page const pageCopy = await page.copy(sp.web, \"newpagename\", \"New Page Title\"); // creates a draft (unpublished) copy of the page const pageCopy2 = await page.copy(sp.web, \"newpagename\", \"New Page Title\", false); // edits to pageCopy2 ... // publish the page pageCopy2.save();","title":"copy"},{"location":"sp/clientside-pages/#copyto","text":"Copies the contents of a page to another existing page instance. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // our page instances, loaded in any of the ways shown above const source: IClientsidePage; const target: IClientsidePage; const target2: IClientsidePage; // creates a published copy of the page await source.copyTo(target); // creates a draft (unpublished) copy of the page await source.copyTo(target2, false); // edits to target2... // publish the page target2.save();","title":"copyTo"},{"location":"sp/clientside-pages/#setbannerimage","text":"Sets the banner image url and optionally additional properties. Allows you to set additional properties if needed, if you do not need to set the additional properties they are equivalent. Banner images need to exist within the same site collection as the page where you want to use them. // our page instance const page: IClientsidePage; page.setBannerImage(\"/server/relative/path/to/image.png\"); // save the changes await page.save(); // set additional props page.setBannerImage(\"/server/relative/path/to/image.png\", { altText: \"Image description\", imageSourceType: 2, translateX: 30, translateY: 1234, }); // save the changes await page.save(); This sample shows the full process of adding a page, image file, and setting the banner image in nodejs. The same code would work in a browser with an update on how you get the file - likely from a file input or similar. import { SPFetchClient } from \"@pnp/nodejs\"; import { join } from \"path\"; import { readFileSync } from \"fs\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/clientside-pages\"; // configure your node options sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{Site Url}\", \"{Client Id}\", \"{Client Secret}\"); }, }, }); // add the banner image const dirname = join(\"C:/path/to/file\", \"img-file.jpg\"); const file: Uint8Array = new Uint8Array(readFileSync(dirname)); const far = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents\").files.add(\"banner.jpg\", file, true); // add the page const page = await sp.web.addClientsidePage(\"MyPage\", \"Page Title\"); // set the banner image page.setBannerImage(far.data.ServerRelativeUrl); // publish the page await page.save();","title":"setBannerImage"},{"location":"sp/clientside-pages/#setbannerimagefromexternalurl","text":"Added in 2.0.12 Allows you to set the banner image from a source outside the current site collection. The image file will be copied to the SiteAssets library and referenced from there. // our page instance const page: IClientsidePage; // you must await this method await page.setBannerImageFromExternalUrl(\"https://absolute.url/to/my/image.jpg\"); // save the changes await page.save(); You can optionally supply additional props for the banner image, these match the properties when calling setBannerImage // our page instance const page: IClientsidePage; // you must await this method await page.setBannerImageFromExternalUrl(\"https://absolute.url/to/my/image.jpg\", { altText: \"Image description\", imageSourceType: 2, translateX: 30, translateY: 1234, }); // save the changes await page.save();","title":"setBannerImageFromExternalUrl"},{"location":"sp/column-defaults/","text":"@pnp/sp/column-defaults \u00b6 The column defaults sub-module allows you to manage the default column values on a library or library folder. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { IFieldDefault, IFieldDefaultProps, AllowedDefaultColumnValues } from \"@pnp/sp/column-defaults\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/column-defaults\"; Preset: All import { sp, IFieldDefault, IFieldDefaultProps, AllowedDefaultColumnValues } from \"@pnp/sp/presents/all\"; Get Folder Defaults \u00b6 You can get the default values for a specific folder as shown below: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/column-defaults\"; const defaults = await sp.web.getFolderByServerRelativePath(\"/sites/dev/DefaultColumnValues/fld_GHk5\").getDefaultColumnValues(); /* The resulting structure will have the form: [ { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues/fld_GHk5\", \"value\": \"{the default value}\" }, { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues/fld_GHk5\", \"value\": \"{the default value}\" } ] */ Set Folder Defaults \u00b6 When setting the defaults for a folder you need to include the field's internal name and the value. For more examples of other field types see the section Pattern for setting defaults on various column types Note: Be very careful when setting the path as the site collection url is case sensitive import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.getFolderByServerRelativePath(\"/sites/dev/DefaultColumnValues/fld_GHk5\").setDefaultColumnValues([{ name: \"TextField\", value: \"Something\", }, { name: \"NumberField\", value: 14, }]); Get Library Defaults \u00b6 You can also get all of the defaults for the entire library. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/column-defaults\"; const defaults = await sp.web.lists.getByTitle(\"DefaultColumnValues\").getDefaultColumnValues(); /* The resulting structure will have the form: [ { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues\", \"value\": \"{the default value}\" }, { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues/fld_GHk5\", \"value\": \"{a different default value}\" } ] */ Set Library Defaults \u00b6 You can also set the defaults for an entire library at once (root and all sub-folders). This may be helpful in provisioning a library or other scenarios. When setting the defaults for the entire library you must also include the path value with is the server relative path to the folder. When setting the defaults for a folder you need to include the field's internal name and the value. For more examples of other field types see the section Pattern for setting defaults on various column types Note: Be very careful when setting the path as the site collection url is case sensitive import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.lists.getByTitle(\"DefaultColumnValues\").setDefaultColumnValues([{ name: \"TextField\", path: \"/sites/dev/DefaultColumnValues\", value: \"#PnPjs Rocks!\", }]); Clear Folder Defaults \u00b6 If you want to clear all of the folder defaults you can use the clear method: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.getFolderByServerRelativePath(\"/sites/dev/DefaultColumnValues/fld_GHk5\").clearDefaultColumnValues(); Clear Library Defaults \u00b6 If you need to clear all of the default column values in a library you can pass an empty array to the list's setDefaultColumnValues method. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.lists.getByTitle(\"DefaultColumnValues\").setDefaultColumnValues([]); Pattern for setting defaults on various column types \u00b6 The following is an example of the structure for setting the default column value when using the setDefaultColumnValues that covers the various field types. [{ // Text/Boolean/CurrencyDateTime/Choice/User name: \"TextField\": path: \"/sites/dev/DefaultColumnValues\", value: \"#PnPjs Rocks!\", }, { //Number name: \"NumberField\", path: \"/sites/dev/DefaultColumnValues\", value: 42, }, { //Date name: \"NumberField\", path: \"/sites/dev/DefaultColumnValues\", value: \"1900-01-01T00:00:00Z\", }, { //Date - Today name: \"NumberField\", path: \"/sites/dev/DefaultColumnValues\", value: \"[today]\", }, { //MultiChoice name: \"MultiChoiceField\", path: \"/sites/dev/DefaultColumnValues\", value: [\"Item 1\", \"Item 2\"], }, { //MultiChoice - single value name: \"MultiChoiceField\", path: \"/sites/dev/DefaultColumnValues/folder2\", value: [\"Item 1\"], }, { //Taxonomy - single value name: \"TaxonomyField\", path: \"/sites/dev/DefaultColumnValues\", value: { wssId:\"-1\", termName: \"TaxValueName\", termId: \"924d2077-d5e3-4507-9f36-4a3655e74274\" } }, { //Taxonomy - multiple value name: \"TaxonomyMultiField\", path: \"/sites/dev/DefaultColumnValues\", value: [{ wssId:\"-1\", termName: \"TaxValueName\", termId: \"924d2077-d5e3-4507-9f36-4a3655e74274\" },{ wssId:\"-1\", termName: \"TaxValueName2\", termId: \"95d4c307-dde5-49d8-b861-392e145d94d3\" },] }]); Taxonomy Full Example \u00b6 This example shows fully how to get the taxonomy values and set them as a default column value using PnPjs. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/column-defaults\"; import \"@pnp/sp/taxonomy\"; // get the term's info we want to use as the default const term = await sp.termStore.sets.getById(\"ea6fc521-d293-4f3d-9e84-f3a5bc0936ce\").getTermById(\"775c9cf6-c3cd-4db9-8cfa-fc0aeefad93a\")(); // get the default term label const defLabel = term.labels.find(v => v.isDefault); // set the default value using -1, the term id, and the term's default label name await sp.web.lists.getByTitle(\"MetaDataDocLib\").rootFolder.setDefaultColumnValues([{ name: \"MetaDataColumnInternalName\", value: { wssId: \"-1\", termId: term.id, termName: defLabel.name, } }]) // check that the defaults have updated const newDefaults = await sp.web.lists.getByTitle(\"MetaDataDocLib\").getDefaultColumnValues();","title":"Column Defaults"},{"location":"sp/column-defaults/#pnpspcolumn-defaults","text":"The column defaults sub-module allows you to manage the default column values on a library or library folder. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { IFieldDefault, IFieldDefaultProps, AllowedDefaultColumnValues } from \"@pnp/sp/column-defaults\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/column-defaults\"; Preset: All import { sp, IFieldDefault, IFieldDefaultProps, AllowedDefaultColumnValues } from \"@pnp/sp/presents/all\";","title":"@pnp/sp/column-defaults"},{"location":"sp/column-defaults/#get-folder-defaults","text":"You can get the default values for a specific folder as shown below: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/column-defaults\"; const defaults = await sp.web.getFolderByServerRelativePath(\"/sites/dev/DefaultColumnValues/fld_GHk5\").getDefaultColumnValues(); /* The resulting structure will have the form: [ { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues/fld_GHk5\", \"value\": \"{the default value}\" }, { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues/fld_GHk5\", \"value\": \"{the default value}\" } ] */","title":"Get Folder Defaults"},{"location":"sp/column-defaults/#set-folder-defaults","text":"When setting the defaults for a folder you need to include the field's internal name and the value. For more examples of other field types see the section Pattern for setting defaults on various column types Note: Be very careful when setting the path as the site collection url is case sensitive import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.getFolderByServerRelativePath(\"/sites/dev/DefaultColumnValues/fld_GHk5\").setDefaultColumnValues([{ name: \"TextField\", value: \"Something\", }, { name: \"NumberField\", value: 14, }]);","title":"Set Folder Defaults"},{"location":"sp/column-defaults/#get-library-defaults","text":"You can also get all of the defaults for the entire library. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/column-defaults\"; const defaults = await sp.web.lists.getByTitle(\"DefaultColumnValues\").getDefaultColumnValues(); /* The resulting structure will have the form: [ { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues\", \"value\": \"{the default value}\" }, { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues/fld_GHk5\", \"value\": \"{a different default value}\" } ] */","title":"Get Library Defaults"},{"location":"sp/column-defaults/#set-library-defaults","text":"You can also set the defaults for an entire library at once (root and all sub-folders). This may be helpful in provisioning a library or other scenarios. When setting the defaults for the entire library you must also include the path value with is the server relative path to the folder. When setting the defaults for a folder you need to include the field's internal name and the value. For more examples of other field types see the section Pattern for setting defaults on various column types Note: Be very careful when setting the path as the site collection url is case sensitive import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.lists.getByTitle(\"DefaultColumnValues\").setDefaultColumnValues([{ name: \"TextField\", path: \"/sites/dev/DefaultColumnValues\", value: \"#PnPjs Rocks!\", }]);","title":"Set Library Defaults"},{"location":"sp/column-defaults/#clear-folder-defaults","text":"If you want to clear all of the folder defaults you can use the clear method: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.getFolderByServerRelativePath(\"/sites/dev/DefaultColumnValues/fld_GHk5\").clearDefaultColumnValues();","title":"Clear Folder Defaults"},{"location":"sp/column-defaults/#clear-library-defaults","text":"If you need to clear all of the default column values in a library you can pass an empty array to the list's setDefaultColumnValues method. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.lists.getByTitle(\"DefaultColumnValues\").setDefaultColumnValues([]);","title":"Clear Library Defaults"},{"location":"sp/column-defaults/#pattern-for-setting-defaults-on-various-column-types","text":"The following is an example of the structure for setting the default column value when using the setDefaultColumnValues that covers the various field types. [{ // Text/Boolean/CurrencyDateTime/Choice/User name: \"TextField\": path: \"/sites/dev/DefaultColumnValues\", value: \"#PnPjs Rocks!\", }, { //Number name: \"NumberField\", path: \"/sites/dev/DefaultColumnValues\", value: 42, }, { //Date name: \"NumberField\", path: \"/sites/dev/DefaultColumnValues\", value: \"1900-01-01T00:00:00Z\", }, { //Date - Today name: \"NumberField\", path: \"/sites/dev/DefaultColumnValues\", value: \"[today]\", }, { //MultiChoice name: \"MultiChoiceField\", path: \"/sites/dev/DefaultColumnValues\", value: [\"Item 1\", \"Item 2\"], }, { //MultiChoice - single value name: \"MultiChoiceField\", path: \"/sites/dev/DefaultColumnValues/folder2\", value: [\"Item 1\"], }, { //Taxonomy - single value name: \"TaxonomyField\", path: \"/sites/dev/DefaultColumnValues\", value: { wssId:\"-1\", termName: \"TaxValueName\", termId: \"924d2077-d5e3-4507-9f36-4a3655e74274\" } }, { //Taxonomy - multiple value name: \"TaxonomyMultiField\", path: \"/sites/dev/DefaultColumnValues\", value: [{ wssId:\"-1\", termName: \"TaxValueName\", termId: \"924d2077-d5e3-4507-9f36-4a3655e74274\" },{ wssId:\"-1\", termName: \"TaxValueName2\", termId: \"95d4c307-dde5-49d8-b861-392e145d94d3\" },] }]);","title":"Pattern for setting defaults on various column types"},{"location":"sp/column-defaults/#taxonomy-full-example","text":"This example shows fully how to get the taxonomy values and set them as a default column value using PnPjs. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/column-defaults\"; import \"@pnp/sp/taxonomy\"; // get the term's info we want to use as the default const term = await sp.termStore.sets.getById(\"ea6fc521-d293-4f3d-9e84-f3a5bc0936ce\").getTermById(\"775c9cf6-c3cd-4db9-8cfa-fc0aeefad93a\")(); // get the default term label const defLabel = term.labels.find(v => v.isDefault); // set the default value using -1, the term id, and the term's default label name await sp.web.lists.getByTitle(\"MetaDataDocLib\").rootFolder.setDefaultColumnValues([{ name: \"MetaDataColumnInternalName\", value: { wssId: \"-1\", termId: term.id, termName: defLabel.name, } }]) // check that the defaults have updated const newDefaults = await sp.web.lists.getByTitle(\"MetaDataDocLib\").getDefaultColumnValues();","title":"Taxonomy Full Example"},{"location":"sp/comments-likes/","text":"@pnp/sp/comments and likes \u00b6 Comments can be accessed through either IItem or IClientsidePage instances, though in slightly different ways. For information on loading clientside pages or items please refer to those articles. These APIs are currently in BETA and are subject to change or may not work on all tenants. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/comments\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; ClientsidePage Comments \u00b6 The IClientsidePage interface has three methods to provide easier access to the comments for a page, without requiring that you load the item separately. Add Comments \u00b6 You can add a comment using the addComment method as shown import { CreateClientsidePage } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/comments/clientside-page\"; const page = await CreateClientsidePage(sp.web, \"mypage\", \"My Page Title\", \"Article\"); // optionally publish the page first await page.save(); const comment = await page.addComment(\"A test comment\"); Get Page Comments \u00b6 import { CreateClientsidePage } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/comments/clientside-page\"; const page = await CreateClientsidePage(sp.web, \"mypage\", \"My Page Title\", \"Article\"); // optionally publish the page first await page.save(); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); const comments = await page.getComments(); enableComments & disableComments \u00b6 Used to control the availability of comments on a page // you need to import the comments sub-module or use the all preset import \"@pnp/sp/comments/clientside-page\"; // our page instance const page: IClientsidePage; // turn on comments await page.enableComments(); // turn off comments await page.disableComments(); GetById \u00b6 import { CreateClientsidePage } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/comments/clientside-page\"; const page = await CreateClientsidePage(sp.web, \"mypage\", \"My Page Title\", \"Article\"); // optionally publish the page first await page.save(); const comment = await page.addComment(\"A test comment\"); const commentData = await page.getCommentById(parseInt(comment.id, 10)); Clear Comments \u00b6 Item Comments \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/comments/item\"; const item = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/SitePages/Test_8q5L.aspx\").getItem(); // as an example, or any of the below options await item.like(); The below examples use a variable named \"item\" which is taken to represent an IItem instance. Comments \u00b6 Get Item Comments \u00b6 const comments = await item.comments(); You can also get the comments merged with instances of the Comment class to immediately start accessing the properties and methods: import { spODataEntityArray } from \"@pnp/sp/odata\"; import { Comment, ICommentData } from \"@pnp/sp/comments\"; const comments = await item.comments(spODataEntityArray(Comment)); // these will be Comment instances in the array comments[0].replies.add({ text: \"#PnPjs is pretty ok!\" }); //load the top 20 replies and comments for an item including likedBy information const comments = await item.comments.expand(\"replies\", \"likedBy\", \"replies/likedBy\").top(20)(); Add Comment \u00b6 // you can add a comment as a string item.comments.add(\"string comment\"); // or you can add it as an object to include mentions item.comments.add({ text: \"comment from object property\" }); Delete a Comment \u00b6 import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); // these will be Comment instances in the array comments[0].delete() Like Comment \u00b6 import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); // these will be Comment instances in the array comments[0].like() Unlike Comment \u00b6 import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); comments[0].unlike() Reply to a Comment \u00b6 import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); const comment: Comment & CommentData = await comments[0].replies.add({ text: \"#PnPjs is pretty ok!\" }); Load Replies to a Comment \u00b6 import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); const replies = await comments[0].replies(); Like \u00b6 You can like/unlike client-side pages, items, and comments on items. See above for how to like or unlike a comment. Below you can see how to like and unlike an items, as well as get the liked by data. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/comments/item\"; import { ILikeData, ILikedByInformation } from \"@pnp/sp/comments\"; // like an item await item.like(); // unlike an item await item.unlike(); // get the liked by data const likedByData: ILikeData[] = await item.getLikedBy(); // get the liked by information const likedByInfo: ILikedByInformation = await item.getLikedByInformation(); To like/unlike a client-side page and get liked by information. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/comments/clientside-page\"; import { ILikedByInformation } from \"@pnp/sp/comments\"; // like a page await page.like(); // unlike a page await page.unlike(); // get the liked by information const likedByInfo: ILikedByInformation = await page.getLikedByInformation();","title":"Comments and Likes"},{"location":"sp/comments-likes/#pnpspcomments-and-likes","text":"Comments can be accessed through either IItem or IClientsidePage instances, though in slightly different ways. For information on loading clientside pages or items please refer to those articles. These APIs are currently in BETA and are subject to change or may not work on all tenants. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/comments\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"@pnp/sp/comments and likes"},{"location":"sp/comments-likes/#clientsidepage-comments","text":"The IClientsidePage interface has three methods to provide easier access to the comments for a page, without requiring that you load the item separately.","title":"ClientsidePage Comments"},{"location":"sp/comments-likes/#add-comments","text":"You can add a comment using the addComment method as shown import { CreateClientsidePage } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/comments/clientside-page\"; const page = await CreateClientsidePage(sp.web, \"mypage\", \"My Page Title\", \"Article\"); // optionally publish the page first await page.save(); const comment = await page.addComment(\"A test comment\");","title":"Add Comments"},{"location":"sp/comments-likes/#get-page-comments","text":"import { CreateClientsidePage } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/comments/clientside-page\"; const page = await CreateClientsidePage(sp.web, \"mypage\", \"My Page Title\", \"Article\"); // optionally publish the page first await page.save(); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); const comments = await page.getComments();","title":"Get Page Comments"},{"location":"sp/comments-likes/#enablecomments-disablecomments","text":"Used to control the availability of comments on a page // you need to import the comments sub-module or use the all preset import \"@pnp/sp/comments/clientside-page\"; // our page instance const page: IClientsidePage; // turn on comments await page.enableComments(); // turn off comments await page.disableComments();","title":"enableComments & disableComments"},{"location":"sp/comments-likes/#getbyid","text":"import { CreateClientsidePage } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/comments/clientside-page\"; const page = await CreateClientsidePage(sp.web, \"mypage\", \"My Page Title\", \"Article\"); // optionally publish the page first await page.save(); const comment = await page.addComment(\"A test comment\"); const commentData = await page.getCommentById(parseInt(comment.id, 10));","title":"GetById"},{"location":"sp/comments-likes/#clear-comments","text":"","title":"Clear Comments"},{"location":"sp/comments-likes/#item-comments","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/comments/item\"; const item = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/SitePages/Test_8q5L.aspx\").getItem(); // as an example, or any of the below options await item.like(); The below examples use a variable named \"item\" which is taken to represent an IItem instance.","title":"Item Comments"},{"location":"sp/comments-likes/#comments","text":"","title":"Comments"},{"location":"sp/comments-likes/#get-item-comments","text":"const comments = await item.comments(); You can also get the comments merged with instances of the Comment class to immediately start accessing the properties and methods: import { spODataEntityArray } from \"@pnp/sp/odata\"; import { Comment, ICommentData } from \"@pnp/sp/comments\"; const comments = await item.comments(spODataEntityArray(Comment)); // these will be Comment instances in the array comments[0].replies.add({ text: \"#PnPjs is pretty ok!\" }); //load the top 20 replies and comments for an item including likedBy information const comments = await item.comments.expand(\"replies\", \"likedBy\", \"replies/likedBy\").top(20)();","title":"Get Item Comments"},{"location":"sp/comments-likes/#add-comment","text":"// you can add a comment as a string item.comments.add(\"string comment\"); // or you can add it as an object to include mentions item.comments.add({ text: \"comment from object property\" });","title":"Add Comment"},{"location":"sp/comments-likes/#delete-a-comment","text":"import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); // these will be Comment instances in the array comments[0].delete()","title":"Delete a Comment"},{"location":"sp/comments-likes/#like-comment","text":"import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); // these will be Comment instances in the array comments[0].like()","title":"Like Comment"},{"location":"sp/comments-likes/#unlike-comment","text":"import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); comments[0].unlike()","title":"Unlike Comment"},{"location":"sp/comments-likes/#reply-to-a-comment","text":"import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); const comment: Comment & CommentData = await comments[0].replies.add({ text: \"#PnPjs is pretty ok!\" });","title":"Reply to a Comment"},{"location":"sp/comments-likes/#load-replies-to-a-comment","text":"import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); const replies = await comments[0].replies();","title":"Load Replies to a Comment"},{"location":"sp/comments-likes/#like","text":"You can like/unlike client-side pages, items, and comments on items. See above for how to like or unlike a comment. Below you can see how to like and unlike an items, as well as get the liked by data. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/comments/item\"; import { ILikeData, ILikedByInformation } from \"@pnp/sp/comments\"; // like an item await item.like(); // unlike an item await item.unlike(); // get the liked by data const likedByData: ILikeData[] = await item.getLikedBy(); // get the liked by information const likedByInfo: ILikedByInformation = await item.getLikedByInformation(); To like/unlike a client-side page and get liked by information. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/comments/clientside-page\"; import { ILikedByInformation } from \"@pnp/sp/comments\"; // like a page await page.like(); // unlike a page await page.unlike(); // get the liked by information const likedByInfo: ILikedByInformation = await page.getLikedByInformation();","title":"Like"},{"location":"sp/content-types/","text":"@pnp/sp/content-types \u00b6 Content Types are used to define sets of columns in SharePoint. IContentTypes \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import { ContentTypes, IContentTypes } from \"@pnp/sp/content-types\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/content-types\"; Preset: All import { sp, ContentTypes, IContentTypes } from \"@pnp/sp/presets/all\"; Add an existing Content Type to a collection \u00b6 The following example shows how to add the built in Picture Content Type to the Documents library. sp.web.lists.getByTitle(\"Documents\").contentTypes.addAvailableContentType(\"0x010102\"); Get a Content Type by Id \u00b6 const d: IContentType = await sp.web.contentTypes.getById(\"0x01\")(); // log content type name to console console.log(d.name); Add a new Content Type \u00b6 To add a new Content Type to a collection, parameters id and name are required. For more information on creating content type IDs reference the Microsoft Documentation . While this documentation references SharePoint 2010 the structure of the IDs has not changed. sp.web.contentTypes.add(\"0x01008D19F38845B0884EBEBE239FDF359184\", \"My Content Type\"); It is also possible to provide a description and group parameter. For other settings, we can use the parameter named 'additionalSettings' which is a TypedHash, meaning you can send whatever properties you'd like in the body (provided that the property is supported by the SharePoint API). //Adding a content type with id, name, description, group and setting it to read only mode (using additionalsettings) sp.web.contentTypes.add(\"0x01008D19F38845B0884EBEBE239FDF359184\", \"My Content Type\", \"This is my content type.\", \"_PnP Content Types\", { ReadOnly: true }); IContentType \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { ContentType, IContentType } from \"@pnp/sp/content-types\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/content-types\"; Preset: All import { sp, ContentType, IContentType } from \"@pnp/sp/presets/all\"; Get the field links \u00b6 Use this method to get a collection containing all the field links (SP.FieldLink) for a Content Type. // get field links from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").fieldLinks(); // log collection of fieldlinks to console console.log(d); Get Content Type fields \u00b6 To get a collection with all fields on the Content Type, simply use this method. // get fields from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").fields(); // log collection of fields to console console.log(d); Get parent Content Type \u00b6 // get parent Content Type from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").parent(); // log name of parent Content Type to console console.log(d.Name) Get Content Type Workflow associations \u00b6 // get workflow associations from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").workflowAssociations(); // log collection of workflow associations to console console.log(d);","title":"Content Types"},{"location":"sp/content-types/#pnpspcontent-types","text":"Content Types are used to define sets of columns in SharePoint.","title":"@pnp/sp/content-types"},{"location":"sp/content-types/#icontenttypes","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import { ContentTypes, IContentTypes } from \"@pnp/sp/content-types\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/content-types\"; Preset: All import { sp, ContentTypes, IContentTypes } from \"@pnp/sp/presets/all\";","title":"IContentTypes"},{"location":"sp/content-types/#add-an-existing-content-type-to-a-collection","text":"The following example shows how to add the built in Picture Content Type to the Documents library. sp.web.lists.getByTitle(\"Documents\").contentTypes.addAvailableContentType(\"0x010102\");","title":"Add an existing Content Type to a collection"},{"location":"sp/content-types/#get-a-content-type-by-id","text":"const d: IContentType = await sp.web.contentTypes.getById(\"0x01\")(); // log content type name to console console.log(d.name);","title":"Get a Content Type by Id"},{"location":"sp/content-types/#add-a-new-content-type","text":"To add a new Content Type to a collection, parameters id and name are required. For more information on creating content type IDs reference the Microsoft Documentation . While this documentation references SharePoint 2010 the structure of the IDs has not changed. sp.web.contentTypes.add(\"0x01008D19F38845B0884EBEBE239FDF359184\", \"My Content Type\"); It is also possible to provide a description and group parameter. For other settings, we can use the parameter named 'additionalSettings' which is a TypedHash, meaning you can send whatever properties you'd like in the body (provided that the property is supported by the SharePoint API). //Adding a content type with id, name, description, group and setting it to read only mode (using additionalsettings) sp.web.contentTypes.add(\"0x01008D19F38845B0884EBEBE239FDF359184\", \"My Content Type\", \"This is my content type.\", \"_PnP Content Types\", { ReadOnly: true });","title":"Add a new Content Type"},{"location":"sp/content-types/#icontenttype","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { ContentType, IContentType } from \"@pnp/sp/content-types\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/content-types\"; Preset: All import { sp, ContentType, IContentType } from \"@pnp/sp/presets/all\";","title":"IContentType"},{"location":"sp/content-types/#get-the-field-links","text":"Use this method to get a collection containing all the field links (SP.FieldLink) for a Content Type. // get field links from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").fieldLinks(); // log collection of fieldlinks to console console.log(d);","title":"Get the field links"},{"location":"sp/content-types/#get-content-type-fields","text":"To get a collection with all fields on the Content Type, simply use this method. // get fields from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").fields(); // log collection of fields to console console.log(d);","title":"Get Content Type fields"},{"location":"sp/content-types/#get-parent-content-type","text":"// get parent Content Type from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").parent(); // log name of parent Content Type to console console.log(d.Name)","title":"Get parent Content Type"},{"location":"sp/content-types/#get-content-type-workflow-associations","text":"// get workflow associations from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").workflowAssociations(); // log collection of workflow associations to console console.log(d);","title":"Get Content Type Workflow associations"},{"location":"sp/custom-irequestclient/","text":"Custom IRequestClient \u00b6 Scenario: You have some special requirements involving auth scenarios or other needs that the library can't directly support. You may need to create a custom IRequestClient implementation to meet those needs as we can't customize the library to handle every case. This article walks you through how to create a custom IRequestClient and register it for use by the library. It is very unlikely this is a step you ever need to take and we encourage you to ask a question in the issues list before going down this path. Create the Client \u00b6 The easiest way to create a new IRequestClient is to subclass the existing SPHttpClient. You can always write a full client from scratch so long as it supports the IRequestClient interface but you need to handle all of the logic for retry, headers, and the request digest. Here we show implementing a client to solve the need discussed in pull request 1264 as an example. // we subclass SPHttpClient class CustomSPHttpClient extends SPHttpClient { // optionally add a constructor, done here as an example constructor(impl?: IHttpClientImpl) { super(impl); } // override the fetchRaw method to ensure we always include the credentials = \"include\" option // you could also override fetch, but fetchRaw ensures no matter what all requests get your custom logic is applied public fetchRaw(url: string, options?: IFetchOptions): Promise { options.credentials = \"include\"; return super.fetchRaw(url, options); } } The final step is to register the custom client with the library so it is used instead of the default. For that we import the registerCustomRequestClientFactory function and call it before our request generating code. You can reset to the default client factory by passing null to this same function. import { sp, registerCustomRequestClientFactory } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; registerCustomRequestClientFactory(() => new CustomSPHttpClient()); // configure your other options sp.setup({ // ... }); // this request will be executed through your custom client const w = await sp.web(); Unregister Custom Client \u00b6 // unregister custom client factory registerCustomRequestClientFactory(null); IRequestClient Interface \u00b6 If you want to 100% roll your own client you need to implement the below interface, found in common. import { IRequestClient } from \"@pnp/core\"; export interface IRequestClient { fetch(url: string, options?: IFetchOptions): Promise; fetchRaw(url: string, options?: IFetchOptions): Promise; get(url: string, options?: IFetchOptions): Promise; post(url: string, options?: IFetchOptions): Promise; patch(url: string, options?: IFetchOptions): Promise; delete(url: string, options?: IFetchOptions): Promise; } Supportability Note \u00b6 We cannot provide support for your custom client implementation, and creating your own client assumes an intimate knowledge of how SharePoint requests work. Again, this is very likely something you will never need to do - and we recommend exhausting all other options before taking this route.","title":"Custom Request Client"},{"location":"sp/custom-irequestclient/#custom-irequestclient","text":"Scenario: You have some special requirements involving auth scenarios or other needs that the library can't directly support. You may need to create a custom IRequestClient implementation to meet those needs as we can't customize the library to handle every case. This article walks you through how to create a custom IRequestClient and register it for use by the library. It is very unlikely this is a step you ever need to take and we encourage you to ask a question in the issues list before going down this path.","title":"Custom IRequestClient"},{"location":"sp/custom-irequestclient/#create-the-client","text":"The easiest way to create a new IRequestClient is to subclass the existing SPHttpClient. You can always write a full client from scratch so long as it supports the IRequestClient interface but you need to handle all of the logic for retry, headers, and the request digest. Here we show implementing a client to solve the need discussed in pull request 1264 as an example. // we subclass SPHttpClient class CustomSPHttpClient extends SPHttpClient { // optionally add a constructor, done here as an example constructor(impl?: IHttpClientImpl) { super(impl); } // override the fetchRaw method to ensure we always include the credentials = \"include\" option // you could also override fetch, but fetchRaw ensures no matter what all requests get your custom logic is applied public fetchRaw(url: string, options?: IFetchOptions): Promise { options.credentials = \"include\"; return super.fetchRaw(url, options); } } The final step is to register the custom client with the library so it is used instead of the default. For that we import the registerCustomRequestClientFactory function and call it before our request generating code. You can reset to the default client factory by passing null to this same function. import { sp, registerCustomRequestClientFactory } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; registerCustomRequestClientFactory(() => new CustomSPHttpClient()); // configure your other options sp.setup({ // ... }); // this request will be executed through your custom client const w = await sp.web();","title":"Create the Client"},{"location":"sp/custom-irequestclient/#unregister-custom-client","text":"// unregister custom client factory registerCustomRequestClientFactory(null);","title":"Unregister Custom Client"},{"location":"sp/custom-irequestclient/#irequestclient-interface","text":"If you want to 100% roll your own client you need to implement the below interface, found in common. import { IRequestClient } from \"@pnp/core\"; export interface IRequestClient { fetch(url: string, options?: IFetchOptions): Promise; fetchRaw(url: string, options?: IFetchOptions): Promise; get(url: string, options?: IFetchOptions): Promise; post(url: string, options?: IFetchOptions): Promise; patch(url: string, options?: IFetchOptions): Promise; delete(url: string, options?: IFetchOptions): Promise; }","title":"IRequestClient Interface"},{"location":"sp/custom-irequestclient/#supportability-note","text":"We cannot provide support for your custom client implementation, and creating your own client assumes an intimate knowledge of how SharePoint requests work. Again, this is very likely something you will never need to do - and we recommend exhausting all other options before taking this route.","title":"Supportability Note"},{"location":"sp/entity-merging/","text":"@pnp/sp - entity merging \u00b6 Sometimes when we make a query entity's data we would like then to immediately run other commands on the returned entity. To have data returned as its representing type we make use of the spODataEntity and spODataEntityArray parsers. The below approach works for all instance types such as List, Web, Item, or Field as examples. Importing spODataEntity and spODataEntityArray \u00b6 You can import spODataEntity and spODataEntityArray in two ways, depending on your use case. The simplest way is to use the presets/all import as shown in the examples. The downside of this approach is that you can't take advantage of selective imports. If you want to take advantage of selective imports while using either of the entity parsers you can use: import { spODataEntity, spODataEntityArray } from \"@pnp/sp/odata\"; The full selective import for the first sample would be: import { sp } from \"@pnp/sp\"; import { spODataEntity } from \"@pnp/sp/odata\"; import { Item, IItem } from \"@pnp/sp/items\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; Request a single entity \u00b6 If we are loading a single entity we use the spODataEntity method. Here we show loading a list item using the Item class and a simple get query. import { sp, spODataEntity, Item, IItem } from \"@pnp/sp/presets/all\"; // interface defining the returned properties interface MyProps { Id: number; } try { // get a list item loaded with data and merged into an instance of Item const item = await sp.web.lists.getByTitle(\"ListTitle\").items.getById(1).usingParser(spODataEntity(Item))(); // log the item id, all properties specified in MyProps will be type checked Logger.write(`Item id: ${item.Id}`); // now we can call update because we have an instance of the Item type to work with as well await item.update({ Title: \"New title.\", }); } catch (e) { Logger.error(e); } Request a collection \u00b6 The same pattern works when requesting a collection of objects with the exception of using the spODataEntityArray method. import { sp, spODataEntityArray, Item, IItem } from \"@pnp/sp/presets/all\"; // interface defining the returned properties interface MyProps { Id: number; Title: string; } try { // get a list item loaded with data and merged into an instance of Item const items = await sp.web.lists.getByTitle(\"OrderByList\").items.select(\"Id\", \"Title\").usingParser(spODataEntityArray(Item))(); Logger.write(`Item id: ${items.length}`); Logger.write(`Item id: ${items[0].Title}`); // now we can call update because we have an instance of the Item type to work with as well await items[0].update({ Title: \"New title.\", }); } catch (e) { Logger.error(e); }","title":"Entity Merging"},{"location":"sp/entity-merging/#pnpsp-entity-merging","text":"Sometimes when we make a query entity's data we would like then to immediately run other commands on the returned entity. To have data returned as its representing type we make use of the spODataEntity and spODataEntityArray parsers. The below approach works for all instance types such as List, Web, Item, or Field as examples.","title":"@pnp/sp - entity merging"},{"location":"sp/entity-merging/#importing-spodataentity-and-spodataentityarray","text":"You can import spODataEntity and spODataEntityArray in two ways, depending on your use case. The simplest way is to use the presets/all import as shown in the examples. The downside of this approach is that you can't take advantage of selective imports. If you want to take advantage of selective imports while using either of the entity parsers you can use: import { spODataEntity, spODataEntityArray } from \"@pnp/sp/odata\"; The full selective import for the first sample would be: import { sp } from \"@pnp/sp\"; import { spODataEntity } from \"@pnp/sp/odata\"; import { Item, IItem } from \"@pnp/sp/items\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\";","title":"Importing spODataEntity and spODataEntityArray"},{"location":"sp/entity-merging/#request-a-single-entity","text":"If we are loading a single entity we use the spODataEntity method. Here we show loading a list item using the Item class and a simple get query. import { sp, spODataEntity, Item, IItem } from \"@pnp/sp/presets/all\"; // interface defining the returned properties interface MyProps { Id: number; } try { // get a list item loaded with data and merged into an instance of Item const item = await sp.web.lists.getByTitle(\"ListTitle\").items.getById(1).usingParser(spODataEntity(Item))(); // log the item id, all properties specified in MyProps will be type checked Logger.write(`Item id: ${item.Id}`); // now we can call update because we have an instance of the Item type to work with as well await item.update({ Title: \"New title.\", }); } catch (e) { Logger.error(e); }","title":"Request a single entity"},{"location":"sp/entity-merging/#request-a-collection","text":"The same pattern works when requesting a collection of objects with the exception of using the spODataEntityArray method. import { sp, spODataEntityArray, Item, IItem } from \"@pnp/sp/presets/all\"; // interface defining the returned properties interface MyProps { Id: number; Title: string; } try { // get a list item loaded with data and merged into an instance of Item const items = await sp.web.lists.getByTitle(\"OrderByList\").items.select(\"Id\", \"Title\").usingParser(spODataEntityArray(Item))(); Logger.write(`Item id: ${items.length}`); Logger.write(`Item id: ${items[0].Title}`); // now we can call update because we have an instance of the Item type to work with as well await items[0].update({ Title: \"New title.\", }); } catch (e) { Logger.error(e); }","title":"Request a collection"},{"location":"sp/features/","text":"@pnp/sp/features \u00b6 Features module provides method to get the details of activated features. And to activate/deactivate features scoped at Site Collection and Web. IFeatures \u00b6 Represents a collection of features. SharePoint Sites and Webs will have a collection of features Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features/site\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features/web\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; Preset: All import { sp, IFeatures, Features } from \"@pnp/sp/presets/all\"; getById \u00b6 Gets the information about a feature for the given GUID import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; const webFeature = await sp.web.features.getById(webFeatureId)(); const siteFeatureId = \"guid-of-site-scope-feature\"; const siteFeature = await sp.site.features.getById(siteFeatureId)(); add \u00b6 Adds (activates) a feature at the Site or Web level import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; let res = await sp.web.features.add(webFeatureId); // Activate with force res = await sp.web.features.add(webFeatureId, true); remove \u00b6 Removes and deactivates the specified feature from the SharePoint Site or Web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; let res = await sp.web.features.remove(webFeatureId); // Deactivate with force res = await sp.web.features.remove(webFeatureId, true); IFeature \u00b6 Represents an instance of a SharePoint feature. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features/site\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features/web\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; Preset: All import { sp, IFeatures, Features, IFeature, Feature } from \"@pnp/sp/presets/all\"; deactivate \u00b6 Deactivates the specified feature from the SharePoint Site or Web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; sp.web.features.getById(webFeatureId).deactivate() // Deactivate with force sp.web.features.getById(webFeatureId).deactivate(true)","title":"Features"},{"location":"sp/features/#pnpspfeatures","text":"Features module provides method to get the details of activated features. And to activate/deactivate features scoped at Site Collection and Web.","title":"@pnp/sp/features"},{"location":"sp/features/#ifeatures","text":"Represents a collection of features. SharePoint Sites and Webs will have a collection of features Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features/site\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features/web\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; Preset: All import { sp, IFeatures, Features } from \"@pnp/sp/presets/all\";","title":"IFeatures"},{"location":"sp/features/#getbyid","text":"Gets the information about a feature for the given GUID import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; const webFeature = await sp.web.features.getById(webFeatureId)(); const siteFeatureId = \"guid-of-site-scope-feature\"; const siteFeature = await sp.site.features.getById(siteFeatureId)();","title":"getById"},{"location":"sp/features/#add","text":"Adds (activates) a feature at the Site or Web level import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; let res = await sp.web.features.add(webFeatureId); // Activate with force res = await sp.web.features.add(webFeatureId, true);","title":"add"},{"location":"sp/features/#remove","text":"Removes and deactivates the specified feature from the SharePoint Site or Web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; let res = await sp.web.features.remove(webFeatureId); // Deactivate with force res = await sp.web.features.remove(webFeatureId, true);","title":"remove"},{"location":"sp/features/#ifeature","text":"Represents an instance of a SharePoint feature. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features/site\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features/web\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; Preset: All import { sp, IFeatures, Features, IFeature, Feature } from \"@pnp/sp/presets/all\";","title":"IFeature"},{"location":"sp/features/#deactivate","text":"Deactivates the specified feature from the SharePoint Site or Web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; sp.web.features.getById(webFeatureId).deactivate() // Deactivate with force sp.web.features.getById(webFeatureId).deactivate(true)","title":"deactivate"},{"location":"sp/fields/","text":"@pnp/sp/lists \u00b6 Fields in SharePoint can be applied to both webs and lists. When referencing a webs' fields you are effectively looking at site columns which are common fields that can be utilized in any list/library in the site. When referencing a lists' fields you are looking at the fields only associated to that particular list. IFields \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import { Fields, IFields } from \"@pnp/sp/fields\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/fields\"; Preset: All import { sp, Fields, IFields } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Fields, IFields } from \"@pnp/sp/presets/core\"; Get Field by Id \u00b6 Gets a field from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/fields\"; // get the field by Id for web const field: IField = sp.web.fields.getById(\"03b05ff4-d95d-45ed-841d-3855f77a2483\"); // get the field by Id for list 'My List' const field2: IFieldInfo = await sp.web.lists.getByTitle(\"My List\").fields.getById(\"03b05ff4-d95d-45ed-841d-3855f77a2483\")(); // we can use this 'field' variable to execute more queries on the field: const r = await field.select(\"Title\")(); // show the response from the server console.log(r.Title); Get Field by Title \u00b6 You can also get a field from the collection by title. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\" import \"@pnp/sp/fields\"; // get the field with the title 'Author' for web const field: IField = sp.web.fields.getByTitle(\"Author\"); // get the field with the title 'Author' for list 'My List' const field2: IFieldInfo = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"Author\")(); // we can use this 'field' variable to run more queries on the field: const r = await field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Get Field by Internal Name or Title \u00b6 You can also get a field from the collection regardless of if the string is the fields internal name or title which can be different. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\" import \"@pnp/sp/fields\"; // get the field with the internal name 'ModifiedBy' for web const field: IField = sp.web.fields.getByInternalNameOrTitle(\"ModifiedBy\"); // get the field with the internal name 'ModifiedBy' for list 'My List' const field2: IFieldInfo = await sp.web.lists.getByTitle(\"My List\").fields.getByInternalNameOrTitle(\"ModifiedBy\")(); // we can use this 'field' variable to run more queries on the field: const r = await field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Create a Field using an XML schema \u00b6 Create a new field by defining an XML schema that assigns all the properties for the field. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // define the schema for your new field, in this case a date field with a default date of today. const fieldSchema = `[today]`; // create the new field in the web const field: IFieldAddResult = await sp.web.fields.createFieldAsXml(fieldSchema); // create the new field in the list 'My List' const field2: IFieldAddResult = await sp.web.lists.getByTitle(\"My List\").fields.createFieldAsXml(fieldSchema); // we can use this 'field' variable to run more queries on the list: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a New Field \u00b6 Use the add method to create a new field where you define the field type import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new field called 'My Field' in web. const field: IFieldAddResult = await sp.web.fields.add(\"My Field\", \"SP.FieldText\", { FieldTypeKind: 3, Group: \"My Group\" }); // create a new field called 'My Field' in the list 'My List' const field2: IFieldAddResult = await sp.web.lists.getByTitle(\"My List\").fields.add(\"My Field\", \"SP.FieldText\", { FieldTypeKind: 3, Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Site Field to a List \u00b6 Use the createFieldAsXml method to add a site field to a list. import { sp } from \"@pnp/sp\"; import { IFieldAddResult } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new field called 'My Field' in web. const field: IFieldAddResult = await sp.web.fields.add(\"My Field\", \"SP.FieldText\", { FieldTypeKind: 3, Group: \"My Group\" }); // add the site field 'My Field' to the list 'My List' const r = await sp.web.lists.getByTitle(\"My List\").fields.createFieldAsXml(field.data.SchemaXml); // log the field Id to console console.log(r.data.Id); Add a Text Field \u00b6 Use the addText method to create a new text field. import { sp } from \"@pnp/sp\"; import { IFieldAddResult } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new text field called 'My Field' in web. const field: IFieldAddResult = await sp.web.fields.addText(\"My Field\", 255, { Group: \"My Group\" }); // create a new text field called 'My Field' in the list 'My List'. const field2: IFieldAddResult = await sp.web.lists.getByTitle(\"My List\").fields.addText(\"My Field\", 255, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Calculated Field \u00b6 Use the addCalculated method to create a new calculated field. import { sp } from \"@pnp/sp\"; import { DateTimeFieldFormatType, FieldTypes } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new calculated field called 'My Field' in web const field = await sp.web.fields.addCalculated(\"My Field\", \"=Modified+1\", DateTimeFieldFormatType.DateOnly, FieldTypes.DateTime, { Group: \"MyGroup\" }); // create a new calculated field called 'My Field' in the list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addCalculated(\"My Field\", \"=Modified+1\", DateTimeFieldFormatType.DateOnly, FieldTypes.DateTime, { Group: \"MyGroup\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Date/Time Field \u00b6 Use the addDateTime method to create a new date/time field. import { sp } from \"@pnp/sp\"; import { DateTimeFieldFormatType, CalendarType, DateTimeFieldFriendlyFormatType } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new date/time field called 'My Field' in web const field = await sp.web.fields.addDateTime(\"My Field\", DateTimeFieldFormatType.DateOnly, CalendarType.Gregorian, DateTimeFieldFriendlyFormatType.Disabled, { Group: \"My Group\" }); // create a new date/time field called 'My Field' in the list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addDateTime(\"My Field\", DateTimeFieldFormatType.DateOnly, CalendarType.Gregorian, DateTimeFieldFriendlyFormatType.Disabled, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Currency Field \u00b6 Use the addCurrency method to create a new currency field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new currency field called 'My Field' in web const field = await sp.web.fields.addCurrency(\"My Field\", 0, 100, 1033, { Group: \"My Group\" }); // create a new currency field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addCurrency(\"My Field\", 0, 100, 1033, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Multi-line Text Field \u00b6 Use the addMultilineText method to create a new multi-line text field. For Enhanced Rich Text mode, see the next section. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new multi-line text field called 'My Field' in web const field = await sp.web.fields.addMultilineText(\"My Field\", 6, true, false, false, true, { Group: \"My Group\" }); // create a new multi-line text field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addMultilineText(\"My Field\", 6, true, false, false, true, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Multi-line Text Field with Enhanced Rich Text \u00b6 The REST endpoint doesn't support setting the RichTextMode field therefore you will need to revert to Xml to create the field. The following is an example that will create a multi-line text field in Enhanced Rich Text mode. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; //Create a new multi-line text field called 'My Field' in web const field = await sp.web.lists.getByTitle(\"My List\").fields.createFieldAsXml( `` ); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Number Field \u00b6 Use the addNumber method to create a new number field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new number field called 'My Field' in web const field = await sp.web.fields.addNumber(\"My Field\", 1, 100, { Group: \"My Group\" }); // create a new number field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addNumber(\"My Field\", 1, 100, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a URL Field \u00b6 Use the addUrl method to create a new url field. import { sp } from \"@pnp/sp\"; import { UrlFieldFormatType } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new url field called 'My Field' in web const field = await sp.web.fields.addUrl(\"My Field\", UrlFieldFormatType.Hyperlink, { Group: \"My Group\" }); // create a new url field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addUrl(\"My Field\", UrlFieldFormatType.Hyperlink, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a User Field \u00b6 Use the addUser method to create a new user field. import { sp } from \"@pnp/sp\"; import { FieldUserSelectionMode } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new user field called 'My Field' in web const field = await sp.web.fields.addUser(\"My Field\", FieldUserSelectionMode.PeopleOnly, { Group: \"My Group\" }); // create a new user field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addUser(\"My Field\", FieldUserSelectionMode.PeopleOnly, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Lookup Field \u00b6 Use the addLookup method to create a new lookup field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; const list = await sp.web.lists.getByTitle(\"My Lookup List\")(); // create a new lookup field called 'My Field' based on an existing list 'My Lookup List' showing 'Title' field in web. const field = await sp.web.fields.addLookup(\"My Field\", list.Id, \"Title\", { Group: \"My Group\" }); // create a new lookup field called 'My Field' based on an existing list 'My Lookup List' showing 'Title' field in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addLookup(\"My Field\", list.Id, \"Title\", { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); // ** // Adding a lookup that supports multiple values takes two calls: const fieldAddResult = await sp.web.fields.addLookup(\"Test Lookup 124\", \"GUID\", \"Title\"); await fieldAddResult.field.update({ Description: 'New Description' }, \"SP.FieldLookup\"); Add a Choice Field \u00b6 Use the addChoice method to create a new choice field. import { sp } from \"@pnp/sp\"; import { ChoiceFieldFormatType } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; const choices = [`ChoiceA`, `ChoiceB`, `ChoiceC`]; // create a new choice field called 'My Field' in web const field = await sp.web.fields.addChoice(\"My Field\", choices, ChoiceFieldFormatType.Dropdown, false, { Group: \"My Group\" }); // create a new choice field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addChoice(\"My Field\", choices, ChoiceFieldFormatType.Dropdown, false, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Multi-Choice Field \u00b6 Use the addMultiChoice method to create a new multi-choice field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; const choices = [`ChoiceA`, `ChoiceB`, `ChoiceC`]; // create a new multi-choice field called 'My Field' in web const field = await sp.web.fields.addMultiChoice(\"My Field\", choices, false, { Group: \"My Group\" }); // create a new multi-choice field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addMultiChoice(\"My Field\", choices, false, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Boolean Field \u00b6 Use the addBoolean method to create a new boolean field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new boolean field called 'My Field' in web const field = await sp.web.fields.addBoolean(\"My Field\", { Group: \"My Group\" }); // create a new boolean field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addBoolean(\"My Field\", { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Dependent Lookup Field \u00b6 Use the addDependentLookupField method to create a new dependent lookup field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new dependent lookup field called 'My Dep Field' showing 'Description' based on an existing 'My Field' lookup field in web. const field = await sp.web.fields.getByTitle(\"My Field\")(); const fieldDep = await sp.web.fields.addDependentLookupField(\"My Dep Field\", field.Id, \"Description\"); // create a new dependent lookup field called 'My Dep Field' showing 'Description' based on an existing 'My Field' lookup field in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\")(); const fieldDep2 = await sp.web.lists.getByTitle(\"My List\").fields.addDependentLookupField(\"My Dep Field\", field2.Id, \"Description\"); // we can use this 'fieldDep' variable to run more queries on the field: const r = await fieldDep.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Location Field \u00b6 Use the addLocation method to create a new location field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new location field called 'My Field' in web const field = await sp.web.fields.addLocation(\"My Field\", { Group: \"My Group\" }); // create a new location field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addLocation(\"My Field\", { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Delete a Field \u00b6 Use the delete method to delete a field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/fields\"; // delete one or more fields from web, returns boolean const result = await sp.web.fields.getByTitle(\"My Field\").delete(); const result2 = await sp.web.fields.getByTitle(\"My Field 2\").delete(); // delete one or more fields from list 'My List', returns boolean const result = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").delete(); const result2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field 2\").delete(); Update a Field \u00b6 Use the update method to update a field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // update the field called 'My Field' with a description in web, returns FieldUpdateResult const fieldUpdate = await sp.web.fields.getByTitle(\"My Field\").update({ Description: \"My Description\" }); // update the field called 'My Field' with a description in list 'My List', returns FieldUpdateResult const fieldUpdate2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").update({ Description: \"My Description\" }); // if you need to update a field with properties for a specific field type you can optionally include the field type as a second param // if you do not include it we will look up the type, but that adds a call to the server const fieldUpdate2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Look up Field\").update({ RelationshipDeleteBehavior: 1 }, \"SP.FieldLookup\"); Show a Field in the Display Form \u00b6 Use the setShowInDisplayForm method to add a field to the display form. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // show field called 'My Field' in display form throughout web await sp.web.fields.getByTitle(\"My Field\").setShowInDisplayForm(true); // show field called 'My Field' in display form for list 'My List' await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").setShowInDisplayForm(true); Show a Field in the Edit Form \u00b6 Use the setShowInEditForm method to add a field to the edit form. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // show field called 'My Field' in edit form throughout web await sp.web.fields.getByTitle(\"My Field\").setShowInEditForm(true); // show field called 'My Field' in edit form for list 'My List' await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").setShowInEditForm(true); Show a Field in the New Form \u00b6 Use the setShowInNewForm method to add a field to the display form. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // show field called 'My Field' in new form throughout web await sp.web.fields.getByTitle(\"My Field\").setShowInNewForm(true); // show field called 'My Field' in new form for list 'My List' await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").setShowInNewForm(true);","title":"Fields"},{"location":"sp/fields/#pnpsplists","text":"Fields in SharePoint can be applied to both webs and lists. When referencing a webs' fields you are effectively looking at site columns which are common fields that can be utilized in any list/library in the site. When referencing a lists' fields you are looking at the fields only associated to that particular list.","title":"@pnp/sp/lists"},{"location":"sp/fields/#ifields","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import { Fields, IFields } from \"@pnp/sp/fields\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/fields\"; Preset: All import { sp, Fields, IFields } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Fields, IFields } from \"@pnp/sp/presets/core\";","title":"IFields"},{"location":"sp/fields/#get-field-by-id","text":"Gets a field from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/fields\"; // get the field by Id for web const field: IField = sp.web.fields.getById(\"03b05ff4-d95d-45ed-841d-3855f77a2483\"); // get the field by Id for list 'My List' const field2: IFieldInfo = await sp.web.lists.getByTitle(\"My List\").fields.getById(\"03b05ff4-d95d-45ed-841d-3855f77a2483\")(); // we can use this 'field' variable to execute more queries on the field: const r = await field.select(\"Title\")(); // show the response from the server console.log(r.Title);","title":"Get Field by Id"},{"location":"sp/fields/#get-field-by-title","text":"You can also get a field from the collection by title. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\" import \"@pnp/sp/fields\"; // get the field with the title 'Author' for web const field: IField = sp.web.fields.getByTitle(\"Author\"); // get the field with the title 'Author' for list 'My List' const field2: IFieldInfo = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"Author\")(); // we can use this 'field' variable to run more queries on the field: const r = await field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Get Field by Title"},{"location":"sp/fields/#get-field-by-internal-name-or-title","text":"You can also get a field from the collection regardless of if the string is the fields internal name or title which can be different. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\" import \"@pnp/sp/fields\"; // get the field with the internal name 'ModifiedBy' for web const field: IField = sp.web.fields.getByInternalNameOrTitle(\"ModifiedBy\"); // get the field with the internal name 'ModifiedBy' for list 'My List' const field2: IFieldInfo = await sp.web.lists.getByTitle(\"My List\").fields.getByInternalNameOrTitle(\"ModifiedBy\")(); // we can use this 'field' variable to run more queries on the field: const r = await field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Get Field by Internal Name or Title"},{"location":"sp/fields/#create-a-field-using-an-xml-schema","text":"Create a new field by defining an XML schema that assigns all the properties for the field. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // define the schema for your new field, in this case a date field with a default date of today. const fieldSchema = `[today]`; // create the new field in the web const field: IFieldAddResult = await sp.web.fields.createFieldAsXml(fieldSchema); // create the new field in the list 'My List' const field2: IFieldAddResult = await sp.web.lists.getByTitle(\"My List\").fields.createFieldAsXml(fieldSchema); // we can use this 'field' variable to run more queries on the list: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Create a Field using an XML schema"},{"location":"sp/fields/#add-a-new-field","text":"Use the add method to create a new field where you define the field type import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new field called 'My Field' in web. const field: IFieldAddResult = await sp.web.fields.add(\"My Field\", \"SP.FieldText\", { FieldTypeKind: 3, Group: \"My Group\" }); // create a new field called 'My Field' in the list 'My List' const field2: IFieldAddResult = await sp.web.lists.getByTitle(\"My List\").fields.add(\"My Field\", \"SP.FieldText\", { FieldTypeKind: 3, Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a New Field"},{"location":"sp/fields/#add-a-site-field-to-a-list","text":"Use the createFieldAsXml method to add a site field to a list. import { sp } from \"@pnp/sp\"; import { IFieldAddResult } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new field called 'My Field' in web. const field: IFieldAddResult = await sp.web.fields.add(\"My Field\", \"SP.FieldText\", { FieldTypeKind: 3, Group: \"My Group\" }); // add the site field 'My Field' to the list 'My List' const r = await sp.web.lists.getByTitle(\"My List\").fields.createFieldAsXml(field.data.SchemaXml); // log the field Id to console console.log(r.data.Id);","title":"Add a Site Field to a List"},{"location":"sp/fields/#add-a-text-field","text":"Use the addText method to create a new text field. import { sp } from \"@pnp/sp\"; import { IFieldAddResult } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new text field called 'My Field' in web. const field: IFieldAddResult = await sp.web.fields.addText(\"My Field\", 255, { Group: \"My Group\" }); // create a new text field called 'My Field' in the list 'My List'. const field2: IFieldAddResult = await sp.web.lists.getByTitle(\"My List\").fields.addText(\"My Field\", 255, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Text Field"},{"location":"sp/fields/#add-a-calculated-field","text":"Use the addCalculated method to create a new calculated field. import { sp } from \"@pnp/sp\"; import { DateTimeFieldFormatType, FieldTypes } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new calculated field called 'My Field' in web const field = await sp.web.fields.addCalculated(\"My Field\", \"=Modified+1\", DateTimeFieldFormatType.DateOnly, FieldTypes.DateTime, { Group: \"MyGroup\" }); // create a new calculated field called 'My Field' in the list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addCalculated(\"My Field\", \"=Modified+1\", DateTimeFieldFormatType.DateOnly, FieldTypes.DateTime, { Group: \"MyGroup\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Calculated Field"},{"location":"sp/fields/#add-a-datetime-field","text":"Use the addDateTime method to create a new date/time field. import { sp } from \"@pnp/sp\"; import { DateTimeFieldFormatType, CalendarType, DateTimeFieldFriendlyFormatType } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new date/time field called 'My Field' in web const field = await sp.web.fields.addDateTime(\"My Field\", DateTimeFieldFormatType.DateOnly, CalendarType.Gregorian, DateTimeFieldFriendlyFormatType.Disabled, { Group: \"My Group\" }); // create a new date/time field called 'My Field' in the list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addDateTime(\"My Field\", DateTimeFieldFormatType.DateOnly, CalendarType.Gregorian, DateTimeFieldFriendlyFormatType.Disabled, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Date/Time Field"},{"location":"sp/fields/#add-a-currency-field","text":"Use the addCurrency method to create a new currency field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new currency field called 'My Field' in web const field = await sp.web.fields.addCurrency(\"My Field\", 0, 100, 1033, { Group: \"My Group\" }); // create a new currency field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addCurrency(\"My Field\", 0, 100, 1033, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Currency Field"},{"location":"sp/fields/#add-a-multi-line-text-field","text":"Use the addMultilineText method to create a new multi-line text field. For Enhanced Rich Text mode, see the next section. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new multi-line text field called 'My Field' in web const field = await sp.web.fields.addMultilineText(\"My Field\", 6, true, false, false, true, { Group: \"My Group\" }); // create a new multi-line text field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addMultilineText(\"My Field\", 6, true, false, false, true, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Multi-line Text Field"},{"location":"sp/fields/#add-a-multi-line-text-field-with-enhanced-rich-text","text":"The REST endpoint doesn't support setting the RichTextMode field therefore you will need to revert to Xml to create the field. The following is an example that will create a multi-line text field in Enhanced Rich Text mode. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; //Create a new multi-line text field called 'My Field' in web const field = await sp.web.lists.getByTitle(\"My List\").fields.createFieldAsXml( `` ); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Multi-line Text Field with Enhanced Rich Text"},{"location":"sp/fields/#add-a-number-field","text":"Use the addNumber method to create a new number field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new number field called 'My Field' in web const field = await sp.web.fields.addNumber(\"My Field\", 1, 100, { Group: \"My Group\" }); // create a new number field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addNumber(\"My Field\", 1, 100, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Number Field"},{"location":"sp/fields/#add-a-url-field","text":"Use the addUrl method to create a new url field. import { sp } from \"@pnp/sp\"; import { UrlFieldFormatType } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new url field called 'My Field' in web const field = await sp.web.fields.addUrl(\"My Field\", UrlFieldFormatType.Hyperlink, { Group: \"My Group\" }); // create a new url field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addUrl(\"My Field\", UrlFieldFormatType.Hyperlink, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a URL Field"},{"location":"sp/fields/#add-a-user-field","text":"Use the addUser method to create a new user field. import { sp } from \"@pnp/sp\"; import { FieldUserSelectionMode } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new user field called 'My Field' in web const field = await sp.web.fields.addUser(\"My Field\", FieldUserSelectionMode.PeopleOnly, { Group: \"My Group\" }); // create a new user field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addUser(\"My Field\", FieldUserSelectionMode.PeopleOnly, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a User Field"},{"location":"sp/fields/#add-a-lookup-field","text":"Use the addLookup method to create a new lookup field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; const list = await sp.web.lists.getByTitle(\"My Lookup List\")(); // create a new lookup field called 'My Field' based on an existing list 'My Lookup List' showing 'Title' field in web. const field = await sp.web.fields.addLookup(\"My Field\", list.Id, \"Title\", { Group: \"My Group\" }); // create a new lookup field called 'My Field' based on an existing list 'My Lookup List' showing 'Title' field in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addLookup(\"My Field\", list.Id, \"Title\", { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); // ** // Adding a lookup that supports multiple values takes two calls: const fieldAddResult = await sp.web.fields.addLookup(\"Test Lookup 124\", \"GUID\", \"Title\"); await fieldAddResult.field.update({ Description: 'New Description' }, \"SP.FieldLookup\");","title":"Add a Lookup Field"},{"location":"sp/fields/#add-a-choice-field","text":"Use the addChoice method to create a new choice field. import { sp } from \"@pnp/sp\"; import { ChoiceFieldFormatType } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; const choices = [`ChoiceA`, `ChoiceB`, `ChoiceC`]; // create a new choice field called 'My Field' in web const field = await sp.web.fields.addChoice(\"My Field\", choices, ChoiceFieldFormatType.Dropdown, false, { Group: \"My Group\" }); // create a new choice field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addChoice(\"My Field\", choices, ChoiceFieldFormatType.Dropdown, false, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Choice Field"},{"location":"sp/fields/#add-a-multi-choice-field","text":"Use the addMultiChoice method to create a new multi-choice field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; const choices = [`ChoiceA`, `ChoiceB`, `ChoiceC`]; // create a new multi-choice field called 'My Field' in web const field = await sp.web.fields.addMultiChoice(\"My Field\", choices, false, { Group: \"My Group\" }); // create a new multi-choice field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addMultiChoice(\"My Field\", choices, false, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Multi-Choice Field"},{"location":"sp/fields/#add-a-boolean-field","text":"Use the addBoolean method to create a new boolean field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new boolean field called 'My Field' in web const field = await sp.web.fields.addBoolean(\"My Field\", { Group: \"My Group\" }); // create a new boolean field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addBoolean(\"My Field\", { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Boolean Field"},{"location":"sp/fields/#add-a-dependent-lookup-field","text":"Use the addDependentLookupField method to create a new dependent lookup field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new dependent lookup field called 'My Dep Field' showing 'Description' based on an existing 'My Field' lookup field in web. const field = await sp.web.fields.getByTitle(\"My Field\")(); const fieldDep = await sp.web.fields.addDependentLookupField(\"My Dep Field\", field.Id, \"Description\"); // create a new dependent lookup field called 'My Dep Field' showing 'Description' based on an existing 'My Field' lookup field in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\")(); const fieldDep2 = await sp.web.lists.getByTitle(\"My List\").fields.addDependentLookupField(\"My Dep Field\", field2.Id, \"Description\"); // we can use this 'fieldDep' variable to run more queries on the field: const r = await fieldDep.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Dependent Lookup Field"},{"location":"sp/fields/#add-a-location-field","text":"Use the addLocation method to create a new location field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new location field called 'My Field' in web const field = await sp.web.fields.addLocation(\"My Field\", { Group: \"My Group\" }); // create a new location field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addLocation(\"My Field\", { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Location Field"},{"location":"sp/fields/#delete-a-field","text":"Use the delete method to delete a field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/fields\"; // delete one or more fields from web, returns boolean const result = await sp.web.fields.getByTitle(\"My Field\").delete(); const result2 = await sp.web.fields.getByTitle(\"My Field 2\").delete(); // delete one or more fields from list 'My List', returns boolean const result = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").delete(); const result2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field 2\").delete();","title":"Delete a Field"},{"location":"sp/fields/#update-a-field","text":"Use the update method to update a field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // update the field called 'My Field' with a description in web, returns FieldUpdateResult const fieldUpdate = await sp.web.fields.getByTitle(\"My Field\").update({ Description: \"My Description\" }); // update the field called 'My Field' with a description in list 'My List', returns FieldUpdateResult const fieldUpdate2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").update({ Description: \"My Description\" }); // if you need to update a field with properties for a specific field type you can optionally include the field type as a second param // if you do not include it we will look up the type, but that adds a call to the server const fieldUpdate2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Look up Field\").update({ RelationshipDeleteBehavior: 1 }, \"SP.FieldLookup\");","title":"Update a Field"},{"location":"sp/fields/#show-a-field-in-the-display-form","text":"Use the setShowInDisplayForm method to add a field to the display form. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // show field called 'My Field' in display form throughout web await sp.web.fields.getByTitle(\"My Field\").setShowInDisplayForm(true); // show field called 'My Field' in display form for list 'My List' await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").setShowInDisplayForm(true);","title":"Show a Field in the Display Form"},{"location":"sp/fields/#show-a-field-in-the-edit-form","text":"Use the setShowInEditForm method to add a field to the edit form. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // show field called 'My Field' in edit form throughout web await sp.web.fields.getByTitle(\"My Field\").setShowInEditForm(true); // show field called 'My Field' in edit form for list 'My List' await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").setShowInEditForm(true);","title":"Show a Field in the Edit Form"},{"location":"sp/fields/#show-a-field-in-the-new-form","text":"Use the setShowInNewForm method to add a field to the display form. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // show field called 'My Field' in new form throughout web await sp.web.fields.getByTitle(\"My Field\").setShowInNewForm(true); // show field called 'My Field' in new form for list 'My List' await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").setShowInNewForm(true);","title":"Show a Field in the New Form"},{"location":"sp/files/","text":"@pnp/sp/files \u00b6 One of the more challenging tasks on the client side is working with SharePoint files, especially if they are large files. We have added some methods to the library to help and their use is outlined below. Reading Files \u00b6 Reading files from the client using REST is covered in the below examples. The important thing to remember is choosing which format you want the file in so you can appropriately process it. You can retrieve a file as Blob, Buffer, JSON, or Text. If you have a special requirement you could also write your own parser . import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; const blob: Blob = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.avi\").getBlob(); const buffer: ArrayBuffer = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.avi\").getBuffer(); const json: any = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.json\").getJSON(); const text: string = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.txt\").getText(); // all of these also work from a file object no matter how you access it const text2: string = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/documents\").files.getByName(\"file.txt\").getText(); getFileByUrl \u00b6 Added in 2.0.4 This method supports opening files from sharing links or absolute urls. The file must reside in the site from which you are trying to open the file. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files/web\"; const url = \"{absolute file url OR sharing url}\"; // file is an IFile and supports all the file operations const file = sp.web.getFileByUrl(url); // for example const fileContent = await file.getText(); Adding Files \u00b6 Likewise you can add files using one of two methods, add or addChunked. AddChunked is appropriate for larger files, generally larger than 10 MB but this may differ based on your bandwidth/latency so you can adjust the code to use the chunked method. The below example shows getting the file object from an input and uploading it to SharePoint, choosing the upload method based on file size. declare var require: (s: string) => any; import { ConsoleListener, Logger, LogLevel } from \"@pnp/logging\"; import { sp } from \"@pnp/sp\"; import { Web } from \"@pnp/sp/webs\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import { auth } from \"./auth\"; let $ = require(\"jquery\"); // <-- used here for illustration let siteUrl = \"https://mytenant.sharepoint.com/sites/dev\"; // comment this out for non-node execution // auth(siteUrl); Logger.subscribe(new ConsoleListener()); Logger.activeLogLevel = LogLevel.Verbose; let web = Web(siteUrl); $(() => { $(\"#testingdiv\").append(\"\"); $(\"#thebuttontodoit\").on('click', async (e) => { e.preventDefault(); let input = document.getElementById(\"thefileinput\"); let file = input.files[0]; // you can adjust this number to control what size files are uploaded in chunks if (file.size <= 10485760) { // small upload await web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.add(file.name, file, true); Logger.write(\"done\"); } else { // large upload await web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.addChunked(file.name, file, data => { Logger.log({ data: data, level: LogLevel.Verbose, message: \"progress\" }); }, true); Logger.write(\"done!\") } }); }); Adding a file using Nodejs Streams \u00b6 If you are working in nodejs you can also add a file using a stream. This example makes a copy of a file using streams. // triggers auto-application of extensions, in this case to add getStream import \"@pnp/nodejs\"; // get a stream of an existing file const sr = await sp.web.getFileByServerRelativePath(\"/sites/dev/shared documents/old.md\").getStream(); // now add the stream as a new file, remember to set the content-length header const fr = await sp.web.lists.getByTitle(\"Documents\").rootFolder.files.configure({ headers: { \"content-length\": `${sr.knownLength}`, }, }).add(\"new.md\", sr.body); Setting Associated Item Values \u00b6 You can also update the file properties of a newly uploaded file using code similar to the below snippet: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; const file = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.add(\"file.name\", \"file\", true); const item = await file.file.getItem(); await item.update({ Title: \"A Title\", OtherField: \"My Other Value\" }); AddUsingPath \u00b6 If you need to support the percent or pound characters you can use the addUsingPath method of IFiles import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; const file = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.addUsingPath(\"file%#%.name\", \"content\"); Update File Content \u00b6 You can of course use similar methods to update existing files as shown below. This overwrites the existing content in the file. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/test.txt\").setContent(\"New string content for the file.\"); await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/test.mp4\").setContentChunked(file); Check in, Check out, and Approve & Deny \u00b6 The library provides helper methods for checking in, checking out, and approving files. Examples of these methods are shown below. Check In \u00b6 Check in takes two optional arguments, comment and check in type. import { sp } from \"@pnp/sp\"; import { CheckinType } from \"@pnp/sp/files\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // default options with empty comment and CheckinType.Major await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkin(); console.log(\"File checked in!\"); // supply a comment (< 1024 chars) and using default check in type CheckinType.Major await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkin(\"A comment\"); console.log(\"File checked in!\"); // Supply both comment and check in type await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkin(\"A comment\", CheckinType.Overwrite); console.log(\"File checked in!\"); Check Out \u00b6 Check out takes no arguments. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkout(); console.log(\"File checked out!\"); Approve and Deny \u00b6 You can also approve or deny files in libraries that use approval. Approve takes a single required argument of comment, the comment is optional for deny. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").approve(\"Approval Comment\"); console.log(\"File approved!\"); // deny with no comment await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").deny(); console.log(\"File denied!\"); // deny with a supplied comment. await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").deny(\"Deny comment\"); console.log(\"File denied!\"); Publish and Unpublish \u00b6 You can both publish and unpublish a file using the library. Both methods take an optional comment argument. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // publish with no comment await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").publish(); console.log(\"File published!\"); // publish with a supplied comment. await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").publish(\"Publish comment\"); console.log(\"File published!\"); // unpublish with no comment await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").unpublish(); console.log(\"File unpublished!\"); // unpublish with a supplied comment. await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").unpublish(\"Unpublish comment\"); console.log(\"File unpublished!\"); Advanced Upload Options \u00b6 Both the addChunked and setContentChunked methods support options beyond just supplying the file content. progress function \u00b6 A method that is called each time a chunk is uploaded and provides enough information to report progress or update a progress bar easily. The method has the signature: (data: ChunkedFileUploadProgressData) => void The data interface is: export interface ChunkedFileUploadProgressData { stage: \"starting\" | \"continue\" | \"finishing\"; blockNumber: number; totalBlocks: number; chunkSize: number; currentPointer: number; fileSize: number; } chunkSize \u00b6 This property controls the size of the individual chunks and is defaulted to 10485760 bytes (10 MB). You can adjust this based on your bandwidth needs - especially if writing code for mobile uploads or you are seeing frequent timeouts. getItem \u00b6 This method allows you to get the item associated with this file. You can optionally specify one or more select fields. The result will be merged with a new Item instance so you will have both the returned property values and chaining ability in a single object. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/security\"; const item = await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.txt\").getItem(); console.log(item); const item2 = await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.txt\").getItem(\"Title\", \"Modified\"); console.log(item2); // you can also chain directly off this item instance const perms = await item.getCurrentUserEffectivePermissions(); console.log(perms); You can also supply a generic typing parameter and the resulting type will be a union type of Item and the generic type parameter. This allows you to have proper intellisense and type checking. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/items\"; import \"@pnp/sp/security\"; // also supports typing the objects so your type will be a union type const item = await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.txt\").getItem<{ Id: number, Title: string }>(\"Id\", \"Title\"); // You get intellisense and proper typing of the returned object console.log(`Id: ${item.Id} -- ${item.Title}`); // You can also chain directly off this item instance const perms = await item.getCurrentUserEffectivePermissions(); console.log(perms); move \u00b6 It's possible to move a file to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").moveTo(destinationUrl); copy \u00b6 It's possible to copy a file to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").copyTo(destinationUrl, false); move by path \u00b6 It's possible to move a file to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev2/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").moveByPath(destinationUrl, false, true); copy by path \u00b6 It's possible to copy a file to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev2/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").copyByPath(destinationUrl, false, true); getFileById \u00b6 You can get a file by Id from a web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import { IFile } from \"@pnp/sp/files\"; const file: IFile = sp.web.getFileById(\"2b281c7b-ece9-4b76-82f9-f5cf5e152ba0\"); delete \u00b6 Deletes a file import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; await sp.web.rootFolder.files.getByName(\"name.txt\").delete(); delete with params \u00b6 Added in 2.0.9 Deletes a file with options import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; await sp.web.rootFolder.files.getByName(\"name.txt\").deleteWithParams({ BypassSharedLock: true, }); exists \u00b6 Added in 2.0.9 Checks to see if a file exists import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; const exists = await sp.web.rootFolder.files.getByName(\"name.txt\").exists();","title":"Files"},{"location":"sp/files/#pnpspfiles","text":"One of the more challenging tasks on the client side is working with SharePoint files, especially if they are large files. We have added some methods to the library to help and their use is outlined below.","title":"@pnp/sp/files"},{"location":"sp/files/#reading-files","text":"Reading files from the client using REST is covered in the below examples. The important thing to remember is choosing which format you want the file in so you can appropriately process it. You can retrieve a file as Blob, Buffer, JSON, or Text. If you have a special requirement you could also write your own parser . import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; const blob: Blob = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.avi\").getBlob(); const buffer: ArrayBuffer = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.avi\").getBuffer(); const json: any = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.json\").getJSON(); const text: string = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.txt\").getText(); // all of these also work from a file object no matter how you access it const text2: string = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/documents\").files.getByName(\"file.txt\").getText();","title":"Reading Files"},{"location":"sp/files/#getfilebyurl","text":"Added in 2.0.4 This method supports opening files from sharing links or absolute urls. The file must reside in the site from which you are trying to open the file. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files/web\"; const url = \"{absolute file url OR sharing url}\"; // file is an IFile and supports all the file operations const file = sp.web.getFileByUrl(url); // for example const fileContent = await file.getText();","title":"getFileByUrl"},{"location":"sp/files/#adding-files","text":"Likewise you can add files using one of two methods, add or addChunked. AddChunked is appropriate for larger files, generally larger than 10 MB but this may differ based on your bandwidth/latency so you can adjust the code to use the chunked method. The below example shows getting the file object from an input and uploading it to SharePoint, choosing the upload method based on file size. declare var require: (s: string) => any; import { ConsoleListener, Logger, LogLevel } from \"@pnp/logging\"; import { sp } from \"@pnp/sp\"; import { Web } from \"@pnp/sp/webs\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import { auth } from \"./auth\"; let $ = require(\"jquery\"); // <-- used here for illustration let siteUrl = \"https://mytenant.sharepoint.com/sites/dev\"; // comment this out for non-node execution // auth(siteUrl); Logger.subscribe(new ConsoleListener()); Logger.activeLogLevel = LogLevel.Verbose; let web = Web(siteUrl); $(() => { $(\"#testingdiv\").append(\"\"); $(\"#thebuttontodoit\").on('click', async (e) => { e.preventDefault(); let input = document.getElementById(\"thefileinput\"); let file = input.files[0]; // you can adjust this number to control what size files are uploaded in chunks if (file.size <= 10485760) { // small upload await web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.add(file.name, file, true); Logger.write(\"done\"); } else { // large upload await web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.addChunked(file.name, file, data => { Logger.log({ data: data, level: LogLevel.Verbose, message: \"progress\" }); }, true); Logger.write(\"done!\") } }); });","title":"Adding Files"},{"location":"sp/files/#adding-a-file-using-nodejs-streams","text":"If you are working in nodejs you can also add a file using a stream. This example makes a copy of a file using streams. // triggers auto-application of extensions, in this case to add getStream import \"@pnp/nodejs\"; // get a stream of an existing file const sr = await sp.web.getFileByServerRelativePath(\"/sites/dev/shared documents/old.md\").getStream(); // now add the stream as a new file, remember to set the content-length header const fr = await sp.web.lists.getByTitle(\"Documents\").rootFolder.files.configure({ headers: { \"content-length\": `${sr.knownLength}`, }, }).add(\"new.md\", sr.body);","title":"Adding a file using Nodejs Streams"},{"location":"sp/files/#setting-associated-item-values","text":"You can also update the file properties of a newly uploaded file using code similar to the below snippet: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; const file = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.add(\"file.name\", \"file\", true); const item = await file.file.getItem(); await item.update({ Title: \"A Title\", OtherField: \"My Other Value\" });","title":"Setting Associated Item Values"},{"location":"sp/files/#addusingpath","text":"If you need to support the percent or pound characters you can use the addUsingPath method of IFiles import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; const file = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.addUsingPath(\"file%#%.name\", \"content\");","title":"AddUsingPath"},{"location":"sp/files/#update-file-content","text":"You can of course use similar methods to update existing files as shown below. This overwrites the existing content in the file. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/test.txt\").setContent(\"New string content for the file.\"); await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/test.mp4\").setContentChunked(file);","title":"Update File Content"},{"location":"sp/files/#check-in-check-out-and-approve-deny","text":"The library provides helper methods for checking in, checking out, and approving files. Examples of these methods are shown below.","title":"Check in, Check out, and Approve & Deny"},{"location":"sp/files/#check-in","text":"Check in takes two optional arguments, comment and check in type. import { sp } from \"@pnp/sp\"; import { CheckinType } from \"@pnp/sp/files\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // default options with empty comment and CheckinType.Major await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkin(); console.log(\"File checked in!\"); // supply a comment (< 1024 chars) and using default check in type CheckinType.Major await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkin(\"A comment\"); console.log(\"File checked in!\"); // Supply both comment and check in type await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkin(\"A comment\", CheckinType.Overwrite); console.log(\"File checked in!\");","title":"Check In"},{"location":"sp/files/#check-out","text":"Check out takes no arguments. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkout(); console.log(\"File checked out!\");","title":"Check Out"},{"location":"sp/files/#approve-and-deny","text":"You can also approve or deny files in libraries that use approval. Approve takes a single required argument of comment, the comment is optional for deny. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").approve(\"Approval Comment\"); console.log(\"File approved!\"); // deny with no comment await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").deny(); console.log(\"File denied!\"); // deny with a supplied comment. await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").deny(\"Deny comment\"); console.log(\"File denied!\");","title":"Approve and Deny"},{"location":"sp/files/#publish-and-unpublish","text":"You can both publish and unpublish a file using the library. Both methods take an optional comment argument. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // publish with no comment await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").publish(); console.log(\"File published!\"); // publish with a supplied comment. await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").publish(\"Publish comment\"); console.log(\"File published!\"); // unpublish with no comment await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").unpublish(); console.log(\"File unpublished!\"); // unpublish with a supplied comment. await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").unpublish(\"Unpublish comment\"); console.log(\"File unpublished!\");","title":"Publish and Unpublish"},{"location":"sp/files/#advanced-upload-options","text":"Both the addChunked and setContentChunked methods support options beyond just supplying the file content.","title":"Advanced Upload Options"},{"location":"sp/files/#progress-function","text":"A method that is called each time a chunk is uploaded and provides enough information to report progress or update a progress bar easily. The method has the signature: (data: ChunkedFileUploadProgressData) => void The data interface is: export interface ChunkedFileUploadProgressData { stage: \"starting\" | \"continue\" | \"finishing\"; blockNumber: number; totalBlocks: number; chunkSize: number; currentPointer: number; fileSize: number; }","title":"progress function"},{"location":"sp/files/#chunksize","text":"This property controls the size of the individual chunks and is defaulted to 10485760 bytes (10 MB). You can adjust this based on your bandwidth needs - especially if writing code for mobile uploads or you are seeing frequent timeouts.","title":"chunkSize"},{"location":"sp/files/#getitem","text":"This method allows you to get the item associated with this file. You can optionally specify one or more select fields. The result will be merged with a new Item instance so you will have both the returned property values and chaining ability in a single object. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/security\"; const item = await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.txt\").getItem(); console.log(item); const item2 = await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.txt\").getItem(\"Title\", \"Modified\"); console.log(item2); // you can also chain directly off this item instance const perms = await item.getCurrentUserEffectivePermissions(); console.log(perms); You can also supply a generic typing parameter and the resulting type will be a union type of Item and the generic type parameter. This allows you to have proper intellisense and type checking. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/items\"; import \"@pnp/sp/security\"; // also supports typing the objects so your type will be a union type const item = await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.txt\").getItem<{ Id: number, Title: string }>(\"Id\", \"Title\"); // You get intellisense and proper typing of the returned object console.log(`Id: ${item.Id} -- ${item.Title}`); // You can also chain directly off this item instance const perms = await item.getCurrentUserEffectivePermissions(); console.log(perms);","title":"getItem"},{"location":"sp/files/#move","text":"It's possible to move a file to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").moveTo(destinationUrl);","title":"move"},{"location":"sp/files/#copy","text":"It's possible to copy a file to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").copyTo(destinationUrl, false);","title":"copy"},{"location":"sp/files/#move-by-path","text":"It's possible to move a file to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev2/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").moveByPath(destinationUrl, false, true);","title":"move by path"},{"location":"sp/files/#copy-by-path","text":"It's possible to copy a file to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev2/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").copyByPath(destinationUrl, false, true);","title":"copy by path"},{"location":"sp/files/#getfilebyid","text":"You can get a file by Id from a web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import { IFile } from \"@pnp/sp/files\"; const file: IFile = sp.web.getFileById(\"2b281c7b-ece9-4b76-82f9-f5cf5e152ba0\");","title":"getFileById"},{"location":"sp/files/#delete","text":"Deletes a file import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; await sp.web.rootFolder.files.getByName(\"name.txt\").delete();","title":"delete"},{"location":"sp/files/#delete-with-params","text":"Added in 2.0.9 Deletes a file with options import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; await sp.web.rootFolder.files.getByName(\"name.txt\").deleteWithParams({ BypassSharedLock: true, });","title":"delete with params"},{"location":"sp/files/#exists","text":"Added in 2.0.9 Checks to see if a file exists import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; const exists = await sp.web.rootFolder.files.getByName(\"name.txt\").exists();","title":"exists"},{"location":"sp/folders/","text":"@pnp/sp/folders \u00b6 Folders serve as a container for your files and list items. IFolders \u00b6 Represents a collection of folders. SharePoint webs, lists, and list items have a collection of folders under their properties. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IFolders, Folders } from \"@pnp/sp/folders\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; Selective 5 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/folders/item\"; Preset: All import { sp, IFolders, Folders } from \"@pnp/sp/presets/all\"; Get folders collection for various SharePoint objects \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/lists\"; // gets web's folders const webFolders = await sp.web.folders(); // gets list's folders const listFolders = await sp.web.lists.getByTitle(\"My List\").rootFolder.folders(); // gets item's folders const itemFolders = await sp.web.lists.getByTitle(\"My List\").items.getById(1).folder.folders(); add \u00b6 Adds a new folder to collection of folders import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // creates a new folder for web with specified url const folderAddResult = await sp.web.folders.add(\"folder url\"); getByName \u00b6 Gets a folder instance from a collection by folder's name import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folder = await sp.web.folders.getByName(\"folder name\")(); IFolder \u00b6 Represents an instance of a SharePoint folder. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IFolders, Folders } from \"@pnp/sp/folders\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; Selective 5 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/folders/item\"; Preset: All import { sp, IFolders, Folders } from \"@pnp/sp/presets/all\"; Get a folder object associated with different SharePoint artifacts (web, list, list item) \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // web's folder const rootFolder = await sp.web.rootFolder(); // list's folder const listRootFolder = await sp.web.lists.getByTitle(\"234\").rootFolder(); // item's folder const itemFolder = await sp.web.lists.getByTitle(\"234\").items.getById(1).folder(); getItem \u00b6 Gets list item associated with a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folderItem = await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").getItem(); move \u00b6 It's possible to move a folder to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").moveTo(destinationUrl); copy \u00b6 It's possible to copy a folder to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").copyTo(destinationUrl); move by path \u00b6 It's possible to move a folder to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").moveByPath(destinationUrl, true); copy by path \u00b6 It's possible to copy a folder to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").copyByPath(destinationUrl, true); delete \u00b6 Deletes a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.rootFolder.folders.getByName(\"My Folder\").delete(); delete with params \u00b6 Added in 2.0.9 Deletes a folder with options import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.rootFolder.folders.getByName(\"My Folder\").deleteWithParams({ BypassSharedLock: true, DeleteIfEmpty: true, }); recycle \u00b6 Recycles a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.rootFolder.folders.getByName(\"My Folder\").recycle(); serverRelativeUrl \u00b6 Gets folder's server relative url import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const relUrl = await sp.web.rootFolder.folders.getByName(\"SiteAssets\").serverRelativeUrl(); update \u00b6 Updates folder's properties import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.getFolderByServerRelativePath(\"Shared Documents/Folder2\").update({ \"Name\": \"New name\", }); contentTypeOrder \u00b6 Gets content type order of a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const order = await sp.web.getFolderByServerRelativePath(\"Shared Documents\").contentTypeOrder(); folders \u00b6 Gets all child folders associated with the current folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folders = await sp.web.rootFolder.folders(); files \u00b6 Gets all files inside a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/files/folder\"; const files = await sp.web.getFolderByServerRelativePath(\"Shared Documents\").files(); listItemAllFields \u00b6 Gets this folder's list item field values import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const itemFields = await sp.web.getFolderByServerRelativePath(\"Shared Documents/My Folder\").listItemAllFields(); parentFolder \u00b6 Gets the parent folder, if available import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const parentFolder = await sp.web.getFolderByServerRelativePath(\"Shared Documents/My Folder\").parentFolder(); properties \u00b6 Gets this folder's properties import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const properties = await sp.web.getFolderByServerRelativePath(\"Shared Documents/Folder2\").properties(); uniqueContentTypeOrder \u00b6 Gets a value that specifies the content type order. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const contentTypeOrder = await sp.web.getFolderByServerRelativePath(\"Shared Documents/Folder2\").uniqueContentTypeOrder(); Rename a folder \u00b6 You can rename a folder by updating FileLeafRef property: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folder = sp.web.getFolderByServerRelativePath(\"Shared Documents/My Folder\"); const item = await folder.getItem(); const result = await item.update({ FileLeafRef: \"Folder2\" }); Create a folder with custom content type \u00b6 Below code creates a new folder under Document library and assigns custom folder content type to a newly created folder. Additionally it sets a field of a custom folder content type. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/lists\"; const newFolderResult = await sp.web.rootFolder.folders.getByName(\"Shared Documents\").folders.add(\"My New Folder\"); const item = await newFolderResult.folder.listItemAllFields(); await sp.web.lists.getByTitle(\"Documents\").items.getById(item.ID).update({ ContentTypeId: \"0x0120001E76ED75A3E3F3408811F0BF56C4CDDD\", MyFolderField: \"field value\", Title: \"My New Folder\", }); addSubFolderUsingPath \u00b6 Added in 2.0.9 You can use the addSubFolderUsingPath method to add a folder with some special chars supported import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import { IFolder } from \"@pnp/sp/folders\"; // add a folder to site assets const folder: IFolder = await web.rootFolder.folders.getByName(\"SiteAssets\").addSubFolderUsingPath(\"folder name\"); getFolderById \u00b6 You can get a folder by Id from a web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import { IFolder } from \"@pnp/sp/folders\"; const folder: IFolder = sp.web.getFolderById(\"2b281c7b-ece9-4b76-82f9-f5cf5e152ba0\"); getParentInfos \u00b6 Added in 2.0.12 Gets information about folder, including details about the parent list, parent list root folder, and parent web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folder: IFolder = sp.web.getFolderById(\"2b281c7b-ece9-4b76-82f9-f5cf5e152ba0\"); await folder.getParentInfos();","title":"Folders"},{"location":"sp/folders/#pnpspfolders","text":"Folders serve as a container for your files and list items.","title":"@pnp/sp/folders"},{"location":"sp/folders/#ifolders","text":"Represents a collection of folders. SharePoint webs, lists, and list items have a collection of folders under their properties. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IFolders, Folders } from \"@pnp/sp/folders\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; Selective 5 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/folders/item\"; Preset: All import { sp, IFolders, Folders } from \"@pnp/sp/presets/all\";","title":"IFolders"},{"location":"sp/folders/#get-folders-collection-for-various-sharepoint-objects","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/lists\"; // gets web's folders const webFolders = await sp.web.folders(); // gets list's folders const listFolders = await sp.web.lists.getByTitle(\"My List\").rootFolder.folders(); // gets item's folders const itemFolders = await sp.web.lists.getByTitle(\"My List\").items.getById(1).folder.folders();","title":"Get folders collection for various SharePoint objects"},{"location":"sp/folders/#add","text":"Adds a new folder to collection of folders import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // creates a new folder for web with specified url const folderAddResult = await sp.web.folders.add(\"folder url\");","title":"add"},{"location":"sp/folders/#getbyname","text":"Gets a folder instance from a collection by folder's name import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folder = await sp.web.folders.getByName(\"folder name\")();","title":"getByName"},{"location":"sp/folders/#ifolder","text":"Represents an instance of a SharePoint folder. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IFolders, Folders } from \"@pnp/sp/folders\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; Selective 5 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/folders/item\"; Preset: All import { sp, IFolders, Folders } from \"@pnp/sp/presets/all\";","title":"IFolder"},{"location":"sp/folders/#get-a-folder-object-associated-with-different-sharepoint-artifacts-web-list-list-item","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // web's folder const rootFolder = await sp.web.rootFolder(); // list's folder const listRootFolder = await sp.web.lists.getByTitle(\"234\").rootFolder(); // item's folder const itemFolder = await sp.web.lists.getByTitle(\"234\").items.getById(1).folder();","title":"Get a folder object associated with different SharePoint artifacts (web, list, list item)"},{"location":"sp/folders/#getitem","text":"Gets list item associated with a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folderItem = await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").getItem();","title":"getItem"},{"location":"sp/folders/#move","text":"It's possible to move a folder to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").moveTo(destinationUrl);","title":"move"},{"location":"sp/folders/#copy","text":"It's possible to copy a folder to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").copyTo(destinationUrl);","title":"copy"},{"location":"sp/folders/#move-by-path","text":"It's possible to move a folder to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").moveByPath(destinationUrl, true);","title":"move by path"},{"location":"sp/folders/#copy-by-path","text":"It's possible to copy a folder to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").copyByPath(destinationUrl, true);","title":"copy by path"},{"location":"sp/folders/#delete","text":"Deletes a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.rootFolder.folders.getByName(\"My Folder\").delete();","title":"delete"},{"location":"sp/folders/#delete-with-params","text":"Added in 2.0.9 Deletes a folder with options import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.rootFolder.folders.getByName(\"My Folder\").deleteWithParams({ BypassSharedLock: true, DeleteIfEmpty: true, });","title":"delete with params"},{"location":"sp/folders/#recycle","text":"Recycles a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.rootFolder.folders.getByName(\"My Folder\").recycle();","title":"recycle"},{"location":"sp/folders/#serverrelativeurl","text":"Gets folder's server relative url import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const relUrl = await sp.web.rootFolder.folders.getByName(\"SiteAssets\").serverRelativeUrl();","title":"serverRelativeUrl"},{"location":"sp/folders/#update","text":"Updates folder's properties import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.getFolderByServerRelativePath(\"Shared Documents/Folder2\").update({ \"Name\": \"New name\", });","title":"update"},{"location":"sp/folders/#contenttypeorder","text":"Gets content type order of a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const order = await sp.web.getFolderByServerRelativePath(\"Shared Documents\").contentTypeOrder();","title":"contentTypeOrder"},{"location":"sp/folders/#folders","text":"Gets all child folders associated with the current folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folders = await sp.web.rootFolder.folders();","title":"folders"},{"location":"sp/folders/#files","text":"Gets all files inside a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/files/folder\"; const files = await sp.web.getFolderByServerRelativePath(\"Shared Documents\").files();","title":"files"},{"location":"sp/folders/#listitemallfields","text":"Gets this folder's list item field values import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const itemFields = await sp.web.getFolderByServerRelativePath(\"Shared Documents/My Folder\").listItemAllFields();","title":"listItemAllFields"},{"location":"sp/folders/#parentfolder","text":"Gets the parent folder, if available import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const parentFolder = await sp.web.getFolderByServerRelativePath(\"Shared Documents/My Folder\").parentFolder();","title":"parentFolder"},{"location":"sp/folders/#properties","text":"Gets this folder's properties import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const properties = await sp.web.getFolderByServerRelativePath(\"Shared Documents/Folder2\").properties();","title":"properties"},{"location":"sp/folders/#uniquecontenttypeorder","text":"Gets a value that specifies the content type order. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const contentTypeOrder = await sp.web.getFolderByServerRelativePath(\"Shared Documents/Folder2\").uniqueContentTypeOrder();","title":"uniqueContentTypeOrder"},{"location":"sp/folders/#rename-a-folder","text":"You can rename a folder by updating FileLeafRef property: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folder = sp.web.getFolderByServerRelativePath(\"Shared Documents/My Folder\"); const item = await folder.getItem(); const result = await item.update({ FileLeafRef: \"Folder2\" });","title":"Rename a folder"},{"location":"sp/folders/#create-a-folder-with-custom-content-type","text":"Below code creates a new folder under Document library and assigns custom folder content type to a newly created folder. Additionally it sets a field of a custom folder content type. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/lists\"; const newFolderResult = await sp.web.rootFolder.folders.getByName(\"Shared Documents\").folders.add(\"My New Folder\"); const item = await newFolderResult.folder.listItemAllFields(); await sp.web.lists.getByTitle(\"Documents\").items.getById(item.ID).update({ ContentTypeId: \"0x0120001E76ED75A3E3F3408811F0BF56C4CDDD\", MyFolderField: \"field value\", Title: \"My New Folder\", });","title":"Create a folder with custom content type"},{"location":"sp/folders/#addsubfolderusingpath","text":"Added in 2.0.9 You can use the addSubFolderUsingPath method to add a folder with some special chars supported import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import { IFolder } from \"@pnp/sp/folders\"; // add a folder to site assets const folder: IFolder = await web.rootFolder.folders.getByName(\"SiteAssets\").addSubFolderUsingPath(\"folder name\");","title":"addSubFolderUsingPath"},{"location":"sp/folders/#getfolderbyid","text":"You can get a folder by Id from a web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import { IFolder } from \"@pnp/sp/folders\"; const folder: IFolder = sp.web.getFolderById(\"2b281c7b-ece9-4b76-82f9-f5cf5e152ba0\");","title":"getFolderById"},{"location":"sp/folders/#getparentinfos","text":"Added in 2.0.12 Gets information about folder, including details about the parent list, parent list root folder, and parent web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folder: IFolder = sp.web.getFolderById(\"2b281c7b-ece9-4b76-82f9-f5cf5e152ba0\"); await folder.getParentInfos();","title":"getParentInfos"},{"location":"sp/forms/","text":"@pnp/sp/forms \u00b6 Forms in SharePoint are the Display, New, and Edit forms associated with a list. IFields \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import \"@pnp/sp/forms\"; import \"@pnp/sp/lists\"; Get Form by Id \u00b6 Gets a form from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/forms\"; import \"@pnp/sp/lists\"; // get the field by Id for web const form = sp.web.lists.getByTitle(\"Documents\").forms.getById(\"{c4486774-f1e2-4804-96f3-91edf3e22a19}\")();","title":"Forms"},{"location":"sp/forms/#pnpspforms","text":"Forms in SharePoint are the Display, New, and Edit forms associated with a list.","title":"@pnp/sp/forms"},{"location":"sp/forms/#ifields","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import \"@pnp/sp/forms\"; import \"@pnp/sp/lists\";","title":"IFields"},{"location":"sp/forms/#get-form-by-id","text":"Gets a form from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/forms\"; import \"@pnp/sp/lists\"; // get the field by Id for web const form = sp.web.lists.getByTitle(\"Documents\").forms.getById(\"{c4486774-f1e2-4804-96f3-91edf3e22a19}\")();","title":"Get Form by Id"},{"location":"sp/hubsites/","text":"@pnp/sp/hubsites \u00b6 This module helps you with working with hub sites in your tenant. IHubSites \u00b6 Scenario Import Statement Selective import { sp } from \"@pnp/sp\"; import \"@pnp/sp/hubsites\"; Preset: All import { sp, HubSites, IHubSites } from \"@pnp/sp/presets/all\"; Get a Listing of All Hub sites \u00b6 import { sp } from \"@pnp/sp\"; import { IHubSiteInfo } from \"@pnp/sp/hubsites\"; import \"@pnp/sp/hubsites\"; // invoke the hub sites object const hubsites: IHubSiteInfo[] = await sp.hubSites(); // you can also use select to only return certain fields: const hubsites2: IHubSiteInfo[] = await sp.hubSites.select(\"ID\", \"Title\", \"RelatedHubSiteIds\")(); Get Hub site by Id \u00b6 Using the getById method on the hubsites module to get a hub site by site Id (guid). import { sp } from \"@pnp/sp\"; import { IHubSiteInfo } from \"@pnp/sp/hubsites\"; import \"@pnp/sp/hubsites\"; const hubsite: IHubSiteInfo = await sp.hubSites.getById(\"3504348e-b2be-49fb-a2a9-2d748db64beb\")(); // log hub site title to console console.log(hubsite.Title); Get ISite instance \u00b6 We provide a helper method to load the ISite instance from the HubSite import { sp } from \"@pnp/sp\"; import { ISite } from \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites\"; const site: ISite = await sp.hubSites.getById(\"3504348e-b2be-49fb-a2a9-2d748db64beb\").getSite(); const siteData = await site(); console.log(siteData.Title); Get Hub site data for a web \u00b6 import { sp } from \"@pnp/sp\"; import { IHubSiteWebData } from \"@pnp/sp/hubsites\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/hubsites/web\"; const webData: Partial = await sp.web.hubSiteData(); // you can also force a refresh of the hub site data const webData2: Partial = await sp.web.hubSiteData(true); syncHubSiteTheme \u00b6 Allows you to apply theme updates from the parent hub site collection. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/hubsites/web\"; await sp.web.syncHubSiteTheme(); Hub site Site Methods \u00b6 You manage hub sites at the Site level. joinHubSite \u00b6 Id of the hub site collection you want to join. If you want to disassociate the site collection from hub site, then pass the siteId as 00000000-0000-0000-0000-000000000000 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites/site\"; // join a site to a hub site await sp.site.joinHubSite(\"{parent hub site id}\"); // remove a site from a hub site await sp.site.joinHubSite(\"00000000-0000-0000-0000-000000000000\"); registerHubSite \u00b6 Registers the current site collection as hub site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites/site\"; // register current site as a hub site await sp.site.registerHubSite(); unRegisterHubSite \u00b6 Un-registers the current site collection as hub site collection. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites/site\"; // make a site no longer a hub await sp.site.unRegisterHubSite();","title":"Hubsites"},{"location":"sp/hubsites/#pnpsphubsites","text":"This module helps you with working with hub sites in your tenant.","title":"@pnp/sp/hubsites"},{"location":"sp/hubsites/#ihubsites","text":"Scenario Import Statement Selective import { sp } from \"@pnp/sp\"; import \"@pnp/sp/hubsites\"; Preset: All import { sp, HubSites, IHubSites } from \"@pnp/sp/presets/all\";","title":"IHubSites"},{"location":"sp/hubsites/#get-a-listing-of-all-hub-sites","text":"import { sp } from \"@pnp/sp\"; import { IHubSiteInfo } from \"@pnp/sp/hubsites\"; import \"@pnp/sp/hubsites\"; // invoke the hub sites object const hubsites: IHubSiteInfo[] = await sp.hubSites(); // you can also use select to only return certain fields: const hubsites2: IHubSiteInfo[] = await sp.hubSites.select(\"ID\", \"Title\", \"RelatedHubSiteIds\")();","title":"Get a Listing of All Hub sites"},{"location":"sp/hubsites/#get-hub-site-by-id","text":"Using the getById method on the hubsites module to get a hub site by site Id (guid). import { sp } from \"@pnp/sp\"; import { IHubSiteInfo } from \"@pnp/sp/hubsites\"; import \"@pnp/sp/hubsites\"; const hubsite: IHubSiteInfo = await sp.hubSites.getById(\"3504348e-b2be-49fb-a2a9-2d748db64beb\")(); // log hub site title to console console.log(hubsite.Title);","title":"Get Hub site by Id"},{"location":"sp/hubsites/#get-isite-instance","text":"We provide a helper method to load the ISite instance from the HubSite import { sp } from \"@pnp/sp\"; import { ISite } from \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites\"; const site: ISite = await sp.hubSites.getById(\"3504348e-b2be-49fb-a2a9-2d748db64beb\").getSite(); const siteData = await site(); console.log(siteData.Title);","title":"Get ISite instance"},{"location":"sp/hubsites/#get-hub-site-data-for-a-web","text":"import { sp } from \"@pnp/sp\"; import { IHubSiteWebData } from \"@pnp/sp/hubsites\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/hubsites/web\"; const webData: Partial = await sp.web.hubSiteData(); // you can also force a refresh of the hub site data const webData2: Partial = await sp.web.hubSiteData(true);","title":"Get Hub site data for a web"},{"location":"sp/hubsites/#synchubsitetheme","text":"Allows you to apply theme updates from the parent hub site collection. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/hubsites/web\"; await sp.web.syncHubSiteTheme();","title":"syncHubSiteTheme"},{"location":"sp/hubsites/#hub-site-site-methods","text":"You manage hub sites at the Site level.","title":"Hub site Site Methods"},{"location":"sp/hubsites/#joinhubsite","text":"Id of the hub site collection you want to join. If you want to disassociate the site collection from hub site, then pass the siteId as 00000000-0000-0000-0000-000000000000 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites/site\"; // join a site to a hub site await sp.site.joinHubSite(\"{parent hub site id}\"); // remove a site from a hub site await sp.site.joinHubSite(\"00000000-0000-0000-0000-000000000000\");","title":"joinHubSite"},{"location":"sp/hubsites/#registerhubsite","text":"Registers the current site collection as hub site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites/site\"; // register current site as a hub site await sp.site.registerHubSite();","title":"registerHubSite"},{"location":"sp/hubsites/#unregisterhubsite","text":"Un-registers the current site collection as hub site collection. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites/site\"; // make a site no longer a hub await sp.site.unRegisterHubSite();","title":"unRegisterHubSite"},{"location":"sp/items/","text":"@pnp/sp/items \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; Preset: Core import { sp } from \"@pnp/sp/presets/core\"; GET \u00b6 Getting items from a list is one of the basic actions that most applications require. This is made easy through the library and the following examples demonstrate these actions. Basic Get \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // get all the items from a list const items: any[] = await sp.web.lists.getByTitle(\"My List\").items(); console.log(items); // get a specific item by id. const item: any = await sp.web.lists.getByTitle(\"My List\").items.getById(1)(); console.log(item); // use odata operators for more efficient queries const items2: any[] = await sp.web.lists.getByTitle(\"My List\").items.select(\"Title\", \"Description\").top(5).orderBy(\"Modified\", true)(); console.log(items2); Get Paged Items \u00b6 Working with paging can be a challenge as it is based on skip tokens and item ids, something that is hard to guess at runtime. To simplify things you can use the getPaged method on the Items class to assist. Note that there isn't a way to move backwards in the collection, this is by design. The pattern you should use to support backwards navigation in the results is to cache the results into a local array and use the standard array operators to get previous pages. Alternatively you can append the results to the UI, but this can have performance impact for large result sets. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // basic case to get paged items form a list let items = await sp.web.lists.getByTitle(\"BigList\").items.getPaged(); // you can also provide a type for the returned values instead of any let items = await sp.web.lists.getByTitle(\"BigList\").items.getPaged<{Title: string}[]>(); // the query also works with select to choose certain fields and top to set the page size let items = await sp.web.lists.getByTitle(\"BigList\").items.select(\"Title\", \"Description\").top(50).getPaged<{Title: string}[]>(); // the results object will have two properties and one method: // the results property will be an array of the items returned if (items.results.length > 0) { console.log(\"We got results!\"); for (let i = 0; i < items.results.length; i++) { // type checking works here if we specify the return type console.log(items.results[i].Title); } } // the hasNext property is used with the getNext method to handle paging // hasNext will be true so long as there are additional results if (items.hasNext) { // this will carry over the type specified in the original query for the results array items = await items.getNext(); console.log(items.results.length); } getListItemChangesSinceToken \u00b6 The GetListItemChangesSinceToken method allows clients to track changes on a list. Changes, including deleted items, are returned along with a token that represents the moment in time when those changes were requested. By including this token when you call GetListItemChangesSinceToken, the server looks for only those changes that have occurred since the token was generated. Sending a GetListItemChangesSinceToken request without including a token returns the list schema, the full list contents and a token. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // Using RowLimit. Enables paging let changes = await sp.web.lists.getByTitle(\"BigList\").getListItemChangesSinceToken({RowLimit: '5'}); // Use QueryOptions to make a XML-style query. // Because it's XML we need to escape special characters // Instead of & we use & in the query let changes = await sp.web.lists.getByTitle(\"BigList\").getListItemChangesSinceToken({QueryOptions: ''}); // Get everything. Using null with ChangeToken gets everything let changes = await sp.web.lists.getByTitle(\"BigList\").getListItemChangesSinceToken({ChangeToken: null}); Get All Items \u00b6 Using the items collection's getAll method you can get all of the items in a list regardless of the size of the list. Sample usage is shown below. Only the odata operations top, select, and filter are supported. usingCaching and inBatch are ignored - you will need to handle caching the results on your own. This method will write a warning to the Logger and should not frequently be used. Instead the standard paging operations should be used. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // basic usage const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.getAll(); console.log(allItems.length); // set page size const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.getAll(4000); console.log(allItems.length); // use select and top. top will set page size and override the any value passed to getAll const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.select(\"Title\").top(4000).getAll(); console.log(allItems.length); // we can also use filter as a supported odata operation, but this will likely fail on large lists const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.select(\"Title\").filter(\"Title eq 'Test'\").getAll(); console.log(allItems.length); Retrieving Lookup Fields \u00b6 When working with lookup fields you need to use the expand operator along with select to get the related fields from the lookup column. This works for both the items collection and item instances. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; const items = await sp.web.lists.getByTitle(\"LookupList\").items.select(\"Title\", \"Lookup/Title\", \"Lookup/ID\").expand(\"Lookup\")(); console.log(items); const item = await sp.web.lists.getByTitle(\"LookupList\").items.getById(1).select(\"Title\", \"Lookup/Title\", \"Lookup/ID\").expand(\"Lookup\")(); console.log(item); Filter using Metadata fields \u00b6 To filter on a metadata field you must use the getItemsByCAMLQuery method as $filter does not support these fields. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; const r = await sp.web.lists.getByTitle(\"TaxonomyList\").getItemsByCAMLQuery({ ViewXml: `Term 2`, }); Retrieving PublishingPageImage \u00b6 The PublishingPageImage and some other publishing-related fields aren't stored in normal fields, rather in the MetaInfo field. To get these values you need to use the technique shown below, and originally outlined in this thread . Note that a lot of information can be stored in this field so will pull back potentially a significant amount of data, so limit the rows as possible to aid performance. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { Web } from \"@pnp/sp/webs\"; try { const w = Web(\"https://{publishing site url}\"); const r = await w.lists.getByTitle(\"Pages\").items .select(\"Title\", \"FileRef\", \"FieldValuesAsText/MetaInfo\") .expand(\"FieldValuesAsText\") (); // look through the returned items. for (var i = 0; i < r.length; i++) { // the title field value console.log(r[i].Title); // find the value in the MetaInfo string using regex const matches = /PublishingPageImage:SW\\|(.*?)\\r\\n/ig.exec(r[i].FieldValuesAsText.MetaInfo); if (matches !== null && matches.length > 1) { // this wil be the value of the PublishingPageImage field console.log(matches[1]); } } } catch (e) { console.error(e); } Add Items \u00b6 There are several ways to add items to a list. The simplest just uses the add method of the items collection passing in the properties as a plain object. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { IItemAddResult } from \"@pnp/sp/items\"; // add an item to the list const iar: IItemAddResult = await sp.web.lists.getByTitle(\"My List\").items.add({ Title: \"Title\", Description: \"Description\" }); console.log(iar); Content Type \u00b6 You can also set the content type id when you create an item as shown in the example below. For more information on content type IDs reference the Microsoft Documentation . While this documentation references SharePoint 2010 the structure of the IDs has not changed. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; await sp.web.lists.getById(\"4D5A36EA-6E84-4160-8458-65C436DB765C\").items.add({ Title: \"Test 1\", ContentTypeId: \"0x01030058FD86C279252341AB303852303E4DAF\" }); User Fields \u00b6 There are two types of user fields, those that allow a single value and those that allow multiple. For both types, you first need to determine the Id field name, which you can do by doing a GET REST request on an existing item. Typically the value will be the user field internal name with \"Id\" appended. So in our example, we have two fields User1 and User2 so the Id fields are User1Id and User2Id. Next, you need to remember there are two types of user fields, those that take a single value and those that allow multiple - these are updated in different ways. For single value user fields you supply just the user's id. For multiple value fields, you need to supply an object with a \"results\" property and an array. Examples for both are shown below. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { getGUID } from \"@pnp/core\"; const i = await sp.web.lists.getByTitle(\"PeopleFields\").items.add({ Title: getGUID(), User1Id: 9, // allows a single user User2Id: { results: [16, 45] // allows multiple users } }); console.log(i); If you want to update or add user field values when using validateUpdateListItem you need to use the form shown below. You can specify multiple values in the array. import { sp } from \"@pnp/sp\"; const result = await sp.web.lists.getByTitle(\"UserFieldList\").items.getById(1).validateUpdateListItem([{ FieldName: \"UserField\", FieldValue: JSON.stringify([{ \"Key\": \"i:0#.f|membership|person@tenant.com\" }]), }, { FieldName: \"Title\", FieldValue: \"Test - Updated\", }]); Lookup Fields \u00b6 What is said for User Fields is, in general, relevant to Lookup Fields: Lookup Field types: Single-valued lookup Multiple-valued lookup Id suffix should be appended to the end of lookups EntityPropertyName in payloads Numeric Ids for lookups' items should be passed as values import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { getGUID } from \"@pnp/core\"; await sp.web.lists.getByTitle(\"LookupFields\").items.add({ Title: getGUID(), LookupFieldId: 2, // allows a single lookup value MultiLookupFieldId: { results: [ 1, 56 ] // allows multiple lookup value } }); Add Multiple Items \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"rapidadd\"); const entityTypeFullName = await list.getListItemEntityTypeFullName() let batch = sp.web.createBatch(); list.items.inBatch(batch).add({ Title: \"Batch 6\" }, entityTypeFullName).then(b => { console.log(b); }); list.items.inBatch(batch).add({ Title: \"Batch 7\" }, entityTypeFullName).then(b => { console.log(b); }); await batch.execute(); console.log(\"Done\"); Update \u00b6 The update method is very similar to the add method in that it takes a plain object representing the fields to update. The property names are the internal names of the fields. If you aren't sure you can always do a get request for an item in the list and see the field names that come back - you would use these same names to update the item. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); const i = await list.items.getById(1).update({ Title: \"My New Title\", Description: \"Here is a new description\" }); console.log(i); Getting and updating a collection using filter \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // you are getting back a collection here const items: any[] = await sp.web.lists.getByTitle(\"MyList\").items.top(1).filter(\"Title eq 'A Title'\")(); // see if we got something if (items.length > 0) { const updatedItem = await sp.web.lists.getByTitle(\"MyList\").items.getById(items[0].Id).update({ Title: \"Updated Title\", }); console.log(JSON.stringify(updatedItem)); } Update Multiple Items \u00b6 This approach avoids multiple calls for the same list's entity type name. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"rapidupdate\"); const entityTypeFullName = await list.getListItemEntityTypeFullName() let batch = sp.web.createBatch(); // note requirement of \"*\" eTag param - or use a specific eTag value as needed list.items.getById(1).inBatch(batch).update({ Title: \"Batch 6\" }, \"*\", entityTypeFullName).then(b => { console.log(b); }); list.items.getById(2).inBatch(batch).update({ Title: \"Batch 7\" }, \"*\", entityTypeFullName).then(b => { console.log(b); }); await batch.execute(); console.log(\"Done\") Recycle \u00b6 To send an item to the recycle bin use recycle. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); const recycleBinIdentifier = await list.items.getById(1).recycle(); Delete \u00b6 Delete is as simple as calling the .delete method. It optionally takes an eTag if you need to manage concurrency. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(1).delete(); Delete With Params \u00b6 Added in 2.0.9 Deletes the item object with options. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(1).deleteWithParams({ BypassSharedLock: true, }); The deleteWithParams method can only be used by accounts where UserToken.IsSystemAccount is true Resolving field names \u00b6 It's a very common mistake trying wrong field names in the requests. Field's EntityPropertyName value should be used. The easiest way to get know EntityPropertyName is to use the following snippet: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import \"@pnp/sp/fields\"; const response = await sp.web.lists .getByTitle('[Lists_Title]') .fields .select('Title, EntityPropertyName') .filter(`Hidden eq false and Title eq '[Field's_Display_Name]'`) (); console.log(response.map(field => { return { Title: field.Title, EntityPropertyName: field.EntityPropertyName }; })); Lookup fields' names should be ended with additional Id suffix. E.g. for Editor EntityPropertyName EditorId should be used. getParentInfos \u00b6 Added in 2.0.12 Gets information about an item, including details about the parent list, parent list root folder, and parent web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; const item: any = await sp.web.lists.getByTitle(\"My List\").items.getById(1)(); await item.getParentInfos();","title":"List Items"},{"location":"sp/items/#pnpspitems","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; Preset: Core import { sp } from \"@pnp/sp/presets/core\";","title":"@pnp/sp/items"},{"location":"sp/items/#get","text":"Getting items from a list is one of the basic actions that most applications require. This is made easy through the library and the following examples demonstrate these actions.","title":"GET"},{"location":"sp/items/#basic-get","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // get all the items from a list const items: any[] = await sp.web.lists.getByTitle(\"My List\").items(); console.log(items); // get a specific item by id. const item: any = await sp.web.lists.getByTitle(\"My List\").items.getById(1)(); console.log(item); // use odata operators for more efficient queries const items2: any[] = await sp.web.lists.getByTitle(\"My List\").items.select(\"Title\", \"Description\").top(5).orderBy(\"Modified\", true)(); console.log(items2);","title":"Basic Get"},{"location":"sp/items/#get-paged-items","text":"Working with paging can be a challenge as it is based on skip tokens and item ids, something that is hard to guess at runtime. To simplify things you can use the getPaged method on the Items class to assist. Note that there isn't a way to move backwards in the collection, this is by design. The pattern you should use to support backwards navigation in the results is to cache the results into a local array and use the standard array operators to get previous pages. Alternatively you can append the results to the UI, but this can have performance impact for large result sets. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // basic case to get paged items form a list let items = await sp.web.lists.getByTitle(\"BigList\").items.getPaged(); // you can also provide a type for the returned values instead of any let items = await sp.web.lists.getByTitle(\"BigList\").items.getPaged<{Title: string}[]>(); // the query also works with select to choose certain fields and top to set the page size let items = await sp.web.lists.getByTitle(\"BigList\").items.select(\"Title\", \"Description\").top(50).getPaged<{Title: string}[]>(); // the results object will have two properties and one method: // the results property will be an array of the items returned if (items.results.length > 0) { console.log(\"We got results!\"); for (let i = 0; i < items.results.length; i++) { // type checking works here if we specify the return type console.log(items.results[i].Title); } } // the hasNext property is used with the getNext method to handle paging // hasNext will be true so long as there are additional results if (items.hasNext) { // this will carry over the type specified in the original query for the results array items = await items.getNext(); console.log(items.results.length); }","title":"Get Paged Items"},{"location":"sp/items/#getlistitemchangessincetoken","text":"The GetListItemChangesSinceToken method allows clients to track changes on a list. Changes, including deleted items, are returned along with a token that represents the moment in time when those changes were requested. By including this token when you call GetListItemChangesSinceToken, the server looks for only those changes that have occurred since the token was generated. Sending a GetListItemChangesSinceToken request without including a token returns the list schema, the full list contents and a token. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // Using RowLimit. Enables paging let changes = await sp.web.lists.getByTitle(\"BigList\").getListItemChangesSinceToken({RowLimit: '5'}); // Use QueryOptions to make a XML-style query. // Because it's XML we need to escape special characters // Instead of & we use & in the query let changes = await sp.web.lists.getByTitle(\"BigList\").getListItemChangesSinceToken({QueryOptions: ''}); // Get everything. Using null with ChangeToken gets everything let changes = await sp.web.lists.getByTitle(\"BigList\").getListItemChangesSinceToken({ChangeToken: null});","title":"getListItemChangesSinceToken"},{"location":"sp/items/#get-all-items","text":"Using the items collection's getAll method you can get all of the items in a list regardless of the size of the list. Sample usage is shown below. Only the odata operations top, select, and filter are supported. usingCaching and inBatch are ignored - you will need to handle caching the results on your own. This method will write a warning to the Logger and should not frequently be used. Instead the standard paging operations should be used. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // basic usage const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.getAll(); console.log(allItems.length); // set page size const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.getAll(4000); console.log(allItems.length); // use select and top. top will set page size and override the any value passed to getAll const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.select(\"Title\").top(4000).getAll(); console.log(allItems.length); // we can also use filter as a supported odata operation, but this will likely fail on large lists const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.select(\"Title\").filter(\"Title eq 'Test'\").getAll(); console.log(allItems.length);","title":"Get All Items"},{"location":"sp/items/#retrieving-lookup-fields","text":"When working with lookup fields you need to use the expand operator along with select to get the related fields from the lookup column. This works for both the items collection and item instances. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; const items = await sp.web.lists.getByTitle(\"LookupList\").items.select(\"Title\", \"Lookup/Title\", \"Lookup/ID\").expand(\"Lookup\")(); console.log(items); const item = await sp.web.lists.getByTitle(\"LookupList\").items.getById(1).select(\"Title\", \"Lookup/Title\", \"Lookup/ID\").expand(\"Lookup\")(); console.log(item);","title":"Retrieving Lookup Fields"},{"location":"sp/items/#filter-using-metadata-fields","text":"To filter on a metadata field you must use the getItemsByCAMLQuery method as $filter does not support these fields. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; const r = await sp.web.lists.getByTitle(\"TaxonomyList\").getItemsByCAMLQuery({ ViewXml: `Term 2`, });","title":"Filter using Metadata fields"},{"location":"sp/items/#retrieving-publishingpageimage","text":"The PublishingPageImage and some other publishing-related fields aren't stored in normal fields, rather in the MetaInfo field. To get these values you need to use the technique shown below, and originally outlined in this thread . Note that a lot of information can be stored in this field so will pull back potentially a significant amount of data, so limit the rows as possible to aid performance. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { Web } from \"@pnp/sp/webs\"; try { const w = Web(\"https://{publishing site url}\"); const r = await w.lists.getByTitle(\"Pages\").items .select(\"Title\", \"FileRef\", \"FieldValuesAsText/MetaInfo\") .expand(\"FieldValuesAsText\") (); // look through the returned items. for (var i = 0; i < r.length; i++) { // the title field value console.log(r[i].Title); // find the value in the MetaInfo string using regex const matches = /PublishingPageImage:SW\\|(.*?)\\r\\n/ig.exec(r[i].FieldValuesAsText.MetaInfo); if (matches !== null && matches.length > 1) { // this wil be the value of the PublishingPageImage field console.log(matches[1]); } } } catch (e) { console.error(e); }","title":"Retrieving PublishingPageImage"},{"location":"sp/items/#add-items","text":"There are several ways to add items to a list. The simplest just uses the add method of the items collection passing in the properties as a plain object. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { IItemAddResult } from \"@pnp/sp/items\"; // add an item to the list const iar: IItemAddResult = await sp.web.lists.getByTitle(\"My List\").items.add({ Title: \"Title\", Description: \"Description\" }); console.log(iar);","title":"Add Items"},{"location":"sp/items/#content-type","text":"You can also set the content type id when you create an item as shown in the example below. For more information on content type IDs reference the Microsoft Documentation . While this documentation references SharePoint 2010 the structure of the IDs has not changed. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; await sp.web.lists.getById(\"4D5A36EA-6E84-4160-8458-65C436DB765C\").items.add({ Title: \"Test 1\", ContentTypeId: \"0x01030058FD86C279252341AB303852303E4DAF\" });","title":"Content Type"},{"location":"sp/items/#user-fields","text":"There are two types of user fields, those that allow a single value and those that allow multiple. For both types, you first need to determine the Id field name, which you can do by doing a GET REST request on an existing item. Typically the value will be the user field internal name with \"Id\" appended. So in our example, we have two fields User1 and User2 so the Id fields are User1Id and User2Id. Next, you need to remember there are two types of user fields, those that take a single value and those that allow multiple - these are updated in different ways. For single value user fields you supply just the user's id. For multiple value fields, you need to supply an object with a \"results\" property and an array. Examples for both are shown below. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { getGUID } from \"@pnp/core\"; const i = await sp.web.lists.getByTitle(\"PeopleFields\").items.add({ Title: getGUID(), User1Id: 9, // allows a single user User2Id: { results: [16, 45] // allows multiple users } }); console.log(i); If you want to update or add user field values when using validateUpdateListItem you need to use the form shown below. You can specify multiple values in the array. import { sp } from \"@pnp/sp\"; const result = await sp.web.lists.getByTitle(\"UserFieldList\").items.getById(1).validateUpdateListItem([{ FieldName: \"UserField\", FieldValue: JSON.stringify([{ \"Key\": \"i:0#.f|membership|person@tenant.com\" }]), }, { FieldName: \"Title\", FieldValue: \"Test - Updated\", }]);","title":"User Fields"},{"location":"sp/items/#lookup-fields","text":"What is said for User Fields is, in general, relevant to Lookup Fields: Lookup Field types: Single-valued lookup Multiple-valued lookup Id suffix should be appended to the end of lookups EntityPropertyName in payloads Numeric Ids for lookups' items should be passed as values import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { getGUID } from \"@pnp/core\"; await sp.web.lists.getByTitle(\"LookupFields\").items.add({ Title: getGUID(), LookupFieldId: 2, // allows a single lookup value MultiLookupFieldId: { results: [ 1, 56 ] // allows multiple lookup value } });","title":"Lookup Fields"},{"location":"sp/items/#add-multiple-items","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"rapidadd\"); const entityTypeFullName = await list.getListItemEntityTypeFullName() let batch = sp.web.createBatch(); list.items.inBatch(batch).add({ Title: \"Batch 6\" }, entityTypeFullName).then(b => { console.log(b); }); list.items.inBatch(batch).add({ Title: \"Batch 7\" }, entityTypeFullName).then(b => { console.log(b); }); await batch.execute(); console.log(\"Done\");","title":"Add Multiple Items"},{"location":"sp/items/#update","text":"The update method is very similar to the add method in that it takes a plain object representing the fields to update. The property names are the internal names of the fields. If you aren't sure you can always do a get request for an item in the list and see the field names that come back - you would use these same names to update the item. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); const i = await list.items.getById(1).update({ Title: \"My New Title\", Description: \"Here is a new description\" }); console.log(i);","title":"Update"},{"location":"sp/items/#getting-and-updating-a-collection-using-filter","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // you are getting back a collection here const items: any[] = await sp.web.lists.getByTitle(\"MyList\").items.top(1).filter(\"Title eq 'A Title'\")(); // see if we got something if (items.length > 0) { const updatedItem = await sp.web.lists.getByTitle(\"MyList\").items.getById(items[0].Id).update({ Title: \"Updated Title\", }); console.log(JSON.stringify(updatedItem)); }","title":"Getting and updating a collection using filter"},{"location":"sp/items/#update-multiple-items","text":"This approach avoids multiple calls for the same list's entity type name. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"rapidupdate\"); const entityTypeFullName = await list.getListItemEntityTypeFullName() let batch = sp.web.createBatch(); // note requirement of \"*\" eTag param - or use a specific eTag value as needed list.items.getById(1).inBatch(batch).update({ Title: \"Batch 6\" }, \"*\", entityTypeFullName).then(b => { console.log(b); }); list.items.getById(2).inBatch(batch).update({ Title: \"Batch 7\" }, \"*\", entityTypeFullName).then(b => { console.log(b); }); await batch.execute(); console.log(\"Done\")","title":"Update Multiple Items"},{"location":"sp/items/#recycle","text":"To send an item to the recycle bin use recycle. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); const recycleBinIdentifier = await list.items.getById(1).recycle();","title":"Recycle"},{"location":"sp/items/#delete","text":"Delete is as simple as calling the .delete method. It optionally takes an eTag if you need to manage concurrency. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(1).delete();","title":"Delete"},{"location":"sp/items/#delete-with-params","text":"Added in 2.0.9 Deletes the item object with options. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(1).deleteWithParams({ BypassSharedLock: true, }); The deleteWithParams method can only be used by accounts where UserToken.IsSystemAccount is true","title":"Delete With Params"},{"location":"sp/items/#resolving-field-names","text":"It's a very common mistake trying wrong field names in the requests. Field's EntityPropertyName value should be used. The easiest way to get know EntityPropertyName is to use the following snippet: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import \"@pnp/sp/fields\"; const response = await sp.web.lists .getByTitle('[Lists_Title]') .fields .select('Title, EntityPropertyName') .filter(`Hidden eq false and Title eq '[Field's_Display_Name]'`) (); console.log(response.map(field => { return { Title: field.Title, EntityPropertyName: field.EntityPropertyName }; })); Lookup fields' names should be ended with additional Id suffix. E.g. for Editor EntityPropertyName EditorId should be used.","title":"Resolving field names"},{"location":"sp/items/#getparentinfos","text":"Added in 2.0.12 Gets information about an item, including details about the parent list, parent list root folder, and parent web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; const item: any = await sp.web.lists.getByTitle(\"My List\").items.getById(1)(); await item.getParentInfos();","title":"getParentInfos"},{"location":"sp/lists/","text":"@pnp/sp/lists \u00b6 Lists in SharePoint are collections of information built in a structural way using columns and rows. Columns for metadata, and rows representing each entry. Visually, it reminds us a lot of a database table or an Excel spreadsheet. ILists \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import { Lists, ILists } from \"@pnp/sp/lists\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; Preset: All import { sp, Lists, ILists } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Lists, ILists } from \"@pnp/sp/presets/core\"; Get List by Id \u00b6 Gets a list from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // get the list by Id const list = sp.web.lists.getById(\"03b05ff4-d95d-45ed-841d-3855f77a2483\"); // we can use this 'list' variable to execute more queries on the list: const r = await list.select(\"Title\")(); // show the response from the server console.log(r.Title); Get List by Title \u00b6 You can also get a list from the collection by title. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // get the default document library 'Documents' const list = sp.web.lists.getByTitle(\"Documents\"); // we can use this 'list' variable to run more queries on the list: const r = await list.select(\"Id\")(); // log the list Id to console console.log(r.Id); Add List \u00b6 You can add a list to the web's list collection using the .add-method. To invoke this method in its most simple form, you can provide only a title as a parameter. This will result in a standard out of the box list with all default settings, and the title you provide. // create a new list, passing only the title const listAddResult = await sp.web.lists.add(\"My new list\"); // we can work with the list created using the IListAddResult.list property: const r = await listAddResult.list.select(\"Title\")(); // log newly created list title to console console.log(r.Title); }); You can also provide other (optional) parameters like description, template and enableContentTypes. If that is not enough for you, you can use the parameter named 'additionalSettings' which is just a TypedHash, meaning you can sent whatever properties you'd like in the body (provided that the property is supported by the SharePoint API). You can find a listing of list template codes in the official docs. // this will create a list with template 101 (Document library), content types enabled and show it on the quick launch (using additionalSettings) const listAddResult = await sp.web.lists.add(\"My Doc Library\", \"This is a description of doc lib.\", 101, true, { OnQuickLaunch: true }); // get the Id of the newly added document library const r = await listAddResult.list.select(\"Id\")(); // log id to console console.log(r.Id); Ensure that a List exists (by title) \u00b6 Ensures that the specified list exists in the collection (note: this method not supported for batching). Just like with the add-method (see examples above) you can provide only the title, or any or all of the optional parameters desc, template, enableContentTypes and additionalSettings. // ensure that a list exists. If it doesn't it will be created with the provided title (the rest of the settings will be default): const listEnsureResult = await sp.web.lists.ensure(\"My List\"); // check if the list was created, or if it already existed: if (listEnsureResult.created) { console.log(\"My List was created!\"); } else { console.log(\"My List already existed!\"); } // work on the created/updated list const r = await listEnsureResult.list.select(\"Id\")(); // log the Id console.log(r.Id); If the list already exists, the other settings you provide will be used to update the existing list. // add a new list to the lists collection of the web sp.web.lists.add(\"My List 2\").then(async () => { // then call ensure on the created list with an updated description const listEnsureResult = await sp.web.lists.ensure(\"My List 2\", \"Updated description\"); // get the updated description const r = await listEnsureResult.list.select(\"Description\")(); // log the updated description console.log(r.Description); }); Ensure Site Assets Library exist \u00b6 Gets a list that is the default asset location for images or other files, which the users upload to their wiki pages. // get Site Assets library const siteAssetsList = await sp.web.lists.ensureSiteAssetsLibrary(); // get the Title const r = await siteAssetsList.select(\"Title\")(); // log Title console.log(r.Title); Ensure Site Pages Library exist \u00b6 Gets a list that is the default location for wiki pages. // get Site Pages library const siteAssetsList = await sp.web.lists.ensureSitePagesLibrary(); // get the Title const r = await siteAssetsList.select(\"Title\")(); // log Title console.log(r.Title); IList \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { List, IList } from \"@pnp/sp/lists\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/lists\"; Preset: All import { sp, List, IList } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, List, IList } from \"@pnp/sp/presets/core\"; Update a list \u00b6 Update an existing list with the provided properties. You can also provide an eTag value that will be used in the IF-Match header (default is \"*\") import { IListUpdateResult } from \"@pnp/sp/lists\"; // create a TypedHash object with the properties to update const updateProperties = { Description: \"This list title and description has been updated using PnPjs.\", Title: \"Updated title\", }; // update the list with the properties above list.update(updateProperties).then(async (l: IListUpdateResult) => { // get the updated title and description const r = await l.list.select(\"Title\", \"Description\")(); // log the updated properties to the console console.log(r.Title); console.log(r.Description); }); Get changes on a list \u00b6 From the change log, you can get a collection of changes that have occurred within the list based on the specified query. import { sp, IChangeQuery } from \"@pnp/sp\"; // build the changeQuery object, here we look att changes regarding Add, DeleteObject and Restore const changeQuery: IChangeQuery = { Add: true, ChangeTokenEnd: null, ChangeTokenStart: null, DeleteObject: true, Rename: true, Restore: true, }; // get list changes const r = await list.getChanges(changeQuery); // log changes to console console.log(r); Get list items using a CAML Query \u00b6 You can get items from SharePoint using a CAML Query. import { ICamlQuery } from \"@pnp/sp/lists\"; // build the caml query object (in this example, we include Title field and limit rows to 5) const caml: ICamlQuery = { ViewXml: \"5\", }; // get list items const r = await list.getItemsByCAMLQuery(caml); // log resulting array to console console.log(r); If you need to get and expand a lookup field, there is a spread array parameter on the getItemsByCAMLQuery. This means that you can provide multiple properties to this method depending on how many lookup fields you are working with on your list. Below is a minimal example showing how to expand one field (RoleAssignment) import { ICamlQuery } from \"@pnp/sp/lists\"; // build the caml query object (in this example, we include Title field and limit rows to 5) const caml: ICamlQuery = { ViewXml: \"5\", }; // get list items const r = await list.getItemsByCAMLQuery(caml, \"RoleAssignments\"); // log resulting item array to console console.log(r); Get list items changes using a Token \u00b6 import { IChangeLogItemQuery } from \"@pnp/sp/lists\"; // build the caml query object (in this example, we include Title field and limit rows to 5) const changeLogItemQuery: IChangeLogItemQuery = { Contains: `Item16`, QueryOptions: ` FALSE False TRUE FALSE My List`, }; // get list items const r = await list.getListItemChangesSinceToken(changeLogItemQuery); // log resulting XML to console console.log(r); Recycle a list \u00b6 Removes the list from the web's list collection and puts it in the recycle bin. await list.recycle(); Render list data \u00b6 import { IRenderListData } from \"@pnp/sp/lists\"; // render list data, top 5 items const r: IRenderListData = await list.renderListData(\"5\"); // log array of items in response console.log(r.Row); Render list data as stream \u00b6 import { IRenderListDataParameters } from \"@pnp/sp/lists\"; // setup parameters object const renderListDataParams: IRenderListDataParameters = { ViewXml: \"5\", }; // render list data as stream const r = await list.renderListDataAsStream(renderListDataParams); // log array of items in response console.log(r.Row); Reserve list item Id for idempotent list item creation \u00b6 const listItemId = await list.reserveListItemId(); // log id to console console.log(listItemId); Get list item entity type name \u00b6 const entityTypeFullName = await list.getListItemEntityTypeFullName(); // log entity type name console.log(entityTypeFullName); Add a list item using path (folder), validation and set field values \u00b6 const list = await sp.webs.lists.getByTitle(\"MyList\").select(\"Title\", \"ParentWebUrl\")(); const formValues: IListItemFormUpdateValue[] = [ { FieldName: \"Title\", FieldValue: title, }, ]; list.addValidateUpdateItemUsingPath(formValues,`${list.ParentWebUrl}/Lists/${list.Title}/MyFolder`) content-types imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/content-types\"; Selective 2 import \"@pnp/sp/content-types/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; contentTypes \u00b6 Get all content types for a list const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.contentTypes(); fields imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/fields\"; Selective 2 import \"@pnp/sp/fields/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; fields \u00b6 Get all the fields for a list const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.fields(); Add a field to the site, then add the site field to a list const fld = await sp.site.rootWeb.fields.addText(\"MyField\"); await sp.web.lists.getByTitle(\"MyList\").fields.createFieldAsXml(fld.data.SchemaXml); folders imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/folders\"; Selective 2 import \"@pnp/sp/folders/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; folders \u00b6 Get the root folder of a list. const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.rootFolder(); forms imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/forms\"; Selective 2 import \"@pnp/sp/forms/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; forms \u00b6 const r = await list.forms(); items imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/items\"; Selective 2 import \"@pnp/sp/items/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; items \u00b6 Get a collection of list items. const r = await list.items(); views imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/views\"; Selective 2 import \"@pnp/sp/views/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; views \u00b6 Get the default view of the list const list = sp.web.lists.getByTitle(\"Documents\"); const views = await list.views(); const defaultView = await list.defaultView(); Get a list view by Id const view = await list.getView(defaultView.Id).select(\"Title\")(); security imports \u00b6 To work with list security, you can import the list methods as follows: import \"@pnp/sp/security/list\"; For more information on how to call security methods for lists, please refer to the @pnp/sp/security documentation. subscriptions imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/subscriptions\"; Selective 2 import \"@pnp/sp/subscriptions/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; subscriptions \u00b6 Get all subscriptions on the list const list = sp.web.lists.getByTitle(\"Documents\"); const subscriptions = await list.subscriptions(); user-custom-actions imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/user-custom-actions\"; Selective 2 import \"@pnp/sp/user-custom-actions/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; userCustomActions \u00b6 Get a collection of the list's user custom actions. const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.userCustomActions(); getParentInfos \u00b6 Added in 2.0.12 Gets information about an list, including details about the parent list root folder, and parent web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; const list = sp.web.lists.getByTitle(\"Documents\"); await list.getParentInfos();","title":"Lists"},{"location":"sp/lists/#pnpsplists","text":"Lists in SharePoint are collections of information built in a structural way using columns and rows. Columns for metadata, and rows representing each entry. Visually, it reminds us a lot of a database table or an Excel spreadsheet.","title":"@pnp/sp/lists"},{"location":"sp/lists/#ilists","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import { Lists, ILists } from \"@pnp/sp/lists\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; Preset: All import { sp, Lists, ILists } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Lists, ILists } from \"@pnp/sp/presets/core\";","title":"ILists"},{"location":"sp/lists/#get-list-by-id","text":"Gets a list from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // get the list by Id const list = sp.web.lists.getById(\"03b05ff4-d95d-45ed-841d-3855f77a2483\"); // we can use this 'list' variable to execute more queries on the list: const r = await list.select(\"Title\")(); // show the response from the server console.log(r.Title);","title":"Get List by Id"},{"location":"sp/lists/#get-list-by-title","text":"You can also get a list from the collection by title. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // get the default document library 'Documents' const list = sp.web.lists.getByTitle(\"Documents\"); // we can use this 'list' variable to run more queries on the list: const r = await list.select(\"Id\")(); // log the list Id to console console.log(r.Id);","title":"Get List by Title"},{"location":"sp/lists/#add-list","text":"You can add a list to the web's list collection using the .add-method. To invoke this method in its most simple form, you can provide only a title as a parameter. This will result in a standard out of the box list with all default settings, and the title you provide. // create a new list, passing only the title const listAddResult = await sp.web.lists.add(\"My new list\"); // we can work with the list created using the IListAddResult.list property: const r = await listAddResult.list.select(\"Title\")(); // log newly created list title to console console.log(r.Title); }); You can also provide other (optional) parameters like description, template and enableContentTypes. If that is not enough for you, you can use the parameter named 'additionalSettings' which is just a TypedHash, meaning you can sent whatever properties you'd like in the body (provided that the property is supported by the SharePoint API). You can find a listing of list template codes in the official docs. // this will create a list with template 101 (Document library), content types enabled and show it on the quick launch (using additionalSettings) const listAddResult = await sp.web.lists.add(\"My Doc Library\", \"This is a description of doc lib.\", 101, true, { OnQuickLaunch: true }); // get the Id of the newly added document library const r = await listAddResult.list.select(\"Id\")(); // log id to console console.log(r.Id);","title":"Add List"},{"location":"sp/lists/#ensure-that-a-list-exists-by-title","text":"Ensures that the specified list exists in the collection (note: this method not supported for batching). Just like with the add-method (see examples above) you can provide only the title, or any or all of the optional parameters desc, template, enableContentTypes and additionalSettings. // ensure that a list exists. If it doesn't it will be created with the provided title (the rest of the settings will be default): const listEnsureResult = await sp.web.lists.ensure(\"My List\"); // check if the list was created, or if it already existed: if (listEnsureResult.created) { console.log(\"My List was created!\"); } else { console.log(\"My List already existed!\"); } // work on the created/updated list const r = await listEnsureResult.list.select(\"Id\")(); // log the Id console.log(r.Id); If the list already exists, the other settings you provide will be used to update the existing list. // add a new list to the lists collection of the web sp.web.lists.add(\"My List 2\").then(async () => { // then call ensure on the created list with an updated description const listEnsureResult = await sp.web.lists.ensure(\"My List 2\", \"Updated description\"); // get the updated description const r = await listEnsureResult.list.select(\"Description\")(); // log the updated description console.log(r.Description); });","title":"Ensure that a List exists (by title)"},{"location":"sp/lists/#ensure-site-assets-library-exist","text":"Gets a list that is the default asset location for images or other files, which the users upload to their wiki pages. // get Site Assets library const siteAssetsList = await sp.web.lists.ensureSiteAssetsLibrary(); // get the Title const r = await siteAssetsList.select(\"Title\")(); // log Title console.log(r.Title);","title":"Ensure Site Assets Library exist"},{"location":"sp/lists/#ensure-site-pages-library-exist","text":"Gets a list that is the default location for wiki pages. // get Site Pages library const siteAssetsList = await sp.web.lists.ensureSitePagesLibrary(); // get the Title const r = await siteAssetsList.select(\"Title\")(); // log Title console.log(r.Title);","title":"Ensure Site Pages Library exist"},{"location":"sp/lists/#ilist","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { List, IList } from \"@pnp/sp/lists\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/lists\"; Preset: All import { sp, List, IList } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, List, IList } from \"@pnp/sp/presets/core\";","title":"IList"},{"location":"sp/lists/#update-a-list","text":"Update an existing list with the provided properties. You can also provide an eTag value that will be used in the IF-Match header (default is \"*\") import { IListUpdateResult } from \"@pnp/sp/lists\"; // create a TypedHash object with the properties to update const updateProperties = { Description: \"This list title and description has been updated using PnPjs.\", Title: \"Updated title\", }; // update the list with the properties above list.update(updateProperties).then(async (l: IListUpdateResult) => { // get the updated title and description const r = await l.list.select(\"Title\", \"Description\")(); // log the updated properties to the console console.log(r.Title); console.log(r.Description); });","title":"Update a list"},{"location":"sp/lists/#get-changes-on-a-list","text":"From the change log, you can get a collection of changes that have occurred within the list based on the specified query. import { sp, IChangeQuery } from \"@pnp/sp\"; // build the changeQuery object, here we look att changes regarding Add, DeleteObject and Restore const changeQuery: IChangeQuery = { Add: true, ChangeTokenEnd: null, ChangeTokenStart: null, DeleteObject: true, Rename: true, Restore: true, }; // get list changes const r = await list.getChanges(changeQuery); // log changes to console console.log(r);","title":"Get changes on a list"},{"location":"sp/lists/#get-list-items-using-a-caml-query","text":"You can get items from SharePoint using a CAML Query. import { ICamlQuery } from \"@pnp/sp/lists\"; // build the caml query object (in this example, we include Title field and limit rows to 5) const caml: ICamlQuery = { ViewXml: \"5\", }; // get list items const r = await list.getItemsByCAMLQuery(caml); // log resulting array to console console.log(r); If you need to get and expand a lookup field, there is a spread array parameter on the getItemsByCAMLQuery. This means that you can provide multiple properties to this method depending on how many lookup fields you are working with on your list. Below is a minimal example showing how to expand one field (RoleAssignment) import { ICamlQuery } from \"@pnp/sp/lists\"; // build the caml query object (in this example, we include Title field and limit rows to 5) const caml: ICamlQuery = { ViewXml: \"5\", }; // get list items const r = await list.getItemsByCAMLQuery(caml, \"RoleAssignments\"); // log resulting item array to console console.log(r);","title":"Get list items using a CAML Query"},{"location":"sp/lists/#get-list-items-changes-using-a-token","text":"import { IChangeLogItemQuery } from \"@pnp/sp/lists\"; // build the caml query object (in this example, we include Title field and limit rows to 5) const changeLogItemQuery: IChangeLogItemQuery = { Contains: `Item16`, QueryOptions: ` FALSE False TRUE FALSE My List`, }; // get list items const r = await list.getListItemChangesSinceToken(changeLogItemQuery); // log resulting XML to console console.log(r);","title":"Get list items changes using a Token"},{"location":"sp/lists/#recycle-a-list","text":"Removes the list from the web's list collection and puts it in the recycle bin. await list.recycle();","title":"Recycle a list"},{"location":"sp/lists/#render-list-data","text":"import { IRenderListData } from \"@pnp/sp/lists\"; // render list data, top 5 items const r: IRenderListData = await list.renderListData(\"5\"); // log array of items in response console.log(r.Row);","title":"Render list data"},{"location":"sp/lists/#render-list-data-as-stream","text":"import { IRenderListDataParameters } from \"@pnp/sp/lists\"; // setup parameters object const renderListDataParams: IRenderListDataParameters = { ViewXml: \"5\", }; // render list data as stream const r = await list.renderListDataAsStream(renderListDataParams); // log array of items in response console.log(r.Row);","title":"Render list data as stream"},{"location":"sp/lists/#reserve-list-item-id-for-idempotent-list-item-creation","text":"const listItemId = await list.reserveListItemId(); // log id to console console.log(listItemId);","title":"Reserve list item Id for idempotent list item creation"},{"location":"sp/lists/#get-list-item-entity-type-name","text":"const entityTypeFullName = await list.getListItemEntityTypeFullName(); // log entity type name console.log(entityTypeFullName);","title":"Get list item entity type name"},{"location":"sp/lists/#add-a-list-item-using-path-folder-validation-and-set-field-values","text":"const list = await sp.webs.lists.getByTitle(\"MyList\").select(\"Title\", \"ParentWebUrl\")(); const formValues: IListItemFormUpdateValue[] = [ { FieldName: \"Title\", FieldValue: title, }, ]; list.addValidateUpdateItemUsingPath(formValues,`${list.ParentWebUrl}/Lists/${list.Title}/MyFolder`)","title":"Add a list item using path (folder), validation and set field values"},{"location":"sp/lists/#content-types-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/content-types\"; Selective 2 import \"@pnp/sp/content-types/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"content-types imports"},{"location":"sp/lists/#contenttypes","text":"Get all content types for a list const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.contentTypes();","title":"contentTypes"},{"location":"sp/lists/#fields-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/fields\"; Selective 2 import \"@pnp/sp/fields/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"fields imports"},{"location":"sp/lists/#fields","text":"Get all the fields for a list const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.fields(); Add a field to the site, then add the site field to a list const fld = await sp.site.rootWeb.fields.addText(\"MyField\"); await sp.web.lists.getByTitle(\"MyList\").fields.createFieldAsXml(fld.data.SchemaXml);","title":"fields"},{"location":"sp/lists/#folders-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/folders\"; Selective 2 import \"@pnp/sp/folders/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"folders imports"},{"location":"sp/lists/#folders","text":"Get the root folder of a list. const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.rootFolder();","title":"folders"},{"location":"sp/lists/#forms-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/forms\"; Selective 2 import \"@pnp/sp/forms/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"forms imports"},{"location":"sp/lists/#forms","text":"const r = await list.forms();","title":"forms"},{"location":"sp/lists/#items-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/items\"; Selective 2 import \"@pnp/sp/items/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"items imports"},{"location":"sp/lists/#items","text":"Get a collection of list items. const r = await list.items();","title":"items"},{"location":"sp/lists/#views-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/views\"; Selective 2 import \"@pnp/sp/views/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"views imports"},{"location":"sp/lists/#views","text":"Get the default view of the list const list = sp.web.lists.getByTitle(\"Documents\"); const views = await list.views(); const defaultView = await list.defaultView(); Get a list view by Id const view = await list.getView(defaultView.Id).select(\"Title\")();","title":"views"},{"location":"sp/lists/#security-imports","text":"To work with list security, you can import the list methods as follows: import \"@pnp/sp/security/list\"; For more information on how to call security methods for lists, please refer to the @pnp/sp/security documentation.","title":"security imports"},{"location":"sp/lists/#subscriptions-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/subscriptions\"; Selective 2 import \"@pnp/sp/subscriptions/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"subscriptions imports"},{"location":"sp/lists/#subscriptions","text":"Get all subscriptions on the list const list = sp.web.lists.getByTitle(\"Documents\"); const subscriptions = await list.subscriptions();","title":"subscriptions"},{"location":"sp/lists/#user-custom-actions-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/user-custom-actions\"; Selective 2 import \"@pnp/sp/user-custom-actions/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"user-custom-actions imports"},{"location":"sp/lists/#usercustomactions","text":"Get a collection of the list's user custom actions. const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.userCustomActions();","title":"userCustomActions"},{"location":"sp/lists/#getparentinfos","text":"Added in 2.0.12 Gets information about an list, including details about the parent list root folder, and parent web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; const list = sp.web.lists.getByTitle(\"Documents\"); await list.getParentInfos();","title":"getParentInfos"},{"location":"sp/navigation/","text":"@pnp/sp - navigation \u00b6 Navigation Service \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/navigation\"; getMenuState \u00b6 The MenuState service operation returns a Menu-State (dump) of a SiteMapProvider on a site. It will return an exception if the SiteMapProvider cannot be found on the site, the SiteMapProvider does not implement the IEditableSiteMapProvider interface or the SiteMapNode key cannot be found within the provider hierarchy. The IEditableSiteMapProvider also supports Custom Properties which is an optional feature. What will be return in the custom properties is up to the IEditableSiteMapProvider implementation and can differ for for each SiteMapProvider implementation. The custom properties can be requested by providing a comma separated string of property names like: property1,property2,property3\\,containingcomma NOTE: the , separator can be escaped using the \\ as escape character as done in the example above. The string above would split like: property1 property2 property3,containingcomma import { sp } from \"@pnp/sp\"; import \"@pnp/sp/navigation\"; // Will return a menu state of the default SiteMapProvider 'SPSiteMapProvider' where the dump starts a the RootNode (within the site) with a depth of 10 levels. const state = await sp.navigation.getMenuState(); // Will return the menu state of the 'SPSiteMapProvider', starting with the node with the key '1002' with a depth of 5 const state2 = await sp.navigation.getMenuState(\"1002\", 5); // Will return the menu state of the 'CurrentNavSiteMapProviderNoEncode' from the root node of the provider with a depth of 5 const state3 = await sp.navigation.getMenuState(null, 5, \"CurrentNavSiteMapProviderNoEncode\"); getMenuNodeKey \u00b6 Tries to get a SiteMapNode.Key for a given URL within a site collection. If the SiteMapNode cannot be found an Exception is returned. The method is using SiteMapProvider.FindSiteMapNodeFromKey(string rawUrl) to lookup the SiteMapNode. Depending on the actual implementation of FindSiteMapNodeFromKey the matching can differ for different SiteMapProviders. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/navigation\"; const key = await sp.navigation.getMenuNodeKey(\"/sites/dev/Lists/SPPnPJSExampleList/AllItems.aspx\"); Web Navigation \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; The navigation object contains two properties \"quicklaunch\" and \"topnavigationbar\". Both have the same set of methods so our examples below show use of only quicklaunch but apply equally to topnavigationbar. Get navigation \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const top = await sp.web.navigation.topNavigationBar(); const quick = await sp.web.navigation.quicklaunch(); For the following examples we will refer to a variable named \"nav\" that is understood to be one of topNavigationBar or quicklaunch. getById \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const node = await nav.getById(3)(); add \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const result = await nav.add(\"Node Title\", \"/sites/dev/pages/mypage.aspx\", true); const nodeDataRaw = result.data; // request the data from the created node const nodeData = result.node(); moveAfter \u00b6 Places a navigation node after another node in the tree import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const node1result = await nav.add(`Testing - ${getRandomString(4)} (1)`, url, true); const node2result = await nav.add(`Testing - ${getRandomString(4)} (2)`, url, true); const node1 = await node1result.node(); const node2 = await node2result.node(); await nav.moveAfter(node1.Id, node2.Id); Delete \u00b6 Deletes a given node import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const node1result = await nav.add(`Testing - ${getRandomString(4)}`, url, true); let nodes = await nav(); // check we added a node let index = nodes.findIndex(n => n.Id === node1result.data.Id) // index >= 0 // delete a node await nav.getById(node1result.data.Id).delete(); nodes = await nav(); index = nodes.findIndex(n => n.Id === node1result.data.Id) // index = -1 Update \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; await nav.getById(4).update({ Title: \"A new title\", }); Children \u00b6 The children property of a Navigation Node represents a collection with all the same properties and methods available on topNavigationBar or quicklaunch. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const childrenData = await nav.getById(1).children(); // add a child await nav.getById(1).children.add(\"Title\", \"Url\", true);","title":"Navigation"},{"location":"sp/navigation/#pnpsp-navigation","text":"","title":"@pnp/sp - navigation"},{"location":"sp/navigation/#navigation-service","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/navigation\";","title":"Navigation Service"},{"location":"sp/navigation/#getmenustate","text":"The MenuState service operation returns a Menu-State (dump) of a SiteMapProvider on a site. It will return an exception if the SiteMapProvider cannot be found on the site, the SiteMapProvider does not implement the IEditableSiteMapProvider interface or the SiteMapNode key cannot be found within the provider hierarchy. The IEditableSiteMapProvider also supports Custom Properties which is an optional feature. What will be return in the custom properties is up to the IEditableSiteMapProvider implementation and can differ for for each SiteMapProvider implementation. The custom properties can be requested by providing a comma separated string of property names like: property1,property2,property3\\,containingcomma NOTE: the , separator can be escaped using the \\ as escape character as done in the example above. The string above would split like: property1 property2 property3,containingcomma import { sp } from \"@pnp/sp\"; import \"@pnp/sp/navigation\"; // Will return a menu state of the default SiteMapProvider 'SPSiteMapProvider' where the dump starts a the RootNode (within the site) with a depth of 10 levels. const state = await sp.navigation.getMenuState(); // Will return the menu state of the 'SPSiteMapProvider', starting with the node with the key '1002' with a depth of 5 const state2 = await sp.navigation.getMenuState(\"1002\", 5); // Will return the menu state of the 'CurrentNavSiteMapProviderNoEncode' from the root node of the provider with a depth of 5 const state3 = await sp.navigation.getMenuState(null, 5, \"CurrentNavSiteMapProviderNoEncode\");","title":"getMenuState"},{"location":"sp/navigation/#getmenunodekey","text":"Tries to get a SiteMapNode.Key for a given URL within a site collection. If the SiteMapNode cannot be found an Exception is returned. The method is using SiteMapProvider.FindSiteMapNodeFromKey(string rawUrl) to lookup the SiteMapNode. Depending on the actual implementation of FindSiteMapNodeFromKey the matching can differ for different SiteMapProviders. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/navigation\"; const key = await sp.navigation.getMenuNodeKey(\"/sites/dev/Lists/SPPnPJSExampleList/AllItems.aspx\");","title":"getMenuNodeKey"},{"location":"sp/navigation/#web-navigation","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; The navigation object contains two properties \"quicklaunch\" and \"topnavigationbar\". Both have the same set of methods so our examples below show use of only quicklaunch but apply equally to topnavigationbar.","title":"Web Navigation"},{"location":"sp/navigation/#get-navigation","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const top = await sp.web.navigation.topNavigationBar(); const quick = await sp.web.navigation.quicklaunch(); For the following examples we will refer to a variable named \"nav\" that is understood to be one of topNavigationBar or quicklaunch.","title":"Get navigation"},{"location":"sp/navigation/#getbyid","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const node = await nav.getById(3)();","title":"getById"},{"location":"sp/navigation/#add","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const result = await nav.add(\"Node Title\", \"/sites/dev/pages/mypage.aspx\", true); const nodeDataRaw = result.data; // request the data from the created node const nodeData = result.node();","title":"add"},{"location":"sp/navigation/#moveafter","text":"Places a navigation node after another node in the tree import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const node1result = await nav.add(`Testing - ${getRandomString(4)} (1)`, url, true); const node2result = await nav.add(`Testing - ${getRandomString(4)} (2)`, url, true); const node1 = await node1result.node(); const node2 = await node2result.node(); await nav.moveAfter(node1.Id, node2.Id);","title":"moveAfter"},{"location":"sp/navigation/#delete","text":"Deletes a given node import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const node1result = await nav.add(`Testing - ${getRandomString(4)}`, url, true); let nodes = await nav(); // check we added a node let index = nodes.findIndex(n => n.Id === node1result.data.Id) // index >= 0 // delete a node await nav.getById(node1result.data.Id).delete(); nodes = await nav(); index = nodes.findIndex(n => n.Id === node1result.data.Id) // index = -1","title":"Delete"},{"location":"sp/navigation/#update","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; await nav.getById(4).update({ Title: \"A new title\", });","title":"Update"},{"location":"sp/navigation/#children","text":"The children property of a Navigation Node represents a collection with all the same properties and methods available on topNavigationBar or quicklaunch. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const childrenData = await nav.getById(1).children(); // add a child await nav.getById(1).children.add(\"Title\", \"Url\", true);","title":"Children"},{"location":"sp/permissions/","text":"@pnp/sp - permissions \u00b6 A common task is to determine if a user or the current user has a certain permission level. It is a great idea to check before performing a task such as creating a list to ensure a user can without getting back an error. This allows you to provide a better experience to the user. Permissions in SharePoint are assigned to the set of securable objects which include Site, Web, List, and List Item. These are the four level to which unique permissions can be assigned. As such @pnp/sp provides a set of methods defined in the QueryableSecurable class to handle these permissions. These examples all use the Web to get the values, however the methods work identically on all securables. Get Role Assignments \u00b6 This gets a collection of all the role assignments on a given securable. The property returns a RoleAssignments collection which supports the OData collection operators. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/security\"; import { Logger } from \"@pnp/logging\"; const roles = await sp.web.roleAssignments(); Logger.writeJSON(roles); First Unique Ancestor Securable Object \u00b6 This method can be used to find the securable parent up the hierarchy that has unique permissions. If everything inherits permissions this will be the Site. If a sub web has unique permissions it will be the web, and so on. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/security\"; import { Logger } from \"@pnp/logging\"; const obj = await sp.web.firstUniqueAncestorSecurableObject(); Logger.writeJSON(obj); User Effective Permissions \u00b6 This method returns the BasePermissions for a given user or the current user. This value contains the High and Low values for a user on the securable you have queried. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/security\"; import { Logger } from \"@pnp/logging\"; const perms = await sp.web.getUserEffectivePermissions(\"i:0#.f|membership|user@site.com\"); Logger.writeJSON(perms); const perms2 = await sp.web.getCurrentUserEffectivePermissions(); Logger.writeJSON(perms2); User Has Permissions \u00b6 Because the High and Low values in the BasePermission don't obviously mean anything you can use these methods along with the PermissionKind enumeration to check actual rights on the securable. import { sp } from \"@pnp/sp\"; import { PermissionKind } from \"@pnp/sp/security\"; const perms = await sp.web.userHasPermissions(\"i:0#.f|membership|user@site.com\", PermissionKind.ApproveItems); console.log(perms); const perms2 = await sp.web.currentUserHasPermissions(PermissionKind.ApproveItems); console.log(perms2); Has Permissions \u00b6 If you need to check multiple permissions it can be more efficient to get the BasePermissions once and then use the hasPermissions method to check them as shown below. import { sp } from \"@pnp/sp\"; import { PermissionKind } from \"@pnp/sp/security\"; const perms = await sp.web.getCurrentUserEffectivePermissions(); if (sp.web.hasPermissions(perms, PermissionKind.AddListItems) && sp.web.hasPermissions(perms, PermissionKind.DeleteVersions)) { // ... }","title":"Permissions"},{"location":"sp/permissions/#pnpsp-permissions","text":"A common task is to determine if a user or the current user has a certain permission level. It is a great idea to check before performing a task such as creating a list to ensure a user can without getting back an error. This allows you to provide a better experience to the user. Permissions in SharePoint are assigned to the set of securable objects which include Site, Web, List, and List Item. These are the four level to which unique permissions can be assigned. As such @pnp/sp provides a set of methods defined in the QueryableSecurable class to handle these permissions. These examples all use the Web to get the values, however the methods work identically on all securables.","title":"@pnp/sp - permissions"},{"location":"sp/permissions/#get-role-assignments","text":"This gets a collection of all the role assignments on a given securable. The property returns a RoleAssignments collection which supports the OData collection operators. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/security\"; import { Logger } from \"@pnp/logging\"; const roles = await sp.web.roleAssignments(); Logger.writeJSON(roles);","title":"Get Role Assignments"},{"location":"sp/permissions/#first-unique-ancestor-securable-object","text":"This method can be used to find the securable parent up the hierarchy that has unique permissions. If everything inherits permissions this will be the Site. If a sub web has unique permissions it will be the web, and so on. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/security\"; import { Logger } from \"@pnp/logging\"; const obj = await sp.web.firstUniqueAncestorSecurableObject(); Logger.writeJSON(obj);","title":"First Unique Ancestor Securable Object"},{"location":"sp/permissions/#user-effective-permissions","text":"This method returns the BasePermissions for a given user or the current user. This value contains the High and Low values for a user on the securable you have queried. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/security\"; import { Logger } from \"@pnp/logging\"; const perms = await sp.web.getUserEffectivePermissions(\"i:0#.f|membership|user@site.com\"); Logger.writeJSON(perms); const perms2 = await sp.web.getCurrentUserEffectivePermissions(); Logger.writeJSON(perms2);","title":"User Effective Permissions"},{"location":"sp/permissions/#user-has-permissions","text":"Because the High and Low values in the BasePermission don't obviously mean anything you can use these methods along with the PermissionKind enumeration to check actual rights on the securable. import { sp } from \"@pnp/sp\"; import { PermissionKind } from \"@pnp/sp/security\"; const perms = await sp.web.userHasPermissions(\"i:0#.f|membership|user@site.com\", PermissionKind.ApproveItems); console.log(perms); const perms2 = await sp.web.currentUserHasPermissions(PermissionKind.ApproveItems); console.log(perms2);","title":"User Has Permissions"},{"location":"sp/permissions/#has-permissions","text":"If you need to check multiple permissions it can be more efficient to get the BasePermissions once and then use the hasPermissions method to check them as shown below. import { sp } from \"@pnp/sp\"; import { PermissionKind } from \"@pnp/sp/security\"; const perms = await sp.web.getCurrentUserEffectivePermissions(); if (sp.web.hasPermissions(perms, PermissionKind.AddListItems) && sp.web.hasPermissions(perms, PermissionKind.DeleteVersions)) { // ... }","title":"Has Permissions"},{"location":"sp/profiles/","text":"@pnp/sp/profiles \u00b6 The profile services allows you to work with the SharePoint User Profile Store. Profiles \u00b6 Profiles is accessed directly from the root sp object. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/profiles\"; Get edit profile link for the current user \u00b6 editProfileLink(): Promise const editProfileLink = await sp.profiles.editProfileLink(); console.log(\"My edit profile link =\" + editProfileLink); Is My People List Public \u00b6 Provides a boolean that indicates if the current users \"People I'm Following\" list is public or not isMyPeopleListPublic(): Promise const isPublic = await sp.profiles.isMyPeopleListPublic(); console.log(\"Is my Following list Public =\" + isPubic); Find out if the current user is followed by another user \u00b6 Provides a boolean that indicates if the current users is followed by a specific user. amIFollowedBy(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const isFollowedBy = await sp.profiles.amIFollowedBy(loginName); console.log(\"Is \" + loginName + \" following me? \" + isFollowedBy); Find out if I am following a specific user \u00b6 Provides a boolean that indicates if the current users is followed by a specific user. amIFollowing(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const following = await sp.profiles.amIFollowing(loginName); console.log(\"Am I following \" + loginName + \"? \" + following); Get the tags I follow \u00b6 Gets the tags the current user is following. Accepts max count, default is 20. getFollowedTags(maxCount = 20): Promise const tags = await sp.profiles.getFollowedTags(); console.log(tags); Get followers for a specific user \u00b6 Gets the people who are following the specified user. getFollowersFor(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const followers = await sp.profiles.getFollowersFor(loginName); followers.forEach((value) => { console.log(value); }); Get followers for the current \u00b6 Gets the people who are following the current user. myFollowers(): ISharePointQueryableCollection const folowers = await sp.profiles.myFollowers(); console.log(folowers); Get the properties for the current user \u00b6 Gets user properties for the current user. myProperties(): _SharePointQueryableInstance const profile = await sp.profiles.myProperties(); console.log(profile.DisplayName); console.log(profile.Email); console.log(profile.Title); console.log(profile.UserProfileProperties.length); // Properties are stored in Key/Value pairs, // so parse into an object called userProperties var props = {}; profile.UserProfileProperties.forEach((prop) => { props[prop.Key] = prop.Value; }); profile.userProperties = props; console.log(\"Account Name: \" + profile.userProperties.AccountName); Gets people specified user is following \u00b6 getPeopleFollowedBy(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const folowers = await sp.profiles.getFollowersFor(loginName); followers.forEach((value) => { console.log(value); }); Gets properties for a specified user \u00b6 getPropertiesFor(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const profile = await sp.profiles.getPropertiesFor(loginName); console.log(profile.DisplayName); console.log(profile.Email); console.log(profile.Title); console.log(profile.UserProfileProperties.length); // Properties are stored in inconvenient Key/Value pairs, // so parse into an object called userProperties var props = {}; profile.UserProfileProperties.forEach((prop) => { props[prop.Key] = prop.Value; }); profile.userProperties = props; console.log(\"Account Name: \" + profile.userProperties.AccountName); Gets most popular tags \u00b6 Gets the 20 most popular hash tags over the past week, sorted so that the most popular tag appears first trendingTags(): Promise const tags = await sp.profiles.trendingTags(); tags.Items.forEach((tag) => { console.log(tag); }); Gets specified user profile property for the specified user \u00b6 getUserProfilePropertyFor(loginName: string, propertyName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const propertyName = \"AccountName\"; const property = await sp.profiles.getUserProfilePropertyFor(loginName, propertyName); console.log(property); Hide specific user from list of suggested people \u00b6 Removes the specified user from the user's list of suggested people to follow. hideSuggestion(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; await sp.profiles.hideSuggestion(loginName); Is one user following another \u00b6 Indicates whether the first user is following the second user. First parameter is the account name of the user who might be following the followee. Second parameter is the account name of the user who might be followed by the follower. isFollowing(follower: string, followee: string): Promise const follower = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const followee = \"i:0#.f|membership|testuser2@mytenant.onmicrosoft.com\"; const isFollowing = await sp.profiles.isFollowing(follower, followee); console.log(isFollowing); Set User Profile Picture \u00b6 Uploads and sets the user profile picture (Users can upload a picture to their own profile only). Not supported for batching. Accepts the profilePicSource Blob data representing the user's picture in BMP, JPEG, or PNG format of up to 4.76MB. setMyProfilePic(profilePicSource: Blob): Promise import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/profiles\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/files\"; // get the blob object through a request or from a file input const blob = await sp.web.lists.getByTitle(\"Documents\").rootFolder.files.getByName(\"profile.jpg\").getBlob(); await sp.profiles.setMyProfilePic(blob); Sets single value User Profile property \u00b6 accountName The account name of the user propertyName Property name propertyValue Property value setSingleValueProfileProperty(accountName: string, propertyName: string, propertyValue: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; await sp.profiles.setSingleValueProfileProperty(loginName, \"CellPhone\", \"(123) 555-1212\"); Sets a mult-value User Profile property \u00b6 accountName The account name of the user propertyName Property name propertyValues Property values setMultiValuedProfileProperty(accountName: string, propertyName: string, propertyValues: string[]): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const propertyName = \"SPS-Skills\"; const propertyValues = [\"SharePoint\", \"Office 365\", \"Architecture\", \"Azure\"]; await sp.profiles.setMultiValuedProfileProperty(loginName, propertyName, propertyValues); const profile = await sp.profiles.getPropertiesFor(loginName); var props = {}; profile.UserProfileProperties.forEach((prop) => { props[prop.Key] = prop.Value; }); profile.userProperties = props; console.log(profile.userProperties[propertyName]); Create Personal Site for specified users \u00b6 Provisions one or more users' personal sites. (My Site administrator on SharePoint Online only) Emails The email addresses of the users to provision sites for createPersonalSiteEnqueueBulk(...emails: string[]): Promise let userEmails: string[] = [\"testuser1@mytenant.onmicrosoft.com\", \"testuser2@mytenant.onmicrosoft.com\"]; await sp.profiles.createPersonalSiteEnqueueBulk(userEmails); Get the user profile of the owner for the current site \u00b6 ownerUserProfile(): Promise const profile = await sp.profiles.ownerUserProfile(); console.log(profile); Get the user profile of the current user \u00b6 userProfile(): Promise const profile = await sp.profiles.userProfile(); console.log(profile); Create personal site for current user \u00b6 createPersonalSite(interactiveRequest = false): Promise await sp.profiles.createPersonalSite(); Make all profile data public or private \u00b6 Set the privacy settings for all social data. shareAllSocialData(share: boolean): Promise await sp.profiles.shareAllSocialData(true); Resolve a user or group \u00b6 Resolves user or group using specified query parameters clientPeoplePickerResolveUser(queryParams: IClientPeoplePickerQueryParameters): Promise const result = await sp.profiles.clientPeoplePickerSearchUser({ AllowEmailAddresses: true, AllowMultipleEntities: false, MaximumEntitySuggestions: 25, QueryString: 'John' }); console.log(result); Search a user or group \u00b6 Searches for users or groups using specified query parameters clientPeoplePickerSearchUser(queryParams: IClientPeoplePickerQueryParameters): Promise const result = await sp.profiles.clientPeoplePickerSearchUser({ AllowEmailAddresses: true, AllowMultipleEntities: false, MaximumEntitySuggestions: 25, QueryString: 'John' }); console.log(result);","title":"Profiles"},{"location":"sp/profiles/#pnpspprofiles","text":"The profile services allows you to work with the SharePoint User Profile Store.","title":"@pnp/sp/profiles"},{"location":"sp/profiles/#profiles","text":"Profiles is accessed directly from the root sp object. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/profiles\";","title":"Profiles"},{"location":"sp/profiles/#get-edit-profile-link-for-the-current-user","text":"editProfileLink(): Promise const editProfileLink = await sp.profiles.editProfileLink(); console.log(\"My edit profile link =\" + editProfileLink);","title":"Get edit profile link for the current user"},{"location":"sp/profiles/#is-my-people-list-public","text":"Provides a boolean that indicates if the current users \"People I'm Following\" list is public or not isMyPeopleListPublic(): Promise const isPublic = await sp.profiles.isMyPeopleListPublic(); console.log(\"Is my Following list Public =\" + isPubic);","title":"Is My People List Public"},{"location":"sp/profiles/#find-out-if-the-current-user-is-followed-by-another-user","text":"Provides a boolean that indicates if the current users is followed by a specific user. amIFollowedBy(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const isFollowedBy = await sp.profiles.amIFollowedBy(loginName); console.log(\"Is \" + loginName + \" following me? \" + isFollowedBy);","title":"Find out if the current user is followed by another user"},{"location":"sp/profiles/#find-out-if-i-am-following-a-specific-user","text":"Provides a boolean that indicates if the current users is followed by a specific user. amIFollowing(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const following = await sp.profiles.amIFollowing(loginName); console.log(\"Am I following \" + loginName + \"? \" + following);","title":"Find out if I am following a specific user"},{"location":"sp/profiles/#get-the-tags-i-follow","text":"Gets the tags the current user is following. Accepts max count, default is 20. getFollowedTags(maxCount = 20): Promise const tags = await sp.profiles.getFollowedTags(); console.log(tags);","title":"Get the tags I follow"},{"location":"sp/profiles/#get-followers-for-a-specific-user","text":"Gets the people who are following the specified user. getFollowersFor(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const followers = await sp.profiles.getFollowersFor(loginName); followers.forEach((value) => { console.log(value); });","title":"Get followers for a specific user"},{"location":"sp/profiles/#get-followers-for-the-current","text":"Gets the people who are following the current user. myFollowers(): ISharePointQueryableCollection const folowers = await sp.profiles.myFollowers(); console.log(folowers);","title":"Get followers for the current"},{"location":"sp/profiles/#get-the-properties-for-the-current-user","text":"Gets user properties for the current user. myProperties(): _SharePointQueryableInstance const profile = await sp.profiles.myProperties(); console.log(profile.DisplayName); console.log(profile.Email); console.log(profile.Title); console.log(profile.UserProfileProperties.length); // Properties are stored in Key/Value pairs, // so parse into an object called userProperties var props = {}; profile.UserProfileProperties.forEach((prop) => { props[prop.Key] = prop.Value; }); profile.userProperties = props; console.log(\"Account Name: \" + profile.userProperties.AccountName);","title":"Get the properties for the current user"},{"location":"sp/profiles/#gets-people-specified-user-is-following","text":"getPeopleFollowedBy(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const folowers = await sp.profiles.getFollowersFor(loginName); followers.forEach((value) => { console.log(value); });","title":"Gets people specified user is following"},{"location":"sp/profiles/#gets-properties-for-a-specified-user","text":"getPropertiesFor(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const profile = await sp.profiles.getPropertiesFor(loginName); console.log(profile.DisplayName); console.log(profile.Email); console.log(profile.Title); console.log(profile.UserProfileProperties.length); // Properties are stored in inconvenient Key/Value pairs, // so parse into an object called userProperties var props = {}; profile.UserProfileProperties.forEach((prop) => { props[prop.Key] = prop.Value; }); profile.userProperties = props; console.log(\"Account Name: \" + profile.userProperties.AccountName);","title":"Gets properties for a specified user"},{"location":"sp/profiles/#gets-most-popular-tags","text":"Gets the 20 most popular hash tags over the past week, sorted so that the most popular tag appears first trendingTags(): Promise const tags = await sp.profiles.trendingTags(); tags.Items.forEach((tag) => { console.log(tag); });","title":"Gets most popular tags"},{"location":"sp/profiles/#gets-specified-user-profile-property-for-the-specified-user","text":"getUserProfilePropertyFor(loginName: string, propertyName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const propertyName = \"AccountName\"; const property = await sp.profiles.getUserProfilePropertyFor(loginName, propertyName); console.log(property);","title":"Gets specified user profile property for the specified user"},{"location":"sp/profiles/#hide-specific-user-from-list-of-suggested-people","text":"Removes the specified user from the user's list of suggested people to follow. hideSuggestion(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; await sp.profiles.hideSuggestion(loginName);","title":"Hide specific user from list of suggested people"},{"location":"sp/profiles/#is-one-user-following-another","text":"Indicates whether the first user is following the second user. First parameter is the account name of the user who might be following the followee. Second parameter is the account name of the user who might be followed by the follower. isFollowing(follower: string, followee: string): Promise const follower = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const followee = \"i:0#.f|membership|testuser2@mytenant.onmicrosoft.com\"; const isFollowing = await sp.profiles.isFollowing(follower, followee); console.log(isFollowing);","title":"Is one user following another"},{"location":"sp/profiles/#set-user-profile-picture","text":"Uploads and sets the user profile picture (Users can upload a picture to their own profile only). Not supported for batching. Accepts the profilePicSource Blob data representing the user's picture in BMP, JPEG, or PNG format of up to 4.76MB. setMyProfilePic(profilePicSource: Blob): Promise import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/profiles\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/files\"; // get the blob object through a request or from a file input const blob = await sp.web.lists.getByTitle(\"Documents\").rootFolder.files.getByName(\"profile.jpg\").getBlob(); await sp.profiles.setMyProfilePic(blob);","title":"Set User Profile Picture"},{"location":"sp/profiles/#sets-single-value-user-profile-property","text":"accountName The account name of the user propertyName Property name propertyValue Property value setSingleValueProfileProperty(accountName: string, propertyName: string, propertyValue: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; await sp.profiles.setSingleValueProfileProperty(loginName, \"CellPhone\", \"(123) 555-1212\");","title":"Sets single value User Profile property"},{"location":"sp/profiles/#sets-a-mult-value-user-profile-property","text":"accountName The account name of the user propertyName Property name propertyValues Property values setMultiValuedProfileProperty(accountName: string, propertyName: string, propertyValues: string[]): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const propertyName = \"SPS-Skills\"; const propertyValues = [\"SharePoint\", \"Office 365\", \"Architecture\", \"Azure\"]; await sp.profiles.setMultiValuedProfileProperty(loginName, propertyName, propertyValues); const profile = await sp.profiles.getPropertiesFor(loginName); var props = {}; profile.UserProfileProperties.forEach((prop) => { props[prop.Key] = prop.Value; }); profile.userProperties = props; console.log(profile.userProperties[propertyName]);","title":"Sets a mult-value User Profile property"},{"location":"sp/profiles/#create-personal-site-for-specified-users","text":"Provisions one or more users' personal sites. (My Site administrator on SharePoint Online only) Emails The email addresses of the users to provision sites for createPersonalSiteEnqueueBulk(...emails: string[]): Promise let userEmails: string[] = [\"testuser1@mytenant.onmicrosoft.com\", \"testuser2@mytenant.onmicrosoft.com\"]; await sp.profiles.createPersonalSiteEnqueueBulk(userEmails);","title":"Create Personal Site for specified users"},{"location":"sp/profiles/#get-the-user-profile-of-the-owner-for-the-current-site","text":"ownerUserProfile(): Promise const profile = await sp.profiles.ownerUserProfile(); console.log(profile);","title":"Get the user profile of the owner for the current site"},{"location":"sp/profiles/#get-the-user-profile-of-the-current-user","text":"userProfile(): Promise const profile = await sp.profiles.userProfile(); console.log(profile);","title":"Get the user profile of the current user"},{"location":"sp/profiles/#create-personal-site-for-current-user","text":"createPersonalSite(interactiveRequest = false): Promise await sp.profiles.createPersonalSite();","title":"Create personal site for current user"},{"location":"sp/profiles/#make-all-profile-data-public-or-private","text":"Set the privacy settings for all social data. shareAllSocialData(share: boolean): Promise await sp.profiles.shareAllSocialData(true);","title":"Make all profile data public or private"},{"location":"sp/profiles/#resolve-a-user-or-group","text":"Resolves user or group using specified query parameters clientPeoplePickerResolveUser(queryParams: IClientPeoplePickerQueryParameters): Promise const result = await sp.profiles.clientPeoplePickerSearchUser({ AllowEmailAddresses: true, AllowMultipleEntities: false, MaximumEntitySuggestions: 25, QueryString: 'John' }); console.log(result);","title":"Resolve a user or group"},{"location":"sp/profiles/#search-a-user-or-group","text":"Searches for users or groups using specified query parameters clientPeoplePickerSearchUser(queryParams: IClientPeoplePickerQueryParameters): Promise const result = await sp.profiles.clientPeoplePickerSearchUser({ AllowEmailAddresses: true, AllowMultipleEntities: false, MaximumEntitySuggestions: 25, QueryString: 'John' }); console.log(result);","title":"Search a user or group"},{"location":"sp/regional-settings/","text":"@pnp/sp/regional-settings \u00b6 The regional settings module helps with managing dates and times across various timezones. IRegionalSettings \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IRegionalSettings, ITimeZone, ITimeZones, RegionalSettings, TimeZone, TimeZones, } from \"@pnp/sp/regional-settings\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; Preset: All import { sp, Webs, IWebs } from \"@pnp/sp/presets/all\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; // get all the web's regional settings const s = await sp.web.regionalSettings(); // select only some settings to return const s2 = await sp.web.regionalSettings.select(\"DecimalSeparator\", \"ListSeparator\", \"IsUIRightToLeft\")(); Installed Languages \u00b6 You can get a list of the installed languages in the web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; const s = await sp.web.regionalSettings.getInstalledLanguages(); The installedLanguages property accessor is deprecated after 2.0.4 in favor of getInstalledLanguages and will be removed in future versions. TimeZones \u00b6 You can also get information about the selected timezone in the web and all of the defined timezones. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; // get the web's configured timezone const s = await sp.web.regionalSettings.timeZone(); // select just the Description and Id const s2 = await sp.web.regionalSettings.timeZone.select(\"Description\", \"Id\")(); // get all the timezones const s3 = await sp.web.regionalSettings.timeZones(); // get a specific timezone by id // list of ids: https://msdn.microsoft.com/en-us/library/office/jj247008.aspx const s4 = await sp.web.regionalSettings.timeZones.getById(23); const s5 = await s.localTimeToUTC(new Date()); // convert a given date from web's local time to UTC time const s6 = await sp.web.regionalSettings.timeZone.localTimeToUTC(new Date()); // convert a given date from UTC time to web's local time const s6 = await sp.web.regionalSettings.timeZone.utcToLocalTime(new Date()) const s7 = await sp.web.regionalSettings.timeZone.utcToLocalTime(new Date(2019, 6, 10, 10, 0, 0, 0)) Title and Description Resources \u00b6 Added in 2.0.4 Some objects allow you to read language specific title information as shown in the following sample. This applies to Web, List, Field, Content Type, and User Custom Actions. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings\"; // // The below methods appears on // - Web // - List // - Field // - ContentType // - User Custom Action // // after you import @pnp/sp/regional-settings // // you can also import just parts of the regional settings: // import \"@pnp/sp/regional-settings/web\"; // import \"@pnp/sp/regional-settings/list\"; // import \"@pnp/sp/regional-settings/content-type\"; // import \"@pnp/sp/regional-settings/field\"; // import \"@pnp/sp/regional-settings/user-custom-actions\"; const title = await sp.web.titleResource(\"en-us\"); const title2 = await sp.web.titleResource(\"de-de\"); const description = await sp.web.descriptionResource(\"en-us\"); const description2 = await sp.web.descriptionResource(\"de-de\"); You can only read the values through the REST API, not set the value.","title":"Regional Settings"},{"location":"sp/regional-settings/#pnpspregional-settings","text":"The regional settings module helps with managing dates and times across various timezones.","title":"@pnp/sp/regional-settings"},{"location":"sp/regional-settings/#iregionalsettings","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IRegionalSettings, ITimeZone, ITimeZones, RegionalSettings, TimeZone, TimeZones, } from \"@pnp/sp/regional-settings\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; Preset: All import { sp, Webs, IWebs } from \"@pnp/sp/presets/all\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; // get all the web's regional settings const s = await sp.web.regionalSettings(); // select only some settings to return const s2 = await sp.web.regionalSettings.select(\"DecimalSeparator\", \"ListSeparator\", \"IsUIRightToLeft\")();","title":"IRegionalSettings"},{"location":"sp/regional-settings/#installed-languages","text":"You can get a list of the installed languages in the web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; const s = await sp.web.regionalSettings.getInstalledLanguages(); The installedLanguages property accessor is deprecated after 2.0.4 in favor of getInstalledLanguages and will be removed in future versions.","title":"Installed Languages"},{"location":"sp/regional-settings/#timezones","text":"You can also get information about the selected timezone in the web and all of the defined timezones. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; // get the web's configured timezone const s = await sp.web.regionalSettings.timeZone(); // select just the Description and Id const s2 = await sp.web.regionalSettings.timeZone.select(\"Description\", \"Id\")(); // get all the timezones const s3 = await sp.web.regionalSettings.timeZones(); // get a specific timezone by id // list of ids: https://msdn.microsoft.com/en-us/library/office/jj247008.aspx const s4 = await sp.web.regionalSettings.timeZones.getById(23); const s5 = await s.localTimeToUTC(new Date()); // convert a given date from web's local time to UTC time const s6 = await sp.web.regionalSettings.timeZone.localTimeToUTC(new Date()); // convert a given date from UTC time to web's local time const s6 = await sp.web.regionalSettings.timeZone.utcToLocalTime(new Date()) const s7 = await sp.web.regionalSettings.timeZone.utcToLocalTime(new Date(2019, 6, 10, 10, 0, 0, 0))","title":"TimeZones"},{"location":"sp/regional-settings/#title-and-description-resources","text":"Added in 2.0.4 Some objects allow you to read language specific title information as shown in the following sample. This applies to Web, List, Field, Content Type, and User Custom Actions. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings\"; // // The below methods appears on // - Web // - List // - Field // - ContentType // - User Custom Action // // after you import @pnp/sp/regional-settings // // you can also import just parts of the regional settings: // import \"@pnp/sp/regional-settings/web\"; // import \"@pnp/sp/regional-settings/list\"; // import \"@pnp/sp/regional-settings/content-type\"; // import \"@pnp/sp/regional-settings/field\"; // import \"@pnp/sp/regional-settings/user-custom-actions\"; const title = await sp.web.titleResource(\"en-us\"); const title2 = await sp.web.titleResource(\"de-de\"); const description = await sp.web.descriptionResource(\"en-us\"); const description2 = await sp.web.descriptionResource(\"de-de\"); You can only read the values through the REST API, not set the value.","title":"Title and Description Resources"},{"location":"sp/related-items/","text":"@pnp/sp/related-items \u00b6 The related items API allows you to add related items to items within a task or workflow list. Related items need to be in the same site collection. Setup \u00b6 Instead of copying this block of code into each sample, understand that each sample is meant to run with this supporting code to work. import { sp, extractWebUrl } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/related-items/web\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items/list\"; import \"@pnp/sp/files/list\"; import { IList } from \"@pnp/sp/lists\"; import { getRandomString } from \"@pnp/core\"; // setup some lists (or just use existing ones this is just to show the complete process) // we need two lists to use for creating related items, they need to use template 107 (task list) const ler1 = await sp.web.lists.ensure(\"RelatedItemsSourceList\", \"\", 107); const ler2 = await sp.web.lists.ensure(\"RelatedItemsTargetList\", \"\", 107); const sourceList = ler1.list; const targetList = ler2.list; const sourceListName = await sourceList.select(\"Id\")().then(r => r.Id); const targetListName = await targetList.select(\"Id\")().then(r => r.Id); // or whatever you need to get the web url, both our example lists are in the same web. const webUrl = sp.web.toUrl(); // ...individual samples start here addSingleLink \u00b6 const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); addSingleLinkToUrl \u00b6 This method adds a link to task item based on a url. The list name and item id are to the task item, the url is to the related item/document. // get a file's server relative url in some manner, here we add one const file = await sp.web.defaultDocumentLibrary.rootFolder.files.add(`file_${getRandomString(4)}.txt`, \"Content\", true).then(r => r.data); // add an item or get an item from the task list const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); await sp.web.relatedItems.addSingleLinkToUrl(targetListName, targetItem.Id, file.ServerRelativeUrl); addSingleLinkFromUrl \u00b6 This method adds a link to task item based on a url. The list name and item id are to related item, the url is to task item to which the related reference is being added. I haven't found a use case for this method. deleteSingleLink \u00b6 This method allows you to delete a link previously created. const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add the link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); // delete the link await sp.web.relatedItems.deleteSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); getRelatedItems \u00b6 Gets the related items for an item import { IRelatedItem } from \"@pnp/sp/related-items\"; const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); const targetItem2 = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem2.Id, webUrl); const items: IRelatedItem[] = await sp.web.relatedItems.getRelatedItems(sourceListName, sourceItem.Id); // items.length === 2 Related items are defined by the IRelatedItem interface export interface IRelatedItem { ListId: string; ItemId: number; Url: string; Title: string; WebId: string; IconUrl: string; } getPageOneRelatedItems \u00b6 Gets an abbreviated set of related items import { IRelatedItem } from \"@pnp/sp/related-items\"; const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); const targetItem2 = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem2.Id, webUrl); const items: IRelatedItem[] = await sp.web.relatedItems.getPageOneRelatedItems(sourceListName, sourceItem.Id); // items.length === 2","title":"Related Items"},{"location":"sp/related-items/#pnpsprelated-items","text":"The related items API allows you to add related items to items within a task or workflow list. Related items need to be in the same site collection.","title":"@pnp/sp/related-items"},{"location":"sp/related-items/#setup","text":"Instead of copying this block of code into each sample, understand that each sample is meant to run with this supporting code to work. import { sp, extractWebUrl } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/related-items/web\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items/list\"; import \"@pnp/sp/files/list\"; import { IList } from \"@pnp/sp/lists\"; import { getRandomString } from \"@pnp/core\"; // setup some lists (or just use existing ones this is just to show the complete process) // we need two lists to use for creating related items, they need to use template 107 (task list) const ler1 = await sp.web.lists.ensure(\"RelatedItemsSourceList\", \"\", 107); const ler2 = await sp.web.lists.ensure(\"RelatedItemsTargetList\", \"\", 107); const sourceList = ler1.list; const targetList = ler2.list; const sourceListName = await sourceList.select(\"Id\")().then(r => r.Id); const targetListName = await targetList.select(\"Id\")().then(r => r.Id); // or whatever you need to get the web url, both our example lists are in the same web. const webUrl = sp.web.toUrl(); // ...individual samples start here","title":"Setup"},{"location":"sp/related-items/#addsinglelink","text":"const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl);","title":"addSingleLink"},{"location":"sp/related-items/#addsinglelinktourl","text":"This method adds a link to task item based on a url. The list name and item id are to the task item, the url is to the related item/document. // get a file's server relative url in some manner, here we add one const file = await sp.web.defaultDocumentLibrary.rootFolder.files.add(`file_${getRandomString(4)}.txt`, \"Content\", true).then(r => r.data); // add an item or get an item from the task list const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); await sp.web.relatedItems.addSingleLinkToUrl(targetListName, targetItem.Id, file.ServerRelativeUrl);","title":"addSingleLinkToUrl"},{"location":"sp/related-items/#addsinglelinkfromurl","text":"This method adds a link to task item based on a url. The list name and item id are to related item, the url is to task item to which the related reference is being added. I haven't found a use case for this method.","title":"addSingleLinkFromUrl"},{"location":"sp/related-items/#deletesinglelink","text":"This method allows you to delete a link previously created. const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add the link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); // delete the link await sp.web.relatedItems.deleteSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl);","title":"deleteSingleLink"},{"location":"sp/related-items/#getrelateditems","text":"Gets the related items for an item import { IRelatedItem } from \"@pnp/sp/related-items\"; const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); const targetItem2 = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem2.Id, webUrl); const items: IRelatedItem[] = await sp.web.relatedItems.getRelatedItems(sourceListName, sourceItem.Id); // items.length === 2 Related items are defined by the IRelatedItem interface export interface IRelatedItem { ListId: string; ItemId: number; Url: string; Title: string; WebId: string; IconUrl: string; }","title":"getRelatedItems"},{"location":"sp/related-items/#getpageonerelateditems","text":"Gets an abbreviated set of related items import { IRelatedItem } from \"@pnp/sp/related-items\"; const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); const targetItem2 = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem2.Id, webUrl); const items: IRelatedItem[] = await sp.web.relatedItems.getPageOneRelatedItems(sourceListName, sourceItem.Id); // items.length === 2","title":"getPageOneRelatedItems"},{"location":"sp/search/","text":"@pnp/sp/search \u00b6 Using search you can access content throughout your organization in a secure and consistent manner. The library provides support for searching and suggest - as well as some interfaces and helper classes to make building your queries and processing responses easier. Search \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISearchQuery, SearchResults } from \"@pnp/sp/search\"; Preset: All import { sp, ISearchQuery, SearchResults } from \"@pnp/sp/presets/all\"; Search is accessed directly from the root sp object and can take either a string representing the query text, a plain object matching the ISearchQuery interface, or a SearchQueryBuilder instance. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISearchQuery, SearchResults, SearchQueryBuilder } from \"@pnp/sp/search\"; // text search using SharePoint default values for other parameters const results: SearchResults = await sp.search(\"test\"); console.log(results.ElapsedTime); console.log(results.RowCount); console.log(results.PrimarySearchResults); // define a search query object matching the ISearchQuery interface const results2: SearchResults = await sp.search({ Querytext: \"test\", RowLimit: 10, EnableInterleaving: true, }); console.log(results2.ElapsedTime); console.log(results2.RowCount); console.log(results2.PrimarySearchResults); // define a query using a builder const builder = SearchQueryBuilder(\"test\").rowLimit(10).enableInterleaving.enableQueryRules.processPersonalFavorites; const results3 = await sp.search(builder); console.log(results3.ElapsedTime); console.log(results3.RowCount); console.log(results3.PrimarySearchResults); Search Result Caching \u00b6 You can use the searchWithCaching method to enable cache support for your search results this option works with any of the options for providing a query, just replace \"search\" with \"searchWithCaching\" in your method chain and gain all the benefits of caching. The second parameter is optional and allows you to specify the cache options import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISearchQuery, SearchResults, SearchQueryBuilder } from \"@pnp/sp/search\"; sp.searchWithCaching({ Querytext: \"test\", RowLimit: 10, EnableInterleaving: true, } as ISearchQuery).then((r: SearchResults) => { console.log(r.ElapsedTime); console.log(r.RowCount); console.log(r.PrimarySearchResults); }); // use a query builder const builder = SearchQueryBuilder(\"test\").rowLimit(3); // supply a search query builder and caching options const results2 = await sp.searchWithCaching(builder, { key: \"mykey\", expiration: dateAdd(new Date(), \"month\", 1) }); console.log(results2.TotalRows); Paging with SearchResults.getPage \u00b6 Paging is controlled by a start row and page size parameter. You can specify both arguments in your initial query however you can use the getPage method to jump to any page. The second parameter page size is optional and will use the previous RowLimit or default to 10. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { SearchResults, SearchQueryBuilder } from \"@pnp/sp/search\"; // this will hold our current results let currentResults: SearchResults = null; let page = 1; // triggered on page load or through some other means function onStart() { // construct our query that will be used throughout the paging process, likely from user input const q = SearchQueryBuilder(\"test\").rowLimit(5); const results = await sp.search(q); currentResults = results; // set the current results page = 1; // reset page counter // update UI... } // triggered by an event async function next() { currentResults = await currentResults.getPage(++page); // update UI... } // triggered by an event async function prev() { currentResults = await currentResults.getPage(--page); // update UI... } SearchQueryBuilder \u00b6 The SearchQueryBuilder allows you to build your queries in a fluent manner. It also accepts constructor arguments for query text and a base query plain object, should you have a shared configuration for queries in an application you can define them once. The methods and properties match those on the SearchQuery interface. Boolean properties add the flag to the query while methods require that you supply one or more arguments. Also arguments supplied later in the chain will overwrite previous values. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { SearchQueryBuilder, SearchResults, ISearchQuery } from \"@pnp/sp/search\"; // basic usage let q = SearchQueryBuilder().text(\"test\").rowLimit(4).enablePhonetic; sp.search(q).then(h => { /* ... */ }); // provide a default query text at creation let q2 = SearchQueryBuilder(\"text\").rowLimit(4).enablePhonetic; const results: SearchResults = await sp.search(q2); // provide query text and a template for // shared settings across queries that can // be overwritten by individual builders const appSearchSettings: ISearchQuery = { EnablePhonetic: true, HiddenConstraints: \"reports\" }; let q3 = SearchQueryBuilder(\"test\", appSearchSettings).enableQueryRules; let q4 = SearchQueryBuilder(\"financial data\", appSearchSettings).enableSorting.enableStemming; const results2 = await sp.search(q3); const results3 = sp.search(q4); Search Suggest \u00b6 Search suggest works in much the same way as search, except against the suggest end point. It takes a string or a plain object that matches ISuggestQuery. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISuggestQuery, ISuggestResult } from \"@pnp/sp/search\"; const results = await sp.searchSuggest(\"test\"); const results2 = await sp.searchSuggest({ querytext: \"test\", count: 5, } as ISuggestQuery); Search Factory \u00b6 You can also configure a search or suggest query against any valid SP url using the factory methods. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { Search, Suggest } from \"@pnp/sp/search\"; // set the url for search const searcher = Search(\"https://mytenant.sharepoint.com/sites/dev\"); // this can accept any of the query types (text, ISearchQuery, or SearchQueryBuilder) const results = await searcher(\"test\"); // you can reuse the ISearch instance const results2 = await searcher(\"another query\"); // same process works for Suggest const suggester = Suggest(\"https://mytenant.sharepoint.com/sites/dev\"); const suggestions = await suggester({ querytext: \"test\" });","title":"Search"},{"location":"sp/search/#pnpspsearch","text":"Using search you can access content throughout your organization in a secure and consistent manner. The library provides support for searching and suggest - as well as some interfaces and helper classes to make building your queries and processing responses easier.","title":"@pnp/sp/search"},{"location":"sp/search/#search","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISearchQuery, SearchResults } from \"@pnp/sp/search\"; Preset: All import { sp, ISearchQuery, SearchResults } from \"@pnp/sp/presets/all\"; Search is accessed directly from the root sp object and can take either a string representing the query text, a plain object matching the ISearchQuery interface, or a SearchQueryBuilder instance. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISearchQuery, SearchResults, SearchQueryBuilder } from \"@pnp/sp/search\"; // text search using SharePoint default values for other parameters const results: SearchResults = await sp.search(\"test\"); console.log(results.ElapsedTime); console.log(results.RowCount); console.log(results.PrimarySearchResults); // define a search query object matching the ISearchQuery interface const results2: SearchResults = await sp.search({ Querytext: \"test\", RowLimit: 10, EnableInterleaving: true, }); console.log(results2.ElapsedTime); console.log(results2.RowCount); console.log(results2.PrimarySearchResults); // define a query using a builder const builder = SearchQueryBuilder(\"test\").rowLimit(10).enableInterleaving.enableQueryRules.processPersonalFavorites; const results3 = await sp.search(builder); console.log(results3.ElapsedTime); console.log(results3.RowCount); console.log(results3.PrimarySearchResults);","title":"Search"},{"location":"sp/search/#search-result-caching","text":"You can use the searchWithCaching method to enable cache support for your search results this option works with any of the options for providing a query, just replace \"search\" with \"searchWithCaching\" in your method chain and gain all the benefits of caching. The second parameter is optional and allows you to specify the cache options import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISearchQuery, SearchResults, SearchQueryBuilder } from \"@pnp/sp/search\"; sp.searchWithCaching({ Querytext: \"test\", RowLimit: 10, EnableInterleaving: true, } as ISearchQuery).then((r: SearchResults) => { console.log(r.ElapsedTime); console.log(r.RowCount); console.log(r.PrimarySearchResults); }); // use a query builder const builder = SearchQueryBuilder(\"test\").rowLimit(3); // supply a search query builder and caching options const results2 = await sp.searchWithCaching(builder, { key: \"mykey\", expiration: dateAdd(new Date(), \"month\", 1) }); console.log(results2.TotalRows);","title":"Search Result Caching"},{"location":"sp/search/#paging-with-searchresultsgetpage","text":"Paging is controlled by a start row and page size parameter. You can specify both arguments in your initial query however you can use the getPage method to jump to any page. The second parameter page size is optional and will use the previous RowLimit or default to 10. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { SearchResults, SearchQueryBuilder } from \"@pnp/sp/search\"; // this will hold our current results let currentResults: SearchResults = null; let page = 1; // triggered on page load or through some other means function onStart() { // construct our query that will be used throughout the paging process, likely from user input const q = SearchQueryBuilder(\"test\").rowLimit(5); const results = await sp.search(q); currentResults = results; // set the current results page = 1; // reset page counter // update UI... } // triggered by an event async function next() { currentResults = await currentResults.getPage(++page); // update UI... } // triggered by an event async function prev() { currentResults = await currentResults.getPage(--page); // update UI... }","title":"Paging with SearchResults.getPage"},{"location":"sp/search/#searchquerybuilder","text":"The SearchQueryBuilder allows you to build your queries in a fluent manner. It also accepts constructor arguments for query text and a base query plain object, should you have a shared configuration for queries in an application you can define them once. The methods and properties match those on the SearchQuery interface. Boolean properties add the flag to the query while methods require that you supply one or more arguments. Also arguments supplied later in the chain will overwrite previous values. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { SearchQueryBuilder, SearchResults, ISearchQuery } from \"@pnp/sp/search\"; // basic usage let q = SearchQueryBuilder().text(\"test\").rowLimit(4).enablePhonetic; sp.search(q).then(h => { /* ... */ }); // provide a default query text at creation let q2 = SearchQueryBuilder(\"text\").rowLimit(4).enablePhonetic; const results: SearchResults = await sp.search(q2); // provide query text and a template for // shared settings across queries that can // be overwritten by individual builders const appSearchSettings: ISearchQuery = { EnablePhonetic: true, HiddenConstraints: \"reports\" }; let q3 = SearchQueryBuilder(\"test\", appSearchSettings).enableQueryRules; let q4 = SearchQueryBuilder(\"financial data\", appSearchSettings).enableSorting.enableStemming; const results2 = await sp.search(q3); const results3 = sp.search(q4);","title":"SearchQueryBuilder"},{"location":"sp/search/#search-suggest","text":"Search suggest works in much the same way as search, except against the suggest end point. It takes a string or a plain object that matches ISuggestQuery. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISuggestQuery, ISuggestResult } from \"@pnp/sp/search\"; const results = await sp.searchSuggest(\"test\"); const results2 = await sp.searchSuggest({ querytext: \"test\", count: 5, } as ISuggestQuery);","title":"Search Suggest"},{"location":"sp/search/#search-factory","text":"You can also configure a search or suggest query against any valid SP url using the factory methods. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { Search, Suggest } from \"@pnp/sp/search\"; // set the url for search const searcher = Search(\"https://mytenant.sharepoint.com/sites/dev\"); // this can accept any of the query types (text, ISearchQuery, or SearchQueryBuilder) const results = await searcher(\"test\"); // you can reuse the ISearch instance const results2 = await searcher(\"another query\"); // same process works for Suggest const suggester = Suggest(\"https://mytenant.sharepoint.com/sites/dev\"); const suggestions = await suggester({ querytext: \"test\" });","title":"Search Factory"},{"location":"sp/security/","text":"@pnp/sp/security \u00b6 There are four levels where you can break inheritance and assign security: Site, Web, List, Item. All four of these objects share a common set of methods. Because of this we are showing in the examples below usage of these methods for an IList instance, but they apply across all four securable objects. In addition to the shared methods, some types have unique methods which are listed below. Site permissions are managed on the root web of the site collection. A Note on Selective Imports for Security \u00b6 Because the method are shared you can opt to import only the methods for one of the instances. import \"@pnp/sp/security/web\"; import \"@pnp/sp/security/list\"; import \"@pnp/sp/security/item\"; Possibly useful if you are trying to hyper-optimize for bundle size but it is just as easy to import the whole module: import \"@pnp/sp/security\"; Securable Methods \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/security/list\"; import \"@pnp/sp/site-users/web\"; import { IList } from \"@pnp/sp/lists\"; import { PermissionKind } from \"@pnp/sp/security\"; // ensure we have a list const ler = await sp.web.lists.ensure(\"SecurityTestingList\"); const list: IList = ler.list; // role assignments (see section below) await list.roleAssignments(); // data will represent one of the possible parents Site, Web, or List const data = await list.firstUniqueAncestorSecurableObject(); // getUserEffectivePermissions const users = await sp.web.siteUsers.top(1).select(\"LoginName\")(); const perms = await list.getUserEffectivePermissions(users[0].LoginName); // getCurrentUserEffectivePermissions const perms2 = list.getCurrentUserEffectivePermissions(); // userHasPermissions const v: boolean = list.userHasPermissions(users[0].LoginName, PermissionKind.AddListItems) // currentUserHasPermissions const v2: boolean = list.currentUserHasPermissions(PermissionKind.AddListItems) // breakRoleInheritance await list.breakRoleInheritance(); // copy existing permissions await list.breakRoleInheritance(true); // copy existing permissions and reset all child securables to the new permissions await list.breakRoleInheritance(true, true); // resetRoleInheritance await list.resetRoleInheritance(); Web Specific methods \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/security/web\"; // role definitions (see section below) const defs = await sp.web.roleDefinitions(); Role Assignments \u00b6 Allows you to list and manipulate the set of role assignments for the given securable. Again we show usage using list, but the examples apply to web and item as well. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/security/web\"; import \"@pnp/sp/site-users/web\"; import { IList } from \"@pnp/sp/lists\"; import { PermissionKind } from \"@pnp/sp/security\"; // ensure we have a list const ler = await sp.web.lists.ensure(\"SecurityTestingList\"); const list: IList = ler.list; // list role assignments const assignments = await list.roleAssignments(); // add a role assignment const defs = await sp.web.roleDefinitions(); const user = await sp.web.currentUser(); const r = await list.roleAssignments.add(user.Id, defs[0].Id); // remove a role assignment const ras = await list.roleAssignments(); // filter/find the role assignment you want to remove // here we just grab the first const ra = ras.find(v => true); const r = await list.roleAssignments.remove(ra.Id); // read role assignment info const info = await list.roleAssignments.getById(ra.Id)(); // get the groups const info2 = await list.roleAssignments.getById(ra.Id).groups(); // get the bindings const info3 = await list.roleAssignments.getById(ra.Id).bindings(); // delete a role assignment (same as remove) const ras = await list.roleAssignments(); // filter/find the role assignment you want to remove // here we just grab the first const ra = ras.find(v => true); // delete it await list.roleAssignments.getById(ra.Id).delete(); Role Definitions \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/security/web\"; // read role definitions const defs = await sp.web.roleDefinitions(); // get by id const def = await sp.web.roleDefinitions.getById(5)(); const def = await sp.web.roleDefinitions.getById(5).select(\"Name\", \"Order\")(); // get by name const def = await sp.web.roleDefinitions.getByName(\"Full Control\")(); const def = await sp.web.roleDefinitions.getByName(\"Full Control\").select(\"Name\", \"Order\")(); // get by type const def = await sp.web.roleDefinitions.getByName(5)(); const def = await sp.web.roleDefinitions.getByName(5).select(\"Name\", \"Order\")(); // add // name The new role definition's name // description The new role definition's description // order The order in which the role definition appears // basePermissions The permissions mask for this role definition const rdar = await sp.web.roleDefinitions.add(\"title\", \"description\", 99, { High: 1, Low: 2 }); // the following methods work on a single role def, you can use any of the three getBy methods, here we use getById as an example // delete await sp.web.roleDefinitions.getById(5).delete(); // update const res = sp.web.roleDefinitions.getById(5).update({ Name: \"New Name\" });","title":"Security"},{"location":"sp/security/#pnpspsecurity","text":"There are four levels where you can break inheritance and assign security: Site, Web, List, Item. All four of these objects share a common set of methods. Because of this we are showing in the examples below usage of these methods for an IList instance, but they apply across all four securable objects. In addition to the shared methods, some types have unique methods which are listed below. Site permissions are managed on the root web of the site collection.","title":"@pnp/sp/security"},{"location":"sp/security/#a-note-on-selective-imports-for-security","text":"Because the method are shared you can opt to import only the methods for one of the instances. import \"@pnp/sp/security/web\"; import \"@pnp/sp/security/list\"; import \"@pnp/sp/security/item\"; Possibly useful if you are trying to hyper-optimize for bundle size but it is just as easy to import the whole module: import \"@pnp/sp/security\";","title":"A Note on Selective Imports for Security"},{"location":"sp/security/#securable-methods","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/security/list\"; import \"@pnp/sp/site-users/web\"; import { IList } from \"@pnp/sp/lists\"; import { PermissionKind } from \"@pnp/sp/security\"; // ensure we have a list const ler = await sp.web.lists.ensure(\"SecurityTestingList\"); const list: IList = ler.list; // role assignments (see section below) await list.roleAssignments(); // data will represent one of the possible parents Site, Web, or List const data = await list.firstUniqueAncestorSecurableObject(); // getUserEffectivePermissions const users = await sp.web.siteUsers.top(1).select(\"LoginName\")(); const perms = await list.getUserEffectivePermissions(users[0].LoginName); // getCurrentUserEffectivePermissions const perms2 = list.getCurrentUserEffectivePermissions(); // userHasPermissions const v: boolean = list.userHasPermissions(users[0].LoginName, PermissionKind.AddListItems) // currentUserHasPermissions const v2: boolean = list.currentUserHasPermissions(PermissionKind.AddListItems) // breakRoleInheritance await list.breakRoleInheritance(); // copy existing permissions await list.breakRoleInheritance(true); // copy existing permissions and reset all child securables to the new permissions await list.breakRoleInheritance(true, true); // resetRoleInheritance await list.resetRoleInheritance();","title":"Securable Methods"},{"location":"sp/security/#web-specific-methods","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/security/web\"; // role definitions (see section below) const defs = await sp.web.roleDefinitions();","title":"Web Specific methods"},{"location":"sp/security/#role-assignments","text":"Allows you to list and manipulate the set of role assignments for the given securable. Again we show usage using list, but the examples apply to web and item as well. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/security/web\"; import \"@pnp/sp/site-users/web\"; import { IList } from \"@pnp/sp/lists\"; import { PermissionKind } from \"@pnp/sp/security\"; // ensure we have a list const ler = await sp.web.lists.ensure(\"SecurityTestingList\"); const list: IList = ler.list; // list role assignments const assignments = await list.roleAssignments(); // add a role assignment const defs = await sp.web.roleDefinitions(); const user = await sp.web.currentUser(); const r = await list.roleAssignments.add(user.Id, defs[0].Id); // remove a role assignment const ras = await list.roleAssignments(); // filter/find the role assignment you want to remove // here we just grab the first const ra = ras.find(v => true); const r = await list.roleAssignments.remove(ra.Id); // read role assignment info const info = await list.roleAssignments.getById(ra.Id)(); // get the groups const info2 = await list.roleAssignments.getById(ra.Id).groups(); // get the bindings const info3 = await list.roleAssignments.getById(ra.Id).bindings(); // delete a role assignment (same as remove) const ras = await list.roleAssignments(); // filter/find the role assignment you want to remove // here we just grab the first const ra = ras.find(v => true); // delete it await list.roleAssignments.getById(ra.Id).delete();","title":"Role Assignments"},{"location":"sp/security/#role-definitions","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/security/web\"; // read role definitions const defs = await sp.web.roleDefinitions(); // get by id const def = await sp.web.roleDefinitions.getById(5)(); const def = await sp.web.roleDefinitions.getById(5).select(\"Name\", \"Order\")(); // get by name const def = await sp.web.roleDefinitions.getByName(\"Full Control\")(); const def = await sp.web.roleDefinitions.getByName(\"Full Control\").select(\"Name\", \"Order\")(); // get by type const def = await sp.web.roleDefinitions.getByName(5)(); const def = await sp.web.roleDefinitions.getByName(5).select(\"Name\", \"Order\")(); // add // name The new role definition's name // description The new role definition's description // order The order in which the role definition appears // basePermissions The permissions mask for this role definition const rdar = await sp.web.roleDefinitions.add(\"title\", \"description\", 99, { High: 1, Low: 2 }); // the following methods work on a single role def, you can use any of the three getBy methods, here we use getById as an example // delete await sp.web.roleDefinitions.getById(5).delete(); // update const res = sp.web.roleDefinitions.getById(5).update({ Name: \"New Name\" });","title":"Role Definitions"},{"location":"sp/sharing/","text":"@pnp/sp/sharing \u00b6 Note: This API is still considered \"beta\" meaning it may change and some behaviors may differ across tenants by version. It is also supported only in SharePoint Online. One of the newer abilities in SharePoint is the ability to share webs, files, or folders with both internal and external folks. It is important to remember that these settings are managed at the tenant level and ? override anything you may supply as an argument to these methods. If you receive an InvalidOperationException when using these methods please check your tenant sharing settings to ensure sharing is not blocked before ?submitting an issue. Imports \u00b6 In previous versions of this library the sharing methods were part of the inheritance stack for SharePointQueryable objects. Starting with v2 this is no longer the case and they are now selectively importable. There are four objects within the SharePoint hierarchy that support sharing: Item, File, Folder, and Web. You can import the sharing methods for all of them, or for individual objects. Import All \u00b6 To import and attach the sharing methods to all four of the sharable types include all of the sharing sub module: import \"@pnp/sp/sharing\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; import { sp } from \"@pnp/sp\"; const user = await sp.web.siteUsers.getByEmail(\"user@site.com\")(); const r = await sp.web.shareWith(user.LoginName); Selective Import \u00b6 Import only the web's sharing methods into the library import \"@pnp/sp/sharing/web\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; import { sp } from \"@pnp/sp\"; const user = await sp.web.siteUsers.getByEmail(\"user@site.com\")(); const r = await sp.web.shareWith(user.LoginName); getShareLink \u00b6 Applies to: Item, Folder, File Creates a sharing link for the given resource with an optional expiration. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import { SharingLinkKind, IShareLinkResponse } from \"@pnp/sp/sharing\"; import { dateAdd } from \"@pnp/core\"; const result = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/folder1\").getShareLink(SharingLinkKind.AnonymousView); console.log(JSON.stringify(result, null, 2)); const result2 = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/folder1\").getShareLink(SharingLinkKind.AnonymousView, dateAdd(new Date(), \"day\", 5)); console.log(JSON.stringify(result2, null, 2)); shareWith \u00b6 Applies to: Item, Folder, File, Web Shares the given resource with the specified permissions (View or Edit) and optionally sends an email to the users. You can supply a single string for the loginnames parameter or an array of loginnames . The folder method takes an optional parameter \"shareEverything\" which determines if the shared permissions are pushed down to all items in the folder, even those with unique permissions. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/files/web\"; import { ISharingResult, SharingRole } from \"@pnp/sp/sharing\"; const result = await sp.web.shareWith(\"i:0#.f|membership|user@site.com\"); console.log(JSON.stringify(result, null, 2)); // Share and allow editing const result2 = await sp.web.shareWith(\"i:0#.f|membership|user@site.com\", SharingRole.Edit); console.log(JSON.stringify(result2, null, 2)); // share folder const result3 = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/folder1\").shareWith(\"i:0#.f|membership|user@site.com\"); // Share folder with edit permissions, and provide params for requireSignin and propagateAcl (apply to all children) await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").shareWith(\"i:0#.f|membership|user@site.com\", SharingRole.Edit, true, true); // Share a file await sp.web.getFileByServerRelativeUrl(\"/sites/dev/Shared Documents/test.txt\").shareWith(\"i:0#.f|membership|user@site.com\"); // Share a file with edit permissions await sp.web.getFileByServerRelativeUrl(\"/sites/dev/Shared Documents/test.txt\").shareWith(\"i:0#.f|membership|user@site.com\", SharingRole.Edit); shareObject & shareObjectRaw \u00b6 Applies to: Web Allows you to share any shareable object in a web by providing the appropriate parameters. These two methods differ in that shareObject will try and fix up your query based on the supplied parameters where shareObjectRaw will send your supplied json object directly to the server. The later method is provided for the greatest amount of flexibility. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import { ISharingResult, SharingRole } from \"@pnp/sp/sharing\"; // Share an object in this web const result = await sp.web.shareObject(\"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\", \"i:0#.f|membership|user@site.com\", SharingRole.View); // Share an object with all settings available await sp.web.shareObjectRaw({ url: \"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\", peoplePickerInput: [{ Key: \"i:0#.f|membership|user@site.com\" }], roleValue: \"role: 1973741327\", groupId: 0, propagateAcl: false, sendEmail: true, includeAnonymousLinkInEmail: false, emailSubject: \"subject\", emailBody: \"body\", useSimplifiedRoles: true, }); unshareObject \u00b6 Applies to: Web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import { ISharingResult } from \"@pnp/sp/sharing\"; const result = await sp.web.unshareObject(\"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\"); checkSharingPermissions \u00b6 Applies to: Item, Folder, File Checks Permissions on the list of Users and returns back role the users have on the Item. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing/folders\"; import \"@pnp/sp/folders/web\"; import { SharingEntityPermission } from \"@pnp/sp/sharing\"; // check the sharing permissions for a folder const perms = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").checkSharingPermissions([{ alias: \"i:0#.f|membership|user@site.com\" }]); getSharingInformation \u00b6 Applies to: Item, Folder, File Get Sharing Information. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { ISharingInformation } from \"@pnp/sp/sharing\"; // Get the sharing information for a folder const info = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").getSharingInformation(); getObjectSharingSettings \u00b6 Applies to: Item, Folder, File Gets the sharing settings import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { IObjectSharingSettings } from \"@pnp/sp/sharing\"; // Gets the sharing object settings const settings: IObjectSharingSettings = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").getObjectSharingSettings(); unshare \u00b6 Applies to: Item, Folder, File Unshares a given resource import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { ISharingResult } from \"@pnp/sp/sharing\"; const result: ISharingResult = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").unshare(); deleteSharingLinkByKind \u00b6 Applies to: Item, Folder, File import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { ISharingResult, SharingLinkKind } from \"@pnp/sp/sharing\"; const result: ISharingResult = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").deleteSharingLinkByKind(SharingLinkKind.AnonymousEdit); unshareLink \u00b6 Applies to: Item, Folder, File import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { SharingLinkKind } from \"@pnp/sp/sharing\"; await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").unshareLink(SharingLinkKind.AnonymousEdit); // specify the sharing link id if available await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").unshareLink(SharingLinkKind.AnonymousEdit, \"12345\");","title":"Sharing"},{"location":"sp/sharing/#pnpspsharing","text":"Note: This API is still considered \"beta\" meaning it may change and some behaviors may differ across tenants by version. It is also supported only in SharePoint Online. One of the newer abilities in SharePoint is the ability to share webs, files, or folders with both internal and external folks. It is important to remember that these settings are managed at the tenant level and ? override anything you may supply as an argument to these methods. If you receive an InvalidOperationException when using these methods please check your tenant sharing settings to ensure sharing is not blocked before ?submitting an issue.","title":"@pnp/sp/sharing"},{"location":"sp/sharing/#imports","text":"In previous versions of this library the sharing methods were part of the inheritance stack for SharePointQueryable objects. Starting with v2 this is no longer the case and they are now selectively importable. There are four objects within the SharePoint hierarchy that support sharing: Item, File, Folder, and Web. You can import the sharing methods for all of them, or for individual objects.","title":"Imports"},{"location":"sp/sharing/#import-all","text":"To import and attach the sharing methods to all four of the sharable types include all of the sharing sub module: import \"@pnp/sp/sharing\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; import { sp } from \"@pnp/sp\"; const user = await sp.web.siteUsers.getByEmail(\"user@site.com\")(); const r = await sp.web.shareWith(user.LoginName);","title":"Import All"},{"location":"sp/sharing/#selective-import","text":"Import only the web's sharing methods into the library import \"@pnp/sp/sharing/web\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; import { sp } from \"@pnp/sp\"; const user = await sp.web.siteUsers.getByEmail(\"user@site.com\")(); const r = await sp.web.shareWith(user.LoginName);","title":"Selective Import"},{"location":"sp/sharing/#getsharelink","text":"Applies to: Item, Folder, File Creates a sharing link for the given resource with an optional expiration. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import { SharingLinkKind, IShareLinkResponse } from \"@pnp/sp/sharing\"; import { dateAdd } from \"@pnp/core\"; const result = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/folder1\").getShareLink(SharingLinkKind.AnonymousView); console.log(JSON.stringify(result, null, 2)); const result2 = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/folder1\").getShareLink(SharingLinkKind.AnonymousView, dateAdd(new Date(), \"day\", 5)); console.log(JSON.stringify(result2, null, 2));","title":"getShareLink"},{"location":"sp/sharing/#sharewith","text":"Applies to: Item, Folder, File, Web Shares the given resource with the specified permissions (View or Edit) and optionally sends an email to the users. You can supply a single string for the loginnames parameter or an array of loginnames . The folder method takes an optional parameter \"shareEverything\" which determines if the shared permissions are pushed down to all items in the folder, even those with unique permissions. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/files/web\"; import { ISharingResult, SharingRole } from \"@pnp/sp/sharing\"; const result = await sp.web.shareWith(\"i:0#.f|membership|user@site.com\"); console.log(JSON.stringify(result, null, 2)); // Share and allow editing const result2 = await sp.web.shareWith(\"i:0#.f|membership|user@site.com\", SharingRole.Edit); console.log(JSON.stringify(result2, null, 2)); // share folder const result3 = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/folder1\").shareWith(\"i:0#.f|membership|user@site.com\"); // Share folder with edit permissions, and provide params for requireSignin and propagateAcl (apply to all children) await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").shareWith(\"i:0#.f|membership|user@site.com\", SharingRole.Edit, true, true); // Share a file await sp.web.getFileByServerRelativeUrl(\"/sites/dev/Shared Documents/test.txt\").shareWith(\"i:0#.f|membership|user@site.com\"); // Share a file with edit permissions await sp.web.getFileByServerRelativeUrl(\"/sites/dev/Shared Documents/test.txt\").shareWith(\"i:0#.f|membership|user@site.com\", SharingRole.Edit);","title":"shareWith"},{"location":"sp/sharing/#shareobject-shareobjectraw","text":"Applies to: Web Allows you to share any shareable object in a web by providing the appropriate parameters. These two methods differ in that shareObject will try and fix up your query based on the supplied parameters where shareObjectRaw will send your supplied json object directly to the server. The later method is provided for the greatest amount of flexibility. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import { ISharingResult, SharingRole } from \"@pnp/sp/sharing\"; // Share an object in this web const result = await sp.web.shareObject(\"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\", \"i:0#.f|membership|user@site.com\", SharingRole.View); // Share an object with all settings available await sp.web.shareObjectRaw({ url: \"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\", peoplePickerInput: [{ Key: \"i:0#.f|membership|user@site.com\" }], roleValue: \"role: 1973741327\", groupId: 0, propagateAcl: false, sendEmail: true, includeAnonymousLinkInEmail: false, emailSubject: \"subject\", emailBody: \"body\", useSimplifiedRoles: true, });","title":"shareObject & shareObjectRaw"},{"location":"sp/sharing/#unshareobject","text":"Applies to: Web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import { ISharingResult } from \"@pnp/sp/sharing\"; const result = await sp.web.unshareObject(\"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\");","title":"unshareObject"},{"location":"sp/sharing/#checksharingpermissions","text":"Applies to: Item, Folder, File Checks Permissions on the list of Users and returns back role the users have on the Item. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing/folders\"; import \"@pnp/sp/folders/web\"; import { SharingEntityPermission } from \"@pnp/sp/sharing\"; // check the sharing permissions for a folder const perms = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").checkSharingPermissions([{ alias: \"i:0#.f|membership|user@site.com\" }]);","title":"checkSharingPermissions"},{"location":"sp/sharing/#getsharinginformation","text":"Applies to: Item, Folder, File Get Sharing Information. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { ISharingInformation } from \"@pnp/sp/sharing\"; // Get the sharing information for a folder const info = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").getSharingInformation();","title":"getSharingInformation"},{"location":"sp/sharing/#getobjectsharingsettings","text":"Applies to: Item, Folder, File Gets the sharing settings import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { IObjectSharingSettings } from \"@pnp/sp/sharing\"; // Gets the sharing object settings const settings: IObjectSharingSettings = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").getObjectSharingSettings();","title":"getObjectSharingSettings"},{"location":"sp/sharing/#unshare","text":"Applies to: Item, Folder, File Unshares a given resource import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { ISharingResult } from \"@pnp/sp/sharing\"; const result: ISharingResult = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").unshare();","title":"unshare"},{"location":"sp/sharing/#deletesharinglinkbykind","text":"Applies to: Item, Folder, File import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { ISharingResult, SharingLinkKind } from \"@pnp/sp/sharing\"; const result: ISharingResult = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").deleteSharingLinkByKind(SharingLinkKind.AnonymousEdit);","title":"deleteSharingLinkByKind"},{"location":"sp/sharing/#unsharelink","text":"Applies to: Item, Folder, File import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { SharingLinkKind } from \"@pnp/sp/sharing\"; await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").unshareLink(SharingLinkKind.AnonymousEdit); // specify the sharing link id if available await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").unshareLink(SharingLinkKind.AnonymousEdit, \"12345\");","title":"unshareLink"},{"location":"sp/site-designs/","text":"@pnp/sp/site-designs \u00b6 You can create site designs to provide reusable lists, themes, layouts, pages, or custom actions so that your users can quickly build new SharePoint sites with the features they need. Check out SharePoint site design and site script overview for more information. Site Designs \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; Create a new site design \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // WebTemplate: 64 Team site template, 68 Communication site template const siteDesign = await sp.siteDesigns.createSiteDesign({ SiteScriptIds: [\"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\"], Title: \"SiteDesign001\", WebTemplate: \"64\", }); console.log(siteDesign.Title); Applying a site design to a site \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Limited to 30 actions in a site script, but runs synchronously await sp.siteDesigns.applySiteDesign(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\",\"https://contoso.sharepoint.com/sites/teamsite-pnpjs001\"); // Better use the following method for 300 actions in a site script const task = await sp.web.addSiteDesignTask(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); Retrieval \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Retrieving all site designs const allSiteDesigns = await sp.siteDesigns.getSiteDesigns(); console.log(`Total site designs: ${allSiteDesigns.length}`); // Retrieving a single site design by Id const siteDesign = await sp.siteDesigns.getSiteDesignMetadata(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); console.log(siteDesign.Title); Update and delete \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Update const updatedSiteDesign = await sp.siteDesigns.updateSiteDesign({ Id: \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", Title: \"SiteDesignUpdatedTitle001\" }); // Delete await sp.siteDesigns.deleteSiteDesign(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); Setting Rights/Permissions \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Get const rights = await sp.siteDesigns.getSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); console.log(rights.length > 0 ? rights[0].PrincipalName : \"\"); // Grant await sp.siteDesigns.grantSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", [\"user@contoso.onmicrosoft.com\"]); // Revoke await sp.siteDesigns.revokeSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", [\"user@contoso.onmicrosoft.com\"]); // Reset all view rights const rights = await sp.siteDesigns.getSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); await sp.siteDesigns.revokeSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", rights.map(u => u.PrincipalName)); Get a history of site designs that have run on a web \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; const runs = await sp.web.getSiteDesignRuns(); const runs2 = await sp.siteDesigns.getSiteDesignRun(\"https://TENANT.sharepoint.com/sites/mysite\"); // Get runs specific to a site design const runs3 = await sp.web.getSiteDesignRuns(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); const runs4 = await sp.siteDesigns.getSiteDesignRun(\"https://TENANT.sharepoint.com/sites/mysite\", \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); // For more information about the site script actions const runStatus = await sp.web.getSiteDesignRunStatus(runs[0].ID); const runStatus2 = await sp.siteDesigns.getSiteDesignRunStatus(\"https://TENANT.sharepoint.com/sites/mysite\", runs[0].ID);","title":"Site Designs"},{"location":"sp/site-designs/#pnpspsite-designs","text":"You can create site designs to provide reusable lists, themes, layouts, pages, or custom actions so that your users can quickly build new SharePoint sites with the features they need. Check out SharePoint site design and site script overview for more information.","title":"@pnp/sp/site-designs"},{"location":"sp/site-designs/#site-designs","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"Site Designs"},{"location":"sp/site-designs/#create-a-new-site-design","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // WebTemplate: 64 Team site template, 68 Communication site template const siteDesign = await sp.siteDesigns.createSiteDesign({ SiteScriptIds: [\"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\"], Title: \"SiteDesign001\", WebTemplate: \"64\", }); console.log(siteDesign.Title);","title":"Create a new site design"},{"location":"sp/site-designs/#applying-a-site-design-to-a-site","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Limited to 30 actions in a site script, but runs synchronously await sp.siteDesigns.applySiteDesign(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\",\"https://contoso.sharepoint.com/sites/teamsite-pnpjs001\"); // Better use the following method for 300 actions in a site script const task = await sp.web.addSiteDesignTask(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\");","title":"Applying a site design to a site"},{"location":"sp/site-designs/#retrieval","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Retrieving all site designs const allSiteDesigns = await sp.siteDesigns.getSiteDesigns(); console.log(`Total site designs: ${allSiteDesigns.length}`); // Retrieving a single site design by Id const siteDesign = await sp.siteDesigns.getSiteDesignMetadata(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); console.log(siteDesign.Title);","title":"Retrieval"},{"location":"sp/site-designs/#update-and-delete","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Update const updatedSiteDesign = await sp.siteDesigns.updateSiteDesign({ Id: \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", Title: \"SiteDesignUpdatedTitle001\" }); // Delete await sp.siteDesigns.deleteSiteDesign(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\");","title":"Update and delete"},{"location":"sp/site-designs/#setting-rightspermissions","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Get const rights = await sp.siteDesigns.getSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); console.log(rights.length > 0 ? rights[0].PrincipalName : \"\"); // Grant await sp.siteDesigns.grantSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", [\"user@contoso.onmicrosoft.com\"]); // Revoke await sp.siteDesigns.revokeSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", [\"user@contoso.onmicrosoft.com\"]); // Reset all view rights const rights = await sp.siteDesigns.getSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); await sp.siteDesigns.revokeSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", rights.map(u => u.PrincipalName));","title":"Setting Rights/Permissions"},{"location":"sp/site-designs/#get-a-history-of-site-designs-that-have-run-on-a-web","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; const runs = await sp.web.getSiteDesignRuns(); const runs2 = await sp.siteDesigns.getSiteDesignRun(\"https://TENANT.sharepoint.com/sites/mysite\"); // Get runs specific to a site design const runs3 = await sp.web.getSiteDesignRuns(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); const runs4 = await sp.siteDesigns.getSiteDesignRun(\"https://TENANT.sharepoint.com/sites/mysite\", \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); // For more information about the site script actions const runStatus = await sp.web.getSiteDesignRunStatus(runs[0].ID); const runStatus2 = await sp.siteDesigns.getSiteDesignRunStatus(\"https://TENANT.sharepoint.com/sites/mysite\", runs[0].ID);","title":"Get a history of site designs that have run on a web"},{"location":"sp/site-groups/","text":"@pnp/sp/site-groups \u00b6 The site groups module provides methods to manage groups for a sharepoint site. ISiteGroups \u00b6 Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; Preset: All import {sp, SiteGroups } from \"@pnp/sp/presets/all\"; Get all site groups \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // gets all site groups of the web const groups = await sp.web.siteGroups(); Get the associated groups of a web \u00b6 You can get the associated Owner, Member and Visitor groups of a web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // Gets the associated visitors group of a web const visitorGroup = await sp.web.associatedVisitorGroup(); // Gets the associated members group of a web const memberGroup = await sp.web.associatedMemberGroup(); // Gets the associated owners group of a web const ownerGroup = await sp.web.associatedOwnerGroup(); Create the default associated groups for a web \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // Breaks permission inheritance and creates the default associated groups for the web // Login name of the owner const owner1 = \"owner@example.onmicrosoft.com\"; // Specify true, the permissions should be copied from the current parent scope, else false const copyRoleAssignments = false; // Specify true to make all child securable objects inherit role assignments from the current object const clearSubScopes = true; await sp.web.createDefaultAssociatedGroups(\"PnP Site\", owner1, copyRoleAssignments, clearSubScopes); Create a new site group \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // Creates a new site group with the specified title await sp.web.siteGroups.add({\"Title\":\"new group name\"}); ISiteGroup \u00b6 Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; Preset: All import {sp, SiteGroups, SiteGroup } from \"@pnp/sp/presets/all\"; Getting and updating the groups of a sharepoint web \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; // get the group using a group id const groupID = 33; let grp = await sp.web.siteGroups.getById(groupID)(); // get the group using the group's name const groupName = \"ClassicTeam Visitors\"; grp = await sp.web.siteGroups.getByName(groupName)(); // update a group await sp.web.siteGroups.getById(groupID).update({\"Title\": \"New Group Title\"}); // delete a group from the site using group id await sp.web.siteGroups.removeById(groupID); // delete a group from the site using group name await sp.web.siteGroups.removeByLoginName(groupName); Getting all users of a group \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; // get all users of group const groupID = 7; const users = await sp.web.siteGroups.getById(groupID).users(); Updating the owner of a site group \u00b6 Unfortunately for now setting the owner of a group as another or same SharePoint group is currently unsupported in REST. Setting the owner as a user is supported. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; // Update the owner with a user id await sp.web.siteGroups.getById(7).setUserAsOwner(4);","title":"Site Groups"},{"location":"sp/site-groups/#pnpspsite-groups","text":"The site groups module provides methods to manage groups for a sharepoint site.","title":"@pnp/sp/site-groups"},{"location":"sp/site-groups/#isitegroups","text":"Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; Preset: All import {sp, SiteGroups } from \"@pnp/sp/presets/all\";","title":"ISiteGroups"},{"location":"sp/site-groups/#get-all-site-groups","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // gets all site groups of the web const groups = await sp.web.siteGroups();","title":"Get all site groups"},{"location":"sp/site-groups/#get-the-associated-groups-of-a-web","text":"You can get the associated Owner, Member and Visitor groups of a web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // Gets the associated visitors group of a web const visitorGroup = await sp.web.associatedVisitorGroup(); // Gets the associated members group of a web const memberGroup = await sp.web.associatedMemberGroup(); // Gets the associated owners group of a web const ownerGroup = await sp.web.associatedOwnerGroup();","title":"Get the associated groups of a web"},{"location":"sp/site-groups/#create-the-default-associated-groups-for-a-web","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // Breaks permission inheritance and creates the default associated groups for the web // Login name of the owner const owner1 = \"owner@example.onmicrosoft.com\"; // Specify true, the permissions should be copied from the current parent scope, else false const copyRoleAssignments = false; // Specify true to make all child securable objects inherit role assignments from the current object const clearSubScopes = true; await sp.web.createDefaultAssociatedGroups(\"PnP Site\", owner1, copyRoleAssignments, clearSubScopes);","title":"Create the default associated groups for a web"},{"location":"sp/site-groups/#create-a-new-site-group","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // Creates a new site group with the specified title await sp.web.siteGroups.add({\"Title\":\"new group name\"});","title":"Create a new site group"},{"location":"sp/site-groups/#isitegroup","text":"Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; Preset: All import {sp, SiteGroups, SiteGroup } from \"@pnp/sp/presets/all\";","title":"ISiteGroup"},{"location":"sp/site-groups/#getting-and-updating-the-groups-of-a-sharepoint-web","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; // get the group using a group id const groupID = 33; let grp = await sp.web.siteGroups.getById(groupID)(); // get the group using the group's name const groupName = \"ClassicTeam Visitors\"; grp = await sp.web.siteGroups.getByName(groupName)(); // update a group await sp.web.siteGroups.getById(groupID).update({\"Title\": \"New Group Title\"}); // delete a group from the site using group id await sp.web.siteGroups.removeById(groupID); // delete a group from the site using group name await sp.web.siteGroups.removeByLoginName(groupName);","title":"Getting and updating the groups of a sharepoint web"},{"location":"sp/site-groups/#getting-all-users-of-a-group","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; // get all users of group const groupID = 7; const users = await sp.web.siteGroups.getById(groupID).users();","title":"Getting all users of a group"},{"location":"sp/site-groups/#updating-the-owner-of-a-site-group","text":"Unfortunately for now setting the owner of a group as another or same SharePoint group is currently unsupported in REST. Setting the owner as a user is supported. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; // Update the owner with a user id await sp.web.siteGroups.getById(7).setUserAsOwner(4);","title":"Updating the owner of a site group"},{"location":"sp/site-scripts/","text":"@pnp/sp/site-scripts \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; Create a new site script \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; const sitescriptContent = { \"$schema\": \"schema.json\", \"actions\": [ { \"themeName\": \"Theme Name 123\", \"verb\": \"applyTheme\", }, ], \"bindata\": {}, \"version\": 1, }; const siteScript = await sp.siteScripts.createSiteScript(\"Title\", \"description\", sitescriptContent); console.log(siteScript.Title); Retrieval \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; // Retrieving all site scripts const allSiteScripts = await sp.siteScripts.getSiteScripts(); console.log(allSiteScripts.length > 0 ? allSiteScripts[0].Title : \"\"); // Retrieving a single site script by Id const siteScript = await sp.siteScripts.getSiteScriptMetadata(\"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\"); console.log(siteScript.Title); Update and delete \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; // Update const updatedSiteScript = await sp.siteScripts.updateSiteScript({ Id: \"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\", Title: \"New Title\" }); console.log(updatedSiteScript.Title); // Delete await sp.siteScripts.deleteSiteScript(\"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\"); Get site script from a list \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; // Using the absolute URL of the list const ss = await sp.siteScripts.getSiteScriptFromList(\"https://TENANT.sharepoint.com/Lists/mylist\"); // Using the PnPjs web object to fetch the site script from a specific list const ss2 = await sp.web.lists.getByTitle(\"mylist\").getSiteScript(); Get site script from a web \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; const extractInfo = { IncludeBranding: true, IncludeLinksToExportedItems: true, IncludeRegionalSettings: true, IncludeSiteExternalSharingCapability: true, IncludeTheme: true, IncludedLists: [\"Lists/MyList\"] }; const ss = await sp.siteScripts.getSiteScriptFromWeb(\"https://TENANT.sharepoint.com/sites/mysite\", extractInfo); // Using the PnPjs web object to fetch the site script from a specific web const ss2 = await sp.web.getSiteScript(extractInfo); Execute Site Script Action \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; const siteScript = \"your site script action...\"; const ss = await sp.siteScripts.executeSiteScriptAction(siteScript); Execute site script for a specific web \u00b6 import { sp } from \"@pnp/sp\"; import { SiteScripts } \"@pnp/sp/site-scripts\"; const siteScript = \"your site script action...\"; const scriptService = SiteScripts(\"https://absolute/url/to/web\"); const ss = await scriptService.executeSiteScriptAction(siteScript);","title":"Site Scripts"},{"location":"sp/site-scripts/#pnpspsite-scripts","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"@pnp/sp/site-scripts"},{"location":"sp/site-scripts/#create-a-new-site-script","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; const sitescriptContent = { \"$schema\": \"schema.json\", \"actions\": [ { \"themeName\": \"Theme Name 123\", \"verb\": \"applyTheme\", }, ], \"bindata\": {}, \"version\": 1, }; const siteScript = await sp.siteScripts.createSiteScript(\"Title\", \"description\", sitescriptContent); console.log(siteScript.Title);","title":"Create a new site script"},{"location":"sp/site-scripts/#retrieval","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; // Retrieving all site scripts const allSiteScripts = await sp.siteScripts.getSiteScripts(); console.log(allSiteScripts.length > 0 ? allSiteScripts[0].Title : \"\"); // Retrieving a single site script by Id const siteScript = await sp.siteScripts.getSiteScriptMetadata(\"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\"); console.log(siteScript.Title);","title":"Retrieval"},{"location":"sp/site-scripts/#update-and-delete","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; // Update const updatedSiteScript = await sp.siteScripts.updateSiteScript({ Id: \"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\", Title: \"New Title\" }); console.log(updatedSiteScript.Title); // Delete await sp.siteScripts.deleteSiteScript(\"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\");","title":"Update and delete"},{"location":"sp/site-scripts/#get-site-script-from-a-list","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; // Using the absolute URL of the list const ss = await sp.siteScripts.getSiteScriptFromList(\"https://TENANT.sharepoint.com/Lists/mylist\"); // Using the PnPjs web object to fetch the site script from a specific list const ss2 = await sp.web.lists.getByTitle(\"mylist\").getSiteScript();","title":"Get site script from a list"},{"location":"sp/site-scripts/#get-site-script-from-a-web","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; const extractInfo = { IncludeBranding: true, IncludeLinksToExportedItems: true, IncludeRegionalSettings: true, IncludeSiteExternalSharingCapability: true, IncludeTheme: true, IncludedLists: [\"Lists/MyList\"] }; const ss = await sp.siteScripts.getSiteScriptFromWeb(\"https://TENANT.sharepoint.com/sites/mysite\", extractInfo); // Using the PnPjs web object to fetch the site script from a specific web const ss2 = await sp.web.getSiteScript(extractInfo);","title":"Get site script from a web"},{"location":"sp/site-scripts/#execute-site-script-action","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; const siteScript = \"your site script action...\"; const ss = await sp.siteScripts.executeSiteScriptAction(siteScript);","title":"Execute Site Script Action"},{"location":"sp/site-scripts/#execute-site-script-for-a-specific-web","text":"import { sp } from \"@pnp/sp\"; import { SiteScripts } \"@pnp/sp/site-scripts\"; const siteScript = \"your site script action...\"; const scriptService = SiteScripts(\"https://absolute/url/to/web\"); const ss = await scriptService.executeSiteScriptAction(siteScript);","title":"Execute site script for a specific web"},{"location":"sp/site-users/","text":"@pnp/sp/site-users \u00b6 The site users module provides methods to manage users for a sharepoint site. ISiteUsers \u00b6 Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; Preset: All import {sp, SiteUsers } from \"@pnp/sp/presets/all\"; Get all site user \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const users = await sp.web.siteUsers(); Get Current user \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; let user = await sp.web.currentUser(); Get user by id \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const id = 6; user = await sp.web.getUserById(id); Ensure user \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const username = \"usernames@microsoft.com\"; result = await sp.web.ensureUser(username); ISiteUser \u00b6 Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; Preset: All import {sp, SiteUsers, SiteUser } from \"@pnp/sp/presets/all\"; Get user Groups \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; let groups = await sp.web.currentUser.groups(); Add user to Site collection \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const user = await sp.web.ensureUser(\"userLoginname\") const users = await sp.web.siteUsers; await users.add(user.data.LoginName); Get user \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; // get user object by id const user = await sp.web.siteUsers.getById(6); //get user object by Email const user = await sp.web.siteUsers.getByEmail(\"user@mail.com\"); //get user object by LoginName const user = await sp.web.siteUsers.getByLoginName(\"userLoginName\"); Update user \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; let userProps = await sp.web.currentUser(); userProps.Title = \"New title\"; await sp.web.currentUser.update(userProps); Remove user \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; // remove user by id await sp.web.siteUsers.removeById(6); // remove user by LoginName await sp.web.siteUsers.removeByLoginName(6); ISiteUserProps \u00b6 User properties: Property Name Type Description Email string Contains Site user email Id Number Contains Site user Id IsHiddenInUI Boolean Site user IsHiddenInUI IsShareByEmailGuestUser boolean Site user is external user IsSiteAdmin Boolean Describes if Site user Is Site Admin LoginName string Site user LoginName PrincipalType number Site user Principal type Title string Site user Title interface ISiteUserProps { /** * Contains Site user email * */ Email: string; /** * Contains Site user Id * */ Id: number; /** * Site user IsHiddenInUI * */ IsHiddenInUI: boolean; /** * Site user IsShareByEmailGuestUser * */ IsShareByEmailGuestUser: boolean; /** * Describes if Site user Is Site Admin * */ IsSiteAdmin: boolean; /** * Site user LoginName * */ LoginName: string; /** * Site user Principal type * */ PrincipalType: number | PrincipalType; /** * Site user Title * */ Title: string; }","title":"Site Users"},{"location":"sp/site-users/#pnpspsite-users","text":"The site users module provides methods to manage users for a sharepoint site.","title":"@pnp/sp/site-users"},{"location":"sp/site-users/#isiteusers","text":"Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; Preset: All import {sp, SiteUsers } from \"@pnp/sp/presets/all\";","title":"ISiteUsers"},{"location":"sp/site-users/#get-all-site-user","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const users = await sp.web.siteUsers();","title":"Get all site user"},{"location":"sp/site-users/#get-current-user","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; let user = await sp.web.currentUser();","title":"Get Current user"},{"location":"sp/site-users/#get-user-by-id","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const id = 6; user = await sp.web.getUserById(id);","title":"Get user by id"},{"location":"sp/site-users/#ensure-user","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const username = \"usernames@microsoft.com\"; result = await sp.web.ensureUser(username);","title":"Ensure user"},{"location":"sp/site-users/#isiteuser","text":"Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; Preset: All import {sp, SiteUsers, SiteUser } from \"@pnp/sp/presets/all\";","title":"ISiteUser"},{"location":"sp/site-users/#get-user-groups","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; let groups = await sp.web.currentUser.groups();","title":"Get user Groups"},{"location":"sp/site-users/#add-user-to-site-collection","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const user = await sp.web.ensureUser(\"userLoginname\") const users = await sp.web.siteUsers; await users.add(user.data.LoginName);","title":"Add user to Site collection"},{"location":"sp/site-users/#get-user","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; // get user object by id const user = await sp.web.siteUsers.getById(6); //get user object by Email const user = await sp.web.siteUsers.getByEmail(\"user@mail.com\"); //get user object by LoginName const user = await sp.web.siteUsers.getByLoginName(\"userLoginName\");","title":"Get user"},{"location":"sp/site-users/#update-user","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; let userProps = await sp.web.currentUser(); userProps.Title = \"New title\"; await sp.web.currentUser.update(userProps);","title":"Update user"},{"location":"sp/site-users/#remove-user","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; // remove user by id await sp.web.siteUsers.removeById(6); // remove user by LoginName await sp.web.siteUsers.removeByLoginName(6);","title":"Remove user"},{"location":"sp/site-users/#isiteuserprops","text":"User properties: Property Name Type Description Email string Contains Site user email Id Number Contains Site user Id IsHiddenInUI Boolean Site user IsHiddenInUI IsShareByEmailGuestUser boolean Site user is external user IsSiteAdmin Boolean Describes if Site user Is Site Admin LoginName string Site user LoginName PrincipalType number Site user Principal type Title string Site user Title interface ISiteUserProps { /** * Contains Site user email * */ Email: string; /** * Contains Site user Id * */ Id: number; /** * Site user IsHiddenInUI * */ IsHiddenInUI: boolean; /** * Site user IsShareByEmailGuestUser * */ IsShareByEmailGuestUser: boolean; /** * Describes if Site user Is Site Admin * */ IsSiteAdmin: boolean; /** * Site user LoginName * */ LoginName: string; /** * Site user Principal type * */ PrincipalType: number | PrincipalType; /** * Site user Title * */ Title: string; }","title":"ISiteUserProps"},{"location":"sp/sites/","text":"@pnp/sp/site - Site properties \u00b6 Site collection are one of the fundamental entry points while working with SharePoint. Sites serve as container for webs, lists, features and other entity types. Get context information for the current site collection \u00b6 Using the library, you can get the context information of the current site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import { IContextInfo } from \"@pnp/sp/sites\"; const oContext: IContextInfo = await sp.site.getContextInfo(); console.log(oContext.FormDigestValue); Get document libraries of a web \u00b6 Using the library, you can get a list of the document libraries present in the a given web. Note: Works only in SharePoint online import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import { IDocumentLibraryInformation } from \"@pnp/sp/sites\"; const docLibs: IDocumentLibraryInformation[] = await sp.site.getDocumentLibraries(\"https://tenant.sharepoint.com/sites/test/subsite\"); //we got the array of document library information docLibs.forEach((docLib: IDocumentLibraryInformation) => { // do something with each library }); Open Web By Id \u00b6 Because this method is a POST request you can chain off it directly. You will get back the full web properties in the data property of the return object. You can also chain directly off the returned Web instance on the web property. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const w = await sp.site.openWebById(\"111ca453-90f5-482e-a381-cee1ff383c9e\"); //we got all the data from the web as well console.log(w.data); // we can chain const w2 = await w.web.select(\"Title\")(); Get site collection url from page \u00b6 Using the library, you can get the site collection url by providing a page url import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const d: string = await sp.site.getWebUrlFromPageUrl(\"https://tenant.sharepoint.com/sites/test/Pages/test.aspx\"); console.log(d); //https://tenant.sharepoint.com/sites/test Access the root web \u00b6 There are two methods to access the root web. The first, using the rootWeb property, is best for directly accessing information about that web. If you want to chain multiple operations off of the web, better to use the getRootWeb method that will ensure the web instance is created using its own Url vs. \"_api/sites/rootweb\" which does not work for all operations. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; // use for rootweb information access const rootwebData = await sp.site.rootWeb(); // use for chaining const rootweb = await sp.site.getRootWeb(); const listData = await rootWeb.lists.getByTitle(\"MyList\")(); Create a modern communication site \u00b6 Note: Works only in SharePoint online Creates a modern communication site. Property Type Required Description Title string yes The title of the site to create. lcid number yes The default language to use for the site. shareByEmailEnabled boolean yes If set to true, it will enable sharing files via Email. By default it is set to false url string yes The fully qualified URL (e.g. https://yourtenant.sharepoint.com/sites/mysitecollection ) of the site. description string no The description of the communication site. classification string no The Site classification to use. For instance \"Contoso Classified\". See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information siteDesignId string no The Guid of the site design to be used. You can use the below default OOTB GUIDs: Topic: null Showcase: 6142d2a0-63a5-4ba0-aede-d9fefca2c767 Blank: f6cc5403-0d63-442e-96c0-285923709ffc hubSiteId string no The Guid of the already existing Hub site Owner string no Required when using app-only context. Owner principal name e.g. user@tenant.onmicrosoft.com import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const result = await sp.site.createCommunicationSite( \"Title\", 1033, true, \"https://tenant.sharepoint.com/sites/commSite\", \"Description\", \"HBI\", \"f6cc5403-0d63-442e-96c0-285923709ffc\", \"a00ec589-ea9f-4dba-a34e-67e78d41e509\", \"user@TENANT.onmicrosoft.com\"); Create from Props \u00b6 You may need to supply additional parameters such as WebTemplate, to do so please use the createCommunicationSiteFromProps method. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sites\"; // in this case you supply a single struct deinfing the creation props const result = await sp.site.createCommunicationSiteFromProps({ Owner: \"patrick@three18studios.com\", Title: \"A Test Site\", Url: \"https://{tenant}.sharepoint.com/sites/commsite2\", WebTemplate: \"STS#3\", }); Create a modern team site \u00b6 Note: Works only in SharePoint online. It wont work with App only tokens Creates a modern team site backed by O365 group. Property Type Required Description displayName string yes The title/displayName of the site to be created. alias string yes Alias of the underlying Office 365 Group. isPublic boolean yes Defines whether the Office 365 Group will be public (default), or private. lcid number yes The language to use for the site. If not specified will default to English (1033). description string no The description of the modern team site. classification string no The Site classification to use. For instance \"Contoso Classified\". See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information owners string array (string[]) no The Owners of the site to be created hubSiteId string no The Guid of the already existing Hub site siteDesignId string no The Guid of the site design to be used. You can use the below default OOTB GUIDs: Topic: null Showcase: 6142d2a0-63a5-4ba0-aede-d9fefca2c767 Blank: f6cc5403-0d63-442e-96c0-285923709ffc import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const result = await sp.site.createModernTeamSite( \"displayName\", \"alias\", true, 1033, \"description\", \"HBI\", [\"user1@tenant.onmicrosoft.com\",\"user2@tenant.onmicrosoft.com\",\"user3@tenant.onmicrosoft.com\"], \"a00ec589-ea9f-4dba-a34e-67e78d41e509\", \"f6cc5403-0d63-442e-96c0-285923709ffc\" ); console.log(d); Create from Props \u00b6 You may need to supply additional parameters, to do so please use the createModernTeamSiteFromProps method. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sites\"; // in this case you supply a single struct deinfing the creation props const result = await sp.site.createModernTeamSiteFromProps({ alias: \"JenniferGarner\", displayName: \"A Test Site\", owners: [\"patrick@three18studios.com\"], }); Delete a site collection \u00b6 Using the library, you can delete a specific site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import { Site } from \"@pnp/sp/sites\"; // Delete the current site await sp.site.delete(); // Specify which site to delete const siteUrl = \"https://tenant.sharepoint.com/sites/subsite\"; const site2 = Site(siteUrl); await site2.delete(); Check if a Site Collection Exists \u00b6 Using the library, you can check if a specific site collection exist or not on your tenant import { sp } from \"@pnp/sp\"; // Specify which site to verify const siteUrl = \"https://tenant.sharepoint.com/sites/subsite\"; const exists = sp.site.exists(siteUrl); console.log(exists);","title":"Sites"},{"location":"sp/sites/#pnpspsite-site-properties","text":"Site collection are one of the fundamental entry points while working with SharePoint. Sites serve as container for webs, lists, features and other entity types.","title":"@pnp/sp/site - Site properties"},{"location":"sp/sites/#get-context-information-for-the-current-site-collection","text":"Using the library, you can get the context information of the current site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import { IContextInfo } from \"@pnp/sp/sites\"; const oContext: IContextInfo = await sp.site.getContextInfo(); console.log(oContext.FormDigestValue);","title":"Get context information for the current site collection"},{"location":"sp/sites/#get-document-libraries-of-a-web","text":"Using the library, you can get a list of the document libraries present in the a given web. Note: Works only in SharePoint online import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import { IDocumentLibraryInformation } from \"@pnp/sp/sites\"; const docLibs: IDocumentLibraryInformation[] = await sp.site.getDocumentLibraries(\"https://tenant.sharepoint.com/sites/test/subsite\"); //we got the array of document library information docLibs.forEach((docLib: IDocumentLibraryInformation) => { // do something with each library });","title":"Get document libraries of a web"},{"location":"sp/sites/#open-web-by-id","text":"Because this method is a POST request you can chain off it directly. You will get back the full web properties in the data property of the return object. You can also chain directly off the returned Web instance on the web property. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const w = await sp.site.openWebById(\"111ca453-90f5-482e-a381-cee1ff383c9e\"); //we got all the data from the web as well console.log(w.data); // we can chain const w2 = await w.web.select(\"Title\")();","title":"Open Web By Id"},{"location":"sp/sites/#get-site-collection-url-from-page","text":"Using the library, you can get the site collection url by providing a page url import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const d: string = await sp.site.getWebUrlFromPageUrl(\"https://tenant.sharepoint.com/sites/test/Pages/test.aspx\"); console.log(d); //https://tenant.sharepoint.com/sites/test","title":"Get site collection url from page"},{"location":"sp/sites/#access-the-root-web","text":"There are two methods to access the root web. The first, using the rootWeb property, is best for directly accessing information about that web. If you want to chain multiple operations off of the web, better to use the getRootWeb method that will ensure the web instance is created using its own Url vs. \"_api/sites/rootweb\" which does not work for all operations. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; // use for rootweb information access const rootwebData = await sp.site.rootWeb(); // use for chaining const rootweb = await sp.site.getRootWeb(); const listData = await rootWeb.lists.getByTitle(\"MyList\")();","title":"Access the root web"},{"location":"sp/sites/#create-a-modern-communication-site","text":"Note: Works only in SharePoint online Creates a modern communication site. Property Type Required Description Title string yes The title of the site to create. lcid number yes The default language to use for the site. shareByEmailEnabled boolean yes If set to true, it will enable sharing files via Email. By default it is set to false url string yes The fully qualified URL (e.g. https://yourtenant.sharepoint.com/sites/mysitecollection ) of the site. description string no The description of the communication site. classification string no The Site classification to use. For instance \"Contoso Classified\". See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information siteDesignId string no The Guid of the site design to be used. You can use the below default OOTB GUIDs: Topic: null Showcase: 6142d2a0-63a5-4ba0-aede-d9fefca2c767 Blank: f6cc5403-0d63-442e-96c0-285923709ffc hubSiteId string no The Guid of the already existing Hub site Owner string no Required when using app-only context. Owner principal name e.g. user@tenant.onmicrosoft.com import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const result = await sp.site.createCommunicationSite( \"Title\", 1033, true, \"https://tenant.sharepoint.com/sites/commSite\", \"Description\", \"HBI\", \"f6cc5403-0d63-442e-96c0-285923709ffc\", \"a00ec589-ea9f-4dba-a34e-67e78d41e509\", \"user@TENANT.onmicrosoft.com\");","title":"Create a modern communication site"},{"location":"sp/sites/#create-from-props","text":"You may need to supply additional parameters such as WebTemplate, to do so please use the createCommunicationSiteFromProps method. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sites\"; // in this case you supply a single struct deinfing the creation props const result = await sp.site.createCommunicationSiteFromProps({ Owner: \"patrick@three18studios.com\", Title: \"A Test Site\", Url: \"https://{tenant}.sharepoint.com/sites/commsite2\", WebTemplate: \"STS#3\", });","title":"Create from Props"},{"location":"sp/sites/#create-a-modern-team-site","text":"Note: Works only in SharePoint online. It wont work with App only tokens Creates a modern team site backed by O365 group. Property Type Required Description displayName string yes The title/displayName of the site to be created. alias string yes Alias of the underlying Office 365 Group. isPublic boolean yes Defines whether the Office 365 Group will be public (default), or private. lcid number yes The language to use for the site. If not specified will default to English (1033). description string no The description of the modern team site. classification string no The Site classification to use. For instance \"Contoso Classified\". See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information owners string array (string[]) no The Owners of the site to be created hubSiteId string no The Guid of the already existing Hub site siteDesignId string no The Guid of the site design to be used. You can use the below default OOTB GUIDs: Topic: null Showcase: 6142d2a0-63a5-4ba0-aede-d9fefca2c767 Blank: f6cc5403-0d63-442e-96c0-285923709ffc import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const result = await sp.site.createModernTeamSite( \"displayName\", \"alias\", true, 1033, \"description\", \"HBI\", [\"user1@tenant.onmicrosoft.com\",\"user2@tenant.onmicrosoft.com\",\"user3@tenant.onmicrosoft.com\"], \"a00ec589-ea9f-4dba-a34e-67e78d41e509\", \"f6cc5403-0d63-442e-96c0-285923709ffc\" ); console.log(d);","title":"Create a modern team site"},{"location":"sp/sites/#create-from-props_1","text":"You may need to supply additional parameters, to do so please use the createModernTeamSiteFromProps method. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sites\"; // in this case you supply a single struct deinfing the creation props const result = await sp.site.createModernTeamSiteFromProps({ alias: \"JenniferGarner\", displayName: \"A Test Site\", owners: [\"patrick@three18studios.com\"], });","title":"Create from Props"},{"location":"sp/sites/#delete-a-site-collection","text":"Using the library, you can delete a specific site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import { Site } from \"@pnp/sp/sites\"; // Delete the current site await sp.site.delete(); // Specify which site to delete const siteUrl = \"https://tenant.sharepoint.com/sites/subsite\"; const site2 = Site(siteUrl); await site2.delete();","title":"Delete a site collection"},{"location":"sp/sites/#check-if-a-site-collection-exists","text":"Using the library, you can check if a specific site collection exist or not on your tenant import { sp } from \"@pnp/sp\"; // Specify which site to verify const siteUrl = \"https://tenant.sharepoint.com/sites/subsite\"; const exists = sp.site.exists(siteUrl); console.log(exists);","title":"Check if a Site Collection Exists"},{"location":"sp/social/","text":"@pnp/sp/ - social \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; The social API allows you to track followed sites, people, and docs. Note, many of these methods only work with the context of a logged in user, and not with app-only permissions. getFollowedSitesUri \u00b6 Gets a URI to a site that lists the current user's followed sites. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; const uri = await sp.social.getFollowedSitesUri(); getFollowedDocumentsUri \u00b6 Gets a URI to a site that lists the current user's followed documents. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; const uri = await sp.social.getFollowedDocumentsUri(); follow \u00b6 Makes the current user start following a user, document, site, or tag import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // follow a site const r1 = await sp.social.follow({ ActorType: SocialActorType.Site, ContentUri: \"htts://tenant.sharepoint.com/sites/site\", }); // follow a person const r2 = await sp.social.follow({ AccountName: \"i:0#.f|membership|person@tenant.com\", ActorType: SocialActorType.User, }); // follow a doc const r3 = await sp.social.follow({ ActorType: SocialActorType.Document, ContentUri: \"https://tenant.sharepoint.com/sites/dev/SitePages/Test.aspx\", }); // follow a tag // You need the tag GUID to start following a tag. // You can't get the GUID by using the REST service, but you can use the .NET client object model or the JavaScript object model. // See How to get a tag's GUID based on the tag's name by using the JavaScript object model. // https://docs.microsoft.com/en-us/sharepoint/dev/general-development/follow-content-in-sharepoint#bk_getTagGuid const r4 = await sp.social.follow({ ActorType: SocialActorType.Tag, TagGuid: \"19a4a484-c1dc-4bc5-8c93-bb96245ce928\", }); isFollowed \u00b6 Indicates whether the current user is following a specified user, document, site, or tag import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // pass the same social actor struct as shown in follow example for each type const r = await sp.social.isFollowed({ AccountName: \"i:0#.f|membership|person@tenant.com\", ActorType: SocialActorType.User, }); stopFollowing \u00b6 Makes the current user stop following a user, document, site, or tag import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // pass the same social actor struct as shown in follow example for each type const r = await sp.social.stopFollowing({ AccountName: \"i:0#.f|membership|person@tenant.com\", ActorType: SocialActorType.User, }); my \u00b6 get \u00b6 Gets this user's social information import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; const r = await sp.social.my(); followed \u00b6 Gets users, documents, sites, and tags that the current user is following based on the supplied flags. import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // get all the followed documents const r1 = await sp.social.my.followed(SocialActorTypes.Document); // get all the followed documents and sites const r2 = await sp.social.my.followed(SocialActorTypes.Document | SocialActorTypes.Site); // get all the followed sites updated in the last 24 hours const r3 = await sp.social.my.followed(SocialActorTypes.Site | SocialActorTypes.WithinLast24Hours); followedCount \u00b6 Works as followed but returns on the count of actors specified by the query import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // get the followed documents count const r = await sp.social.my.followedCount(SocialActorTypes.Document); followers \u00b6 Gets the users who are following the current user. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; // get the followed documents count const r = await sp.social.my.followers(); suggestions \u00b6 Gets users who the current user might want to follow. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; // get the followed documents count const r = await sp.social.my.suggestions();","title":"Social"},{"location":"sp/social/#pnpsp-social","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; The social API allows you to track followed sites, people, and docs. Note, many of these methods only work with the context of a logged in user, and not with app-only permissions.","title":"@pnp/sp/ - social"},{"location":"sp/social/#getfollowedsitesuri","text":"Gets a URI to a site that lists the current user's followed sites. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; const uri = await sp.social.getFollowedSitesUri();","title":"getFollowedSitesUri"},{"location":"sp/social/#getfolloweddocumentsuri","text":"Gets a URI to a site that lists the current user's followed documents. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; const uri = await sp.social.getFollowedDocumentsUri();","title":"getFollowedDocumentsUri"},{"location":"sp/social/#follow","text":"Makes the current user start following a user, document, site, or tag import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // follow a site const r1 = await sp.social.follow({ ActorType: SocialActorType.Site, ContentUri: \"htts://tenant.sharepoint.com/sites/site\", }); // follow a person const r2 = await sp.social.follow({ AccountName: \"i:0#.f|membership|person@tenant.com\", ActorType: SocialActorType.User, }); // follow a doc const r3 = await sp.social.follow({ ActorType: SocialActorType.Document, ContentUri: \"https://tenant.sharepoint.com/sites/dev/SitePages/Test.aspx\", }); // follow a tag // You need the tag GUID to start following a tag. // You can't get the GUID by using the REST service, but you can use the .NET client object model or the JavaScript object model. // See How to get a tag's GUID based on the tag's name by using the JavaScript object model. // https://docs.microsoft.com/en-us/sharepoint/dev/general-development/follow-content-in-sharepoint#bk_getTagGuid const r4 = await sp.social.follow({ ActorType: SocialActorType.Tag, TagGuid: \"19a4a484-c1dc-4bc5-8c93-bb96245ce928\", });","title":"follow"},{"location":"sp/social/#isfollowed","text":"Indicates whether the current user is following a specified user, document, site, or tag import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // pass the same social actor struct as shown in follow example for each type const r = await sp.social.isFollowed({ AccountName: \"i:0#.f|membership|person@tenant.com\", ActorType: SocialActorType.User, });","title":"isFollowed"},{"location":"sp/social/#stopfollowing","text":"Makes the current user stop following a user, document, site, or tag import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // pass the same social actor struct as shown in follow example for each type const r = await sp.social.stopFollowing({ AccountName: \"i:0#.f|membership|person@tenant.com\", ActorType: SocialActorType.User, });","title":"stopFollowing"},{"location":"sp/social/#my","text":"","title":"my"},{"location":"sp/social/#get","text":"Gets this user's social information import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; const r = await sp.social.my();","title":"get"},{"location":"sp/social/#followed","text":"Gets users, documents, sites, and tags that the current user is following based on the supplied flags. import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // get all the followed documents const r1 = await sp.social.my.followed(SocialActorTypes.Document); // get all the followed documents and sites const r2 = await sp.social.my.followed(SocialActorTypes.Document | SocialActorTypes.Site); // get all the followed sites updated in the last 24 hours const r3 = await sp.social.my.followed(SocialActorTypes.Site | SocialActorTypes.WithinLast24Hours);","title":"followed"},{"location":"sp/social/#followedcount","text":"Works as followed but returns on the count of actors specified by the query import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // get the followed documents count const r = await sp.social.my.followedCount(SocialActorTypes.Document);","title":"followedCount"},{"location":"sp/social/#followers","text":"Gets the users who are following the current user. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; // get the followed documents count const r = await sp.social.my.followers();","title":"followers"},{"location":"sp/social/#suggestions","text":"Gets users who the current user might want to follow. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; // get the followed documents count const r = await sp.social.my.suggestions();","title":"suggestions"},{"location":"sp/sp-utilities-utility/","text":"@pnp/sp/utilities \u00b6 Through the REST api you are able to call a subset of the SP.Utilities.Utility methods. We have explicitly defined some of these methods and provided a method to call any others in a generic manner. These methods are exposed on pnp.sp.utility and support batching and caching. sendEmail \u00b6 This methods allows you to send an email based on the supplied arguments. The method takes a single argument, a plain object defined by the EmailProperties interface (shown below). EmailProperties \u00b6 export interface EmailProperties { To: string[]; CC?: string[]; BCC?: string[]; Subject: string; Body: string; AdditionalHeaders?: TypedHash; From?: string; } Usage \u00b6 You must define the To, Subject, and Body values - the remaining are optional. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; import { IEmailProperties } from \"@pnp/sp/sputilities\"; const emailProps: IEmailProperties = { To: [\"user@site.com\"], CC: [\"user2@site.com\", \"user3@site.com\"], BCC: [\"user4@site.com\", \"user5@site.com\"], Subject: \"This email is about...\", Body: \"Here is the body. It supports html\", AdditionalHeaders: { \"content-type\": \"text/html\" } }; await sp.utility.sendEmail(emailProps); console.log(\"Email Sent!\"); getCurrentUserEmailAddresses \u00b6 This method returns the current user's email addresses known to SharePoint. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let addressString: string = await sp.utility.getCurrentUserEmailAddresses(); // and use it with sendEmail await sp.utility.sendEmail({ To: [addressString], Subject: \"This email is about...\", Body: \"Here is the body. It supports html\", AdditionalHeaders: { \"content-type\": \"text/html\" }, }); resolvePrincipal \u00b6 Gets information about a principal that matches the specified Search criteria import { sp, IPrincipalInfo, PrincipalType, PrincipalSource } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let principal : IPrincipalInfo = await sp.utility.resolvePrincipal(\"user@site.com\", PrincipalType.User, PrincipalSource.All, true, false, true); console.log(principal); searchPrincipals \u00b6 Gets information about the principals that match the specified Search criteria. import { sp, IPrincipalInfo, PrincipalType, PrincipalSource } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let principals : IPrincipalInfo[] = await sp.utility.searchPrincipals(\"john\", PrincipalType.User, PrincipalSource.All,\"\", 10); console.log(principals); createEmailBodyForInvitation \u00b6 Gets the external (outside the firewall) URL to a document or resource in a site. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let url : string = await sp.utility.createEmailBodyForInvitation(\"https://contoso.sharepoint.com/sites/dev/SitePages/DevHome.aspx\"); console.log(url); expandGroupsToPrincipals \u00b6 Resolves the principals contained within the supplied groups import { sp, IPrincipalInfo } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let principals : IPrincipalInfo[] = await sp.utility.expandGroupsToPrincipals([\"Dev Owners\", \"Dev Members\"]); console.log(principals); // optionally supply a max results count. Default is 30. let principals : IPrincipalInfo[] = await sp.utility.expandGroupsToPrincipals([\"Dev Owners\", \"Dev Members\"], 10); console.log(principals); createWikiPage \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; import { ICreateWikiPageResult } from \"@pnp/sp/sputilities\"; let newPage : ICreateWikiPageResult = await sp.utility.createWikiPage({ ServerRelativeUrl: \"/sites/dev/SitePages/mynewpage.aspx\", WikiHtmlContent: \"This is my page content. It supports rich html.\", }); // newPage contains the raw data returned by the service console.log(newPage.data); // newPage contains a File instance you can use to further update the new page let file = await newPage.file(); console.log(file);","title":"SP.Utilities.Utility"},{"location":"sp/sp-utilities-utility/#pnpsputilities","text":"Through the REST api you are able to call a subset of the SP.Utilities.Utility methods. We have explicitly defined some of these methods and provided a method to call any others in a generic manner. These methods are exposed on pnp.sp.utility and support batching and caching.","title":"@pnp/sp/utilities"},{"location":"sp/sp-utilities-utility/#sendemail","text":"This methods allows you to send an email based on the supplied arguments. The method takes a single argument, a plain object defined by the EmailProperties interface (shown below).","title":"sendEmail"},{"location":"sp/sp-utilities-utility/#emailproperties","text":"export interface EmailProperties { To: string[]; CC?: string[]; BCC?: string[]; Subject: string; Body: string; AdditionalHeaders?: TypedHash; From?: string; }","title":"EmailProperties"},{"location":"sp/sp-utilities-utility/#usage","text":"You must define the To, Subject, and Body values - the remaining are optional. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; import { IEmailProperties } from \"@pnp/sp/sputilities\"; const emailProps: IEmailProperties = { To: [\"user@site.com\"], CC: [\"user2@site.com\", \"user3@site.com\"], BCC: [\"user4@site.com\", \"user5@site.com\"], Subject: \"This email is about...\", Body: \"Here is the body. It supports html\", AdditionalHeaders: { \"content-type\": \"text/html\" } }; await sp.utility.sendEmail(emailProps); console.log(\"Email Sent!\");","title":"Usage"},{"location":"sp/sp-utilities-utility/#getcurrentuseremailaddresses","text":"This method returns the current user's email addresses known to SharePoint. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let addressString: string = await sp.utility.getCurrentUserEmailAddresses(); // and use it with sendEmail await sp.utility.sendEmail({ To: [addressString], Subject: \"This email is about...\", Body: \"Here is the body. It supports html\", AdditionalHeaders: { \"content-type\": \"text/html\" }, });","title":"getCurrentUserEmailAddresses"},{"location":"sp/sp-utilities-utility/#resolveprincipal","text":"Gets information about a principal that matches the specified Search criteria import { sp, IPrincipalInfo, PrincipalType, PrincipalSource } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let principal : IPrincipalInfo = await sp.utility.resolvePrincipal(\"user@site.com\", PrincipalType.User, PrincipalSource.All, true, false, true); console.log(principal);","title":"resolvePrincipal"},{"location":"sp/sp-utilities-utility/#searchprincipals","text":"Gets information about the principals that match the specified Search criteria. import { sp, IPrincipalInfo, PrincipalType, PrincipalSource } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let principals : IPrincipalInfo[] = await sp.utility.searchPrincipals(\"john\", PrincipalType.User, PrincipalSource.All,\"\", 10); console.log(principals);","title":"searchPrincipals"},{"location":"sp/sp-utilities-utility/#createemailbodyforinvitation","text":"Gets the external (outside the firewall) URL to a document or resource in a site. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let url : string = await sp.utility.createEmailBodyForInvitation(\"https://contoso.sharepoint.com/sites/dev/SitePages/DevHome.aspx\"); console.log(url);","title":"createEmailBodyForInvitation"},{"location":"sp/sp-utilities-utility/#expandgroupstoprincipals","text":"Resolves the principals contained within the supplied groups import { sp, IPrincipalInfo } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let principals : IPrincipalInfo[] = await sp.utility.expandGroupsToPrincipals([\"Dev Owners\", \"Dev Members\"]); console.log(principals); // optionally supply a max results count. Default is 30. let principals : IPrincipalInfo[] = await sp.utility.expandGroupsToPrincipals([\"Dev Owners\", \"Dev Members\"], 10); console.log(principals);","title":"expandGroupsToPrincipals"},{"location":"sp/sp-utilities-utility/#createwikipage","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; import { ICreateWikiPageResult } from \"@pnp/sp/sputilities\"; let newPage : ICreateWikiPageResult = await sp.utility.createWikiPage({ ServerRelativeUrl: \"/sites/dev/SitePages/mynewpage.aspx\", WikiHtmlContent: \"This is my page content. It supports rich html.\", }); // newPage contains the raw data returned by the service console.log(newPage.data); // newPage contains a File instance you can use to further update the new page let file = await newPage.file(); console.log(file);","title":"createWikiPage"},{"location":"sp/subscriptions/","text":"@pnp/sp/subscriptions \u00b6 Webhooks on a SharePoint list are used to notify any change in the list, to other applications using a push model. This module provides methods to add, update or delete webhooks on a particular SharePoint list or library. ISubscriptions \u00b6 Scenario Import Statement Selective import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { Subscriptions, ISubscriptions} from \"@pnp/sp/subscriptions\"; import \"@pnp/sp/subscriptions/list\" Preset: All import {sp, Webs, IWebs, Lists, ILists, Subscriptions, ISubscriptions, Subscription, ISubscription} from \"@pnp/sp/presets/all\"; Add a webhook \u00b6 Using this library, you can add a webhook to a specified list within the SharePoint site. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { Subscriptions, ISubscriptions} from \"@pnp/sp/subscriptions\"; import \"@pnp/sp/subscriptions/list\"; // This is the URL which will be called by SharePoint when there is a change in the list const notificationUrl = \"\"; // Set the expiry date to 180 days from now, which is the maximum allowed for the webhook expiry date. const expiryDate = dateAdd(new Date(), \"day\" , 180).toISOString(); // Adds a webhook to the Documents library var res = await sp.web.lists.getByTitle(\"Documents\").subscriptions.add(notificationUrl,expiryDate); Get all webhooks added to a list \u00b6 Read all the webhooks' details which are associated to the list import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/subscriptions\"; const res = await sp.web.lists.getByTitle(\"Documents\").subscriptions(); ISubscription \u00b6 This interface provides the methods for managing a particular webhook. Scenario Import Statement Selective import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { Subscriptions, ISubscriptions, Subscription, ISubscription} from \"@pnp/sp/subscriptions\"; import \"@pnp/sp/subscriptions/list\" Preset: All import { sp, Webs, IWebs, Lists, ILists, Subscriptions, ISubscriptions, Subscription, ISubscription } from \"@pnp/sp/presets/all\"; Managing a webhook \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/subscriptions\"; // Get details of a webhook based on its ID const webhookId = \"1f029e5c-16e4-4941-b46f-67895118763f\"; const webhook = await sp.web.lists.getByTitle(\"Documents\").subscriptions.getById(webhookId)(); // Update a webhook const newDate = dateAdd(new Date(), \"day\" , 150).toISOString(); const updatedWebhook = await sp.web.lists.getByTitle(\"Documents\").subscriptions.getById(webhookId).update(newDate); // Delete a webhook await sp.web.lists.getByTitle(\"Documents\").subscriptions.getById(webhookId).delete();","title":"Subscriptions"},{"location":"sp/subscriptions/#pnpspsubscriptions","text":"Webhooks on a SharePoint list are used to notify any change in the list, to other applications using a push model. This module provides methods to add, update or delete webhooks on a particular SharePoint list or library.","title":"@pnp/sp/subscriptions"},{"location":"sp/subscriptions/#isubscriptions","text":"Scenario Import Statement Selective import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { Subscriptions, ISubscriptions} from \"@pnp/sp/subscriptions\"; import \"@pnp/sp/subscriptions/list\" Preset: All import {sp, Webs, IWebs, Lists, ILists, Subscriptions, ISubscriptions, Subscription, ISubscription} from \"@pnp/sp/presets/all\";","title":"ISubscriptions"},{"location":"sp/subscriptions/#add-a-webhook","text":"Using this library, you can add a webhook to a specified list within the SharePoint site. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { Subscriptions, ISubscriptions} from \"@pnp/sp/subscriptions\"; import \"@pnp/sp/subscriptions/list\"; // This is the URL which will be called by SharePoint when there is a change in the list const notificationUrl = \"\"; // Set the expiry date to 180 days from now, which is the maximum allowed for the webhook expiry date. const expiryDate = dateAdd(new Date(), \"day\" , 180).toISOString(); // Adds a webhook to the Documents library var res = await sp.web.lists.getByTitle(\"Documents\").subscriptions.add(notificationUrl,expiryDate);","title":"Add a webhook"},{"location":"sp/subscriptions/#get-all-webhooks-added-to-a-list","text":"Read all the webhooks' details which are associated to the list import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/subscriptions\"; const res = await sp.web.lists.getByTitle(\"Documents\").subscriptions();","title":"Get all webhooks added to a list"},{"location":"sp/subscriptions/#isubscription","text":"This interface provides the methods for managing a particular webhook. Scenario Import Statement Selective import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { Subscriptions, ISubscriptions, Subscription, ISubscription} from \"@pnp/sp/subscriptions\"; import \"@pnp/sp/subscriptions/list\" Preset: All import { sp, Webs, IWebs, Lists, ILists, Subscriptions, ISubscriptions, Subscription, ISubscription } from \"@pnp/sp/presets/all\";","title":"ISubscription"},{"location":"sp/subscriptions/#managing-a-webhook","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/subscriptions\"; // Get details of a webhook based on its ID const webhookId = \"1f029e5c-16e4-4941-b46f-67895118763f\"; const webhook = await sp.web.lists.getByTitle(\"Documents\").subscriptions.getById(webhookId)(); // Update a webhook const newDate = dateAdd(new Date(), \"day\" , 150).toISOString(); const updatedWebhook = await sp.web.lists.getByTitle(\"Documents\").subscriptions.getById(webhookId).update(newDate); // Delete a webhook await sp.web.lists.getByTitle(\"Documents\").subscriptions.getById(webhookId).delete();","title":"Managing a webhook"},{"location":"sp/taxonomy/","text":"@pnp/sp/taxonomy \u00b6 Provides access to the v2.1 api term store Docs updated with v2.0.9 release as the underlying API changed. \u00b6 NOTE: This API may change so please be aware updates to the taxonomy module will not trigger a major version bump in PnPjs even if they are breaking. Once things stabalize this note will be removed. Term Store \u00b6 Access term store data from the root sp object as shown below. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermStoreInfo } from \"@pnp/sp/taxonomy\"; // get term store data const info: ITermStoreInfo = await sp.termStore(); Term Groups \u00b6 Access term group information List \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermGroupInfo } from \"@pnp/sp/taxonomy\"; // get term groups const info: ITermGroupInfo[] = await sp.termStore.groups(); Get By Id \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermGroupInfo } from \"@pnp/sp/taxonomy\"; // get term groups data const info: ITermGroupInfo = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\")(); Term Sets \u00b6 Access term set information List \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermSetInfo } from \"@pnp/sp/taxonomy\"; // get get set info const info: ITermSetInfo[] = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets(); Get By Id \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermSetInfo } from \"@pnp/sp/taxonomy\"; // get term set data const info: ITermSetInfo = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\")(); getAllChildrenAsOrderedTree \u00b6 Added in 2.0.13 This method will get all of a set's child terms in an ordered array. It is a costly method in terms of requests so we suggest you cache the results as taxonomy trees seldom change. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; import { dateAdd, PnPClientStorage } from \"@pnp/core\"; // here we get all the children of a given set const childTree = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").getAllChildrenAsOrderedTree(); // here we show caching the results using the PnPClientStorage class, there are many caching libraries and options available const store = new PnPClientStorage(); // our tree likely doesn't change much in 30 minutes for most applications // adjust to be longer or shorter as needed const cachedTree = await store.local.getOrPut(\"myKey\", () => { return sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").getAllChildrenAsOrderedTree(); }, dateAdd(new Date(), \"minute\", 30)); Terms \u00b6 Access term set information List \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; // list all the terms that are direct children of this set const infos: ITermInfo[] = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").children(); List (terms) \u00b6 Added in 2.0.13 You can use the terms property to get a flat list of all terms in the set. These terms do not contain parent/child relationship information. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; // list all the terms that are direct children of this set const infos: ITermInfo[] = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").terms(); Get By Id \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; // get term set data const info: ITermInfo = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").getTermById(\"338666a8-1111-2222-3333-f72471314e72\")(); Get Term Parent \u00b6 Behavior Change in 2.1.0 The server API changed again, resulting in the removal of the \"parent\" property from ITerm as it is not longer supported as a path property. You now must use \"expand\" to load a term's parent information. The side affect of this is that the parent is no longer chainable, meaning you need to load a new term instance to work with the parent term. An approach for this is shown below. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; // get a ref to the set const set = sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\"); // get a term's information and expand parent to get the parent info as well const w = await set.getTermById(\"338666a8-1111-2222-3333-f72471314e72\").expand(\"parent\")(); // get a ref to the parent term const parent = set.getTermById(w.parent.id); // make a request for the parent term's info - this data currently match the results in the expand call above, but this // is to demonstrate how to gain a ref to the parent and select its data const parentInfo = await parent.select(\"Id\", \"Descriptions\")();","title":"Taxonomy"},{"location":"sp/taxonomy/#pnpsptaxonomy","text":"Provides access to the v2.1 api term store","title":"@pnp/sp/taxonomy"},{"location":"sp/taxonomy/#docs-updated-with-v209-release-as-the-underlying-api-changed","text":"NOTE: This API may change so please be aware updates to the taxonomy module will not trigger a major version bump in PnPjs even if they are breaking. Once things stabalize this note will be removed.","title":"Docs updated with v2.0.9 release as the underlying API changed."},{"location":"sp/taxonomy/#term-store","text":"Access term store data from the root sp object as shown below. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermStoreInfo } from \"@pnp/sp/taxonomy\"; // get term store data const info: ITermStoreInfo = await sp.termStore();","title":"Term Store"},{"location":"sp/taxonomy/#term-groups","text":"Access term group information","title":"Term Groups"},{"location":"sp/taxonomy/#list","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermGroupInfo } from \"@pnp/sp/taxonomy\"; // get term groups const info: ITermGroupInfo[] = await sp.termStore.groups();","title":"List"},{"location":"sp/taxonomy/#get-by-id","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermGroupInfo } from \"@pnp/sp/taxonomy\"; // get term groups data const info: ITermGroupInfo = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\")();","title":"Get By Id"},{"location":"sp/taxonomy/#term-sets","text":"Access term set information","title":"Term Sets"},{"location":"sp/taxonomy/#list_1","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermSetInfo } from \"@pnp/sp/taxonomy\"; // get get set info const info: ITermSetInfo[] = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets();","title":"List"},{"location":"sp/taxonomy/#get-by-id_1","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermSetInfo } from \"@pnp/sp/taxonomy\"; // get term set data const info: ITermSetInfo = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\")();","title":"Get By Id"},{"location":"sp/taxonomy/#getallchildrenasorderedtree","text":"Added in 2.0.13 This method will get all of a set's child terms in an ordered array. It is a costly method in terms of requests so we suggest you cache the results as taxonomy trees seldom change. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; import { dateAdd, PnPClientStorage } from \"@pnp/core\"; // here we get all the children of a given set const childTree = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").getAllChildrenAsOrderedTree(); // here we show caching the results using the PnPClientStorage class, there are many caching libraries and options available const store = new PnPClientStorage(); // our tree likely doesn't change much in 30 minutes for most applications // adjust to be longer or shorter as needed const cachedTree = await store.local.getOrPut(\"myKey\", () => { return sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").getAllChildrenAsOrderedTree(); }, dateAdd(new Date(), \"minute\", 30));","title":"getAllChildrenAsOrderedTree"},{"location":"sp/taxonomy/#terms","text":"Access term set information","title":"Terms"},{"location":"sp/taxonomy/#list_2","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; // list all the terms that are direct children of this set const infos: ITermInfo[] = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").children();","title":"List"},{"location":"sp/taxonomy/#list-terms","text":"Added in 2.0.13 You can use the terms property to get a flat list of all terms in the set. These terms do not contain parent/child relationship information. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; // list all the terms that are direct children of this set const infos: ITermInfo[] = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").terms();","title":"List (terms)"},{"location":"sp/taxonomy/#get-by-id_2","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; // get term set data const info: ITermInfo = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").getTermById(\"338666a8-1111-2222-3333-f72471314e72\")();","title":"Get By Id"},{"location":"sp/taxonomy/#get-term-parent","text":"Behavior Change in 2.1.0 The server API changed again, resulting in the removal of the \"parent\" property from ITerm as it is not longer supported as a path property. You now must use \"expand\" to load a term's parent information. The side affect of this is that the parent is no longer chainable, meaning you need to load a new term instance to work with the parent term. An approach for this is shown below. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; // get a ref to the set const set = sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\"); // get a term's information and expand parent to get the parent info as well const w = await set.getTermById(\"338666a8-1111-2222-3333-f72471314e72\").expand(\"parent\")(); // get a ref to the parent term const parent = set.getTermById(w.parent.id); // make a request for the parent term's info - this data currently match the results in the expand call above, but this // is to demonstrate how to gain a ref to the parent and select its data const parentInfo = await parent.select(\"Id\", \"Descriptions\")();","title":"Get Term Parent"},{"location":"sp/tenant-properties/","text":"@pnp/sp/web - tenant properties \u00b6 You can set, read, and remove tenant properties using the methods shown below: setStorageEntity \u00b6 This method MUST be called in the context of the app catalog web or you will get an access denied message. import { Web } from \"@pnp/sp/webs\"; const w = Web(\"https://tenant.sharepoint.com/sites/appcatalog/\"); // specify required key and value await w.setStorageEntity(\"Test1\", \"Value 1\"); // specify optional description and comments await w.setStorageEntity(\"Test2\", \"Value 2\", \"description\", \"comments\"); getStorageEntity \u00b6 This method can be used from any web to retrieve values previously set. import { sp, IStorageEntity } from \"@pnp/sp/presets/all\"; const prop: IStorageEntity = await sp.web.getStorageEntity(\"Test1\"); console.log(prop.Value); removeStorageEntity \u00b6 This method MUST be called in the context of the app catalog web or you will get an access denied message. import { Web } from \"@pnp/sp/webs\"; const w = Web(\"https://tenant.sharepoint.com/sites/appcatalog/\"); await w.removeStorageEntity(\"Test1\");","title":"Tenant Properties"},{"location":"sp/tenant-properties/#pnpspweb-tenant-properties","text":"You can set, read, and remove tenant properties using the methods shown below:","title":"@pnp/sp/web - tenant properties"},{"location":"sp/tenant-properties/#setstorageentity","text":"This method MUST be called in the context of the app catalog web or you will get an access denied message. import { Web } from \"@pnp/sp/webs\"; const w = Web(\"https://tenant.sharepoint.com/sites/appcatalog/\"); // specify required key and value await w.setStorageEntity(\"Test1\", \"Value 1\"); // specify optional description and comments await w.setStorageEntity(\"Test2\", \"Value 2\", \"description\", \"comments\");","title":"setStorageEntity"},{"location":"sp/tenant-properties/#getstorageentity","text":"This method can be used from any web to retrieve values previously set. import { sp, IStorageEntity } from \"@pnp/sp/presets/all\"; const prop: IStorageEntity = await sp.web.getStorageEntity(\"Test1\"); console.log(prop.Value);","title":"getStorageEntity"},{"location":"sp/tenant-properties/#removestorageentity","text":"This method MUST be called in the context of the app catalog web or you will get an access denied message. import { Web } from \"@pnp/sp/webs\"; const w = Web(\"https://tenant.sharepoint.com/sites/appcatalog/\"); await w.removeStorageEntity(\"Test1\");","title":"removeStorageEntity"},{"location":"sp/user-custom-actions/","text":"@pnp/sp/user-custom-actions \u00b6 Represents a custom action associated with a SharePoint list, web or site collection. IUserCustomActions \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { IUserCustomActions, IUserCustomAction } from \"@pnp/sp/user-custom-actions\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/user-custom-actions\"; Preset: All import { sp, IUserCustomActions, IUserCustomAction } from \"@pnp/sp/presents/all\"; Get a collection of User Custom Actions from a web \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; const userCustomActions = sp.web.userCustomActions(); Add a new User Custom Action \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; import { IUserCustomActionAddResult } from '@pnp/sp/user-custom-actions'; const newValues: TypedHash = { \"Title\": \"New Title\", \"Description\": \"New Description\", \"Location\": \"ScriptLink\", \"ScriptSrc\": \"https://...\" }; const response : IUserCustomActionAddResult = await sp.web.userCustomActions.add(newValues); Get a User Custom Action by ID \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; const uca: IUserCustomAction = sp.web.userCustomActions.getById(\"00000000-0000-0000-0000-000000000000\"); const ucaData = await uca(); Clear the User Custom Action collection \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; // Site collection level await sp.site.userCustomActions.clear(); // Site (web) level await sp.web.userCustomActions.clear(); // List level await sp.web.lists.getByTitle(\"Documents\").userCustomActions.clear(); IUserCustomAction \u00b6 Update an existing User Custom Action \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; import { IUserCustomActionUpdateResult } from '@pnp/sp/user-custom-actions'; const uca = sp.web.userCustomActions.getById(\"00000000-0000-0000-0000-000000000000\"); const newValues: TypedHash = { \"Title\": \"New Title\", \"Description\": \"New Description\", \"ScriptSrc\": \"https://...\" }; const response: IUserCustomActionUpdateResult = uca.update(newValues);","title":"User custom actions"},{"location":"sp/user-custom-actions/#pnpspuser-custom-actions","text":"Represents a custom action associated with a SharePoint list, web or site collection.","title":"@pnp/sp/user-custom-actions"},{"location":"sp/user-custom-actions/#iusercustomactions","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { IUserCustomActions, IUserCustomAction } from \"@pnp/sp/user-custom-actions\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/user-custom-actions\"; Preset: All import { sp, IUserCustomActions, IUserCustomAction } from \"@pnp/sp/presents/all\";","title":"IUserCustomActions"},{"location":"sp/user-custom-actions/#get-a-collection-of-user-custom-actions-from-a-web","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; const userCustomActions = sp.web.userCustomActions();","title":"Get a collection of User Custom Actions from a web"},{"location":"sp/user-custom-actions/#add-a-new-user-custom-action","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; import { IUserCustomActionAddResult } from '@pnp/sp/user-custom-actions'; const newValues: TypedHash = { \"Title\": \"New Title\", \"Description\": \"New Description\", \"Location\": \"ScriptLink\", \"ScriptSrc\": \"https://...\" }; const response : IUserCustomActionAddResult = await sp.web.userCustomActions.add(newValues);","title":"Add a new User Custom Action"},{"location":"sp/user-custom-actions/#get-a-user-custom-action-by-id","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; const uca: IUserCustomAction = sp.web.userCustomActions.getById(\"00000000-0000-0000-0000-000000000000\"); const ucaData = await uca();","title":"Get a User Custom Action by ID"},{"location":"sp/user-custom-actions/#clear-the-user-custom-action-collection","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; // Site collection level await sp.site.userCustomActions.clear(); // Site (web) level await sp.web.userCustomActions.clear(); // List level await sp.web.lists.getByTitle(\"Documents\").userCustomActions.clear();","title":"Clear the User Custom Action collection"},{"location":"sp/user-custom-actions/#iusercustomaction","text":"","title":"IUserCustomAction"},{"location":"sp/user-custom-actions/#update-an-existing-user-custom-action","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; import { IUserCustomActionUpdateResult } from '@pnp/sp/user-custom-actions'; const uca = sp.web.userCustomActions.getById(\"00000000-0000-0000-0000-000000000000\"); const newValues: TypedHash = { \"Title\": \"New Title\", \"Description\": \"New Description\", \"ScriptSrc\": \"https://...\" }; const response: IUserCustomActionUpdateResult = uca.update(newValues);","title":"Update an existing User Custom Action"},{"location":"sp/views/","text":"@pnp/sp/views \u00b6 Views define the columns, ordering, and other details we see when we look at a list. You can have multiple views for a list, including private views - and one default view. IViews \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Views, IViews } from \"@pnp/sp/views\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/views\"; Preset: All import { sp, Views, IViews } from \"@pnp/sp/presets/all\"; Get views in a list \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); // get all the views and their properties const views1 = await list.views(); // you can use odata select operations to get just a set a fields const views2 = await list.views.select(\"Id\", \"Title\")(); // get the top three views const views3 = await list.views.top(3)(); Add a View \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); // create a new view with default fields and properties const result = await list.views.add(\"My New View\"); // create a new view with specific properties const result2 = await list.views.add(\"My New View 2\", false, { RowLimit: 10, ViewQuery: \"\", }); // manipulate the view's fields await result2.view.fields.removeAll(); await Promise.all([ result2.view.fields.add(\"Title\"), result2.view.fields.add(\"Modified\"), ]); IView \u00b6 Get a View's Information \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); const result = await list.views.getById(\"{GUID view id}\")(); const result2 = await list.views.getByTitle(\"My View\")(); const result3 = await list.views.getByTitle(\"My View\").select(\"Id\", \"Title\")(); const result4 = await list.defaultView(); const result5 = await list.getView(\"{GUID view id}\")(); fields \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); const result = await list.views.getById(\"{GUID view id}\").fields(); update \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); const result = await list.views.getById(\"{GUID view id}\").update({ RowLimit: 20, }); renderAsHtml \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const result = await sp.web.lists.getByTitle(\"My List\").views.getById(\"{GUID view id}\").renderAsHtml(); setViewXml \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const viewXml = \"...\"; await sp.web.lists.getByTitle(\"My List\").views.getById(\"{GUID view id}\").setViewXml(viewXml); delete \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const viewXml = \"...\"; await sp.web.lists.getByTitle(\"My List\").views.getById(\"{GUID view id}\").delete(); ViewFields \u00b6 getSchemaXml \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const xml = await sp.web.lists.getByTitle(\"My List\").defaultView.fields.getSchemaXml(); add \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.add(\"Created\"); move \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.move(\"Created\", 0); remove \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.remove(\"Created\"); removeAll \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.removeAll();","title":"Views"},{"location":"sp/views/#pnpspviews","text":"Views define the columns, ordering, and other details we see when we look at a list. You can have multiple views for a list, including private views - and one default view.","title":"@pnp/sp/views"},{"location":"sp/views/#iviews","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Views, IViews } from \"@pnp/sp/views\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/views\"; Preset: All import { sp, Views, IViews } from \"@pnp/sp/presets/all\";","title":"IViews"},{"location":"sp/views/#get-views-in-a-list","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); // get all the views and their properties const views1 = await list.views(); // you can use odata select operations to get just a set a fields const views2 = await list.views.select(\"Id\", \"Title\")(); // get the top three views const views3 = await list.views.top(3)();","title":"Get views in a list"},{"location":"sp/views/#add-a-view","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); // create a new view with default fields and properties const result = await list.views.add(\"My New View\"); // create a new view with specific properties const result2 = await list.views.add(\"My New View 2\", false, { RowLimit: 10, ViewQuery: \"\", }); // manipulate the view's fields await result2.view.fields.removeAll(); await Promise.all([ result2.view.fields.add(\"Title\"), result2.view.fields.add(\"Modified\"), ]);","title":"Add a View"},{"location":"sp/views/#iview","text":"","title":"IView"},{"location":"sp/views/#get-a-views-information","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); const result = await list.views.getById(\"{GUID view id}\")(); const result2 = await list.views.getByTitle(\"My View\")(); const result3 = await list.views.getByTitle(\"My View\").select(\"Id\", \"Title\")(); const result4 = await list.defaultView(); const result5 = await list.getView(\"{GUID view id}\")();","title":"Get a View's Information"},{"location":"sp/views/#fields","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); const result = await list.views.getById(\"{GUID view id}\").fields();","title":"fields"},{"location":"sp/views/#update","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); const result = await list.views.getById(\"{GUID view id}\").update({ RowLimit: 20, });","title":"update"},{"location":"sp/views/#renderashtml","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const result = await sp.web.lists.getByTitle(\"My List\").views.getById(\"{GUID view id}\").renderAsHtml();","title":"renderAsHtml"},{"location":"sp/views/#setviewxml","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const viewXml = \"...\"; await sp.web.lists.getByTitle(\"My List\").views.getById(\"{GUID view id}\").setViewXml(viewXml);","title":"setViewXml"},{"location":"sp/views/#delete","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const viewXml = \"...\"; await sp.web.lists.getByTitle(\"My List\").views.getById(\"{GUID view id}\").delete();","title":"delete"},{"location":"sp/views/#viewfields","text":"","title":"ViewFields"},{"location":"sp/views/#getschemaxml","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const xml = await sp.web.lists.getByTitle(\"My List\").defaultView.fields.getSchemaXml();","title":"getSchemaXml"},{"location":"sp/views/#add","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.add(\"Created\");","title":"add"},{"location":"sp/views/#move","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.move(\"Created\", 0);","title":"move"},{"location":"sp/views/#remove","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.remove(\"Created\");","title":"remove"},{"location":"sp/views/#removeall","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.removeAll();","title":"removeAll"},{"location":"sp/webs/","text":"@pnp/sp/webs \u00b6 Webs are one of the fundamental entry points when working with SharePoint. Webs serve as a container for lists, features, sub-webs, and all of the entity types. IWebs \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; Preset: All import { sp, Webs, IWebs } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Webs, IWebs } from \"@pnp/sp/presets/core\"; Add Web \u00b6 Using the library you can add a web to another web's collection of subwebs. The simplest usage requires only a title and url. This will result in a team site with all of the default settings. You can also provide other settings such as description, template, language, and inherit permissions. import { sp } from \"@pnp/sp\"; import { IWebAddResult } from \"@pnp/sp/webs\"; const result = await sp.web.webs.add(\"title\", \"subweb1\"); // show the response from the server when adding the web console.log(result.data); // we can immediately operate on the new web result.web.select(\"Title\")().then((w: IWebAddResult) => { // show our title console.log(w.Title); }); import { sp } from \"@pnp/sp\"; import { IWebAddResult } from \"@pnp/sp/webs\"; // create a German language wiki site with title, url, description, which does not inherit permissions sp.web.webs.add(\"wiki\", \"subweb2\", \"a wiki web\", \"WIKI#0\", 1031, false).then((w: IWebAddResult) => { // ... }); IWeb \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Web, IWeb } from \"@pnp/sp/webs\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; Preset: All import { sp, Web, IWeb } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Web, IWeb } from \"@pnp/sp/presets/core\"; Access a Web \u00b6 There are several ways to access a web instance, each of these methods is equivalent in that you will have an IWeb instance to work with. All of the examples below use a variable named \"web\" which represents an IWeb instance - regardless of how it was initially accessed. Access the web from the imported \"sp\" object using selective import: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; const r = await sp.web(); Access the web from the imported \"sp\" using the 'all' preset import { sp } from \"@pnp/sp/presets/all\"; const r = await sp.web(); Access the web from the imported \"sp\" using the 'core' preset import { sp } from \"@pnp/sp/presets/core\"; const r = await sp.web(); Create a web instance using the factory function import { Web } from \"@pnp/sp/webs\"; const web = Web(\"https://something.sharepoint.com/sites/dev\"); const r = await web(); webs \u00b6 Access the child webs collection of this web const webs = web.webs(); Get A Web's properties \u00b6 // basic get of the webs properties const props = await web(); // use odata operators to get specific fields const props2 = await web.select(\"Title\")(); // type the result to match what you are requesting const props3 = await web.select(\"Title\")<{ Title: string }>(); getParentWeb \u00b6 Get the data and IWeb instance for the parent web for the given web instance import { IOpenWebByIdResult } from \"@pnp/sp/sites\"; const web: IOpenWebByIdResult = web.getParentWeb(); getSubwebsFilteredForCurrentUser \u00b6 Returns a collection of objects that contain metadata about subsites of the current site in which the current user is a member. const subWebs = await web.getSubwebsFilteredForCurrentUser()(); // apply odata operations to the collection const subWebs2 = await sp.web.getSubwebsFilteredForCurrentUser().select(\"Title\", \"Language\").orderBy(\"Created\", true)(); Note: getSubwebsFilteredForCurrentUser returns IWebInfosData which is a subset of all the available fields on IWebInfo. allProperties \u00b6 Allows access to the web's all properties collection. This is readonly in REST. const props = await web.allProperties(); // select certain props const props2 = await web.allProperties.select(\"prop1\", \"prop2\")(); webinfos \u00b6 Gets a collection of WebInfos for this web's subwebs const infos = await web.webinfos(); // or select certain fields const infos2 = await web.webinfos.select(\"Title\", \"Description\")(); // or filter const infos3 = await web.webinfos.filter(\"Title eq 'MyWebTitle'\")(); // or both const infos4 = await web.webinfos.select(\"Title\", \"Description\").filter(\"Title eq 'MyWebTitle'\")(); // get the top 4 ordered by Title const infos5 = await web.webinfos.top(4).orderBy(\"Title\")(); Note: webinfos returns IWebInfosData which is a subset of all the available fields on IWebInfo. update \u00b6 Updates this web instance with the supplied properties // update the web's title and description const result = await web.update({ Title: \"New Title\", Description: \"My new description\", }); // a project implementation could wrap the update to provide type information for your expected fields: import { IWebUpdateResult } from \"@pnp/sp/webs\"; interface IWebUpdateProps { Title: string; Description: string; } function updateWeb(props: IWebUpdateProps): Promise { web.update(props); } Delete a Web \u00b6 await web.delete(); applyTheme \u00b6 Applies the theme specified by the contents of each of the files specified in the arguments to the site import { combine } from \"@pnp/core\"; // we are going to apply the theme to this sub web as an example const web = Web(\"https://{tenant}.sharepoint.com/sites/dev/subweb\"); // the urls to the color and font need to both be from the catalog at the root // these urls can be constants or calculated from existing urls const colorUrl = combine(\"/\", \"sites/dev\", \"_catalogs/theme/15/palette011.spcolor\"); // this gives us the same result const fontUrl = \"/sites/dev/_catalogs/theme/15/fontscheme007.spfont\"; // apply the font and color, no background image, and don't share this theme await web.applyTheme(colorUrl, fontUrl, \"\", false); applyWebTemplate & availableWebTemplates \u00b6 Applies the specified site definition or site template to the Web site that has no template applied to it. This is seldom used outside provisioning scenarios. const templates = (await web.availableWebTemplates().select(\"Name\")<{ Name: string }[]>()).filter(t => /ENTERWIKI#0/i.test(t.Name)); // apply the wiki template const template = templates.length > 0 ? templates[0].Name : \"STS#0\"; await web.applyWebTemplate(template); getChanges \u00b6 Returns the collection of changes from the change log that have occurred within the web, based on the specified query. // get the web changes including add, update, and delete const changes = await web.getChanges({ Add: true, ChangeTokenEnd: null, ChangeTokenStart: null, DeleteObject: true, Update: true, Web: true, }); mapToIcon \u00b6 Returns the name of the image file for the icon that is used to represent the specified file import { combine } from \"@pnp/core\"; const iconFileName = await web.mapToIcon(\"test.docx\"); // iconPath === \"icdocx.png\" // which you can need to map to a real url const iconFullPath = `https://{tenant}.sharepoint.com/sites/dev/_layouts/images/${iconFileName}`; // OR dynamically const webData = await sp.web.select(\"Url\")(); const iconFullPath2 = combine(webData.Url, \"_layouts\", \"images\", iconFileName); // OR within SPFx using the context const iconFullPath3 = combine(this.context.pageContext.web.absoluteUrl, \"_layouts\", \"images\", iconFileName); // You can also set size // 16x16 pixels = 0, 32x32 pixels = 1 const icon32FileName = await web.mapToIcon(\"test.docx\", 1); storage entities \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/appcatalog\"; import { IStorageEntity } from \"@pnp/sp/webs\"; // needs to be unique, GUIDs are great const key = \"my-storage-key\"; // read an existing entity const entity: IStorageEntity = await web.getStorageEntity(key); // setStorageEntity and removeStorageEntity must be called in the context of the tenant app catalog site // you can get the tenant app catalog using the getTenantAppCatalogWeb const tenantAppCatalogWeb = await sp.getTenantAppCatalogWeb(); tenantAppCatalogWeb.setStorageEntity(key, \"new value\"); // set other properties tenantAppCatalogWeb.setStorageEntity(key, \"another value\", \"description\", \"comments\"); const entity2: IStorageEntity = await web.getStorageEntity(key); /* entity2 === { Value: \"another value\", Comment: \"comments\"; Description: \"description\", }; */ // you can also remove a storage entity await tenantAppCatalogWeb.removeStorageEntity(key); appcatalog imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/appcatalog\"; Selective 2 import \"@pnp/sp/appcatalog/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; getAppCatalog \u00b6 Returns this web as an IAppCatalog instance or creates a new IAppCatalog instance from the provided url. import { IApp } from \"@pnp/sp/appcatalog\"; const appWeb = web.getAppCatalog(); // appWeb url === web url const app: IApp = appWeb.getAppById(\"{your app id}\"); const appWeb2 = web.getAppCatalog(\"https://tenant.sharepoing.com/sites/someappcatalog\"); // appWeb2 url === \"https://tenant.sharepoing.com/sites/someappcatalog\" client-side-pages imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/client-side-pages\"; Selective 2 import \"@pnp/sp/client-side-pages/web\"; Preset: All import { sp, Web, IWeb } from \"@pnp/sp/presets/all\"; You can create and load clientside page instances directly from a web. More details on working with clientside pages are available in the dedicated article. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; // simplest add a page example const page = await sp.web.addClientsidePage(\"mypage1\"); // simplest load a page example const page = await sp.web.loadClientsidePage(\"/sites/dev/sitepages/mypage3.aspx\"); content-type imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/content-types\"; Selective 2 import \"@pnp/sp/content-types/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; contentTypes \u00b6 Allows access to the collection of content types in this web. const cts = await web.contentTypes(); // you can also select fields and use other odata operators const cts2 = await web.contentTypes.select(\"Name\")(); features imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/features\"; Selective 2 import \"@pnp/sp/features/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; features \u00b6 Allows access to the collection of content types in this web. const features = await web.features(); fields imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/fields\"; Selective 2 import \"@pnp/sp/fields/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; fields \u00b6 Allows access to the collection of fields in this web. const fields = await web.fields(); files imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/files\"; Selective 2 import \"@pnp/sp/files/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; getFileByServerRelativeUrl \u00b6 Gets a file by server relative url import { IFile } from \"@pnp/sp/files\"; const file: IFile = web.getFileByServerRelativeUrl(\"/sites/dev/library/myfile.docx\"); getFileByServerRelativePath \u00b6 Gets a file by server relative url if your file name contains # and % characters import { IFile } from \"@pnp/sp/files\"; const file: IFile = web.getFileByServerRelativePath(\"/sites/dev/library/my # file%.docx\"); folders imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/folders\"; Selective 2 import \"@pnp/sp/folders/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; folders \u00b6 Gets the collection of folders in this web const folders = await web.folders(); // you can also filter and select as with any collection const folders2 = await web.folders.select(\"ServerRelativeUrl\", \"TimeLastModified\").filter(\"ItemCount gt 0\")(); // or get the most recently modified folder const folders2 = await web.folders.orderBy(\"TimeLastModified\").top(1)(); rootFolder \u00b6 Gets the root folder of the web const folder = await web.rootFolder(); getFolderByServerRelativeUrl \u00b6 Gets a folder by server relative url import { IFolder } from \"@pnp/sp/folders\"; const folder: IFolder = web.getFolderByServerRelativeUrl(\"/sites/dev/library\"); getFolderByServerRelativePath \u00b6 Gets a folder by server relative url if your folder name contains # and % characters import { IFolder } from \"@pnp/sp/folders\"; const folder: IFolder = web.getFolderByServerRelativePath(\"/sites/dev/library/my # folder%/\"); hubsites imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/hubsites\"; Selective 2 import \"@pnp/sp/hubsites/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; hubSiteData \u00b6 Gets hub site data for the current web import { IHubSiteWebData } from \"@pnp/sp/hubsites\"; // get the data and force a refresh const data: IHubSiteWebData = await web.hubSiteData(true); syncHubSiteTheme \u00b6 Applies theme updates from the parent hub site collection await web.syncHubSiteTheme(); lists imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/lists\"; Selective 2 import \"@pnp/sp/lists/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; Preset: Core import { sp } from \"@pnp/sp/presets/core\"; lists \u00b6 Gets the collection of all lists that are contained in the Web site import { ILists } from \"@pnp/sp/lists\"; const lists: ILists = web.lists; // you can always order the lists and select properties const data = await lists.select(\"Title\").orderBy(\"Title\")(); // and use other odata operators as well const data2 = await web.lists.top(3).orderBy(\"LastItemModifiedDate\")(); siteUserInfoList \u00b6 Gets the UserInfo list of the site collection that contains the Web site import { IList } from \"@pnp/sp/lists\"; const list: IList = web.siteUserInfoList; const data = await list(); // or chain off that list to get additional details const items = await list.items.top(2)(); defaultDocumentLibrary \u00b6 Get a reference the default documents library of a web import { IList } from \"@pnp/sp/lists\"; const list: IList = web.defaultDocumentLibrary; customListTemplates \u00b6 Gets the collection of all list definitions and list templates that are available import { IList } from \"@pnp/sp/lists\"; const templates = await web.customListTemplates(); // odata operators chain off the collection as expected const templates2 = await web.customListTemplates.select(\"Title\")(); getList \u00b6 Gets a list by server relative url (list's root folder) import { IList } from \"@pnp/sp/lists\"; const list: IList = web.getList(\"/sites/dev/lists/test\"); const listData = list(); getCatalog \u00b6 Returns the list gallery on the site Name Value WebTemplateCatalog 111 WebPartCatalog 113 ListTemplateCatalog 114 MasterPageCatalog 116 SolutionCatalog 121 ThemeCatalog 123 DesignCatalog 124 AppDataCatalog 125 import { IList } from \"@pnp/sp/lists\"; const templateCatalog: IList = await web.getCatalog(111); const themeCatalog: IList = await web.getCatalog(123); navigation imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/navigation\"; Selective 2 import \"@pnp/sp/navigation/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; navigation \u00b6 Gets a navigation object that represents navigation on the Web site, including the Quick Launch area and the top navigation bar import { INavigation } from \"@pnp/sp/navigation\"; const nav: INavigation = web.navigation; const navData = await nav(); regional-settings imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/regional-settings\"; Selective 2 import \"@pnp/sp/regional-settings/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; import { IRegionalSettings } from \"@pnp/sp/navigation\"; const settings: IRegionalSettings = web.regionalSettings; const settingsData = await settings(); related-items imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/related-items\"; Selective 2 import \"@pnp/sp/related-items/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; import { IRelatedItemManager, IRelatedItem } from \"@pnp/sp/related-items\"; const manager: IRelatedItemManager = web.relatedItems; const data: IRelatedItem[] = await manager.getRelatedItems(\"{list name}\", 4); security imports \u00b6 Please see information around the available security methods in the security article . sharing imports \u00b6 Please see information around the available sharing methods in the sharing article . site-groups imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/site-groups\"; Selective 2 import \"@pnp/sp/site-groups/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; siteGroups \u00b6 The site groups const groups = await web.siteGroups(); const groups2 = await web.siteGroups.top(2)(); associatedOwnerGroup \u00b6 The web's owner group const group = await web.associatedOwnerGroup(); const users = await web.associatedOwnerGroup.users(); associatedMemberGroup \u00b6 The web's member group const group = await web.associatedMemberGroup(); const users = await web.associatedMemberGroup.users(); associatedVisitorGroup \u00b6 The web's visitor group const group = await web.associatedVisitorGroup(); const users = await web.associatedVisitorGroup.users(); createDefaultAssociatedGroups \u00b6 Creates the default associated groups (Members, Owners, Visitors) and gives them the default permissions on the site. The target site must have unique permissions and no associated members / owners / visitors groups await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\"); // copy the role assignments await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\", true); // don't clear sub assignments await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\", false, false); // specify secondary owner, don't copy permissions, clear sub scopes await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\", false, true, \"{second owner login}\"); site-users imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/site-users\"; Selective 2 import \"@pnp/sp/site-users/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; siteUsers \u00b6 The site users const users = await web.siteUsers(); const users2 = await web.siteUsers.top(5)(); const users3 = await web.siteUsers.filter(`startswith(LoginName, '${encodeURIComponent(\"i:0#.f|m\")}')`)(); currentUser \u00b6 Information on the current user const user = await web.currentUser(); // check the login name of the current user const user2 = await web.currentUser.select(\"LoginName\")(); ensureUser \u00b6 Checks whether the specified login name belongs to a valid user in the web. If the user doesn't exist, adds the user to the web import { IWebEnsureUserResult } from \"@pnp/sp/site-users/\"; const result: IWebEnsureUserResult = await web.ensureUser(\"i:0#.f|membership|user@domain.onmicrosoft.com\"); getUserById \u00b6 Returns the user corresponding to the specified member identifier for the current web import { ISiteUser } from \"@pnp/sp/site-users/\"; const user: ISiteUser = web.getUserById(23); const userData = await user(); const userData2 = await user.select(\"LoginName\")(); user-custom-actions imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/user-custom-actions\"; Selective 2 import \"@pnp/sp/user-custom-actions/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; userCustomActions \u00b6 Gets a newly refreshed collection of the SPWeb's SPUserCustomActionCollection import { IUserCustomActions } from \"@pnp/sp/user-custom-actions\"; const actions: IUserCustomActions = web.userCustomActions; const actionsData = await actions(); IWebInfosData \u00b6 Some web operations return a subset of web information defined by the IWebInfosData interface, shown below. In those cases only these fields are available for select, orderby, and other odata operations. interface IWebInfosData { Configuration: number; Created: string; Description: string; Id: string; Language: number; LastItemModifiedDate: string; LastItemUserModifiedDate: string; ServerRelativeUrl: string; Title: string; WebTemplate: string; WebTemplateId: number; }","title":"Webs"},{"location":"sp/webs/#pnpspwebs","text":"Webs are one of the fundamental entry points when working with SharePoint. Webs serve as a container for lists, features, sub-webs, and all of the entity types.","title":"@pnp/sp/webs"},{"location":"sp/webs/#iwebs","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; Preset: All import { sp, Webs, IWebs } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Webs, IWebs } from \"@pnp/sp/presets/core\";","title":"IWebs"},{"location":"sp/webs/#add-web","text":"Using the library you can add a web to another web's collection of subwebs. The simplest usage requires only a title and url. This will result in a team site with all of the default settings. You can also provide other settings such as description, template, language, and inherit permissions. import { sp } from \"@pnp/sp\"; import { IWebAddResult } from \"@pnp/sp/webs\"; const result = await sp.web.webs.add(\"title\", \"subweb1\"); // show the response from the server when adding the web console.log(result.data); // we can immediately operate on the new web result.web.select(\"Title\")().then((w: IWebAddResult) => { // show our title console.log(w.Title); }); import { sp } from \"@pnp/sp\"; import { IWebAddResult } from \"@pnp/sp/webs\"; // create a German language wiki site with title, url, description, which does not inherit permissions sp.web.webs.add(\"wiki\", \"subweb2\", \"a wiki web\", \"WIKI#0\", 1031, false).then((w: IWebAddResult) => { // ... });","title":"Add Web"},{"location":"sp/webs/#iweb","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Web, IWeb } from \"@pnp/sp/webs\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; Preset: All import { sp, Web, IWeb } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Web, IWeb } from \"@pnp/sp/presets/core\";","title":"IWeb"},{"location":"sp/webs/#access-a-web","text":"There are several ways to access a web instance, each of these methods is equivalent in that you will have an IWeb instance to work with. All of the examples below use a variable named \"web\" which represents an IWeb instance - regardless of how it was initially accessed. Access the web from the imported \"sp\" object using selective import: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; const r = await sp.web(); Access the web from the imported \"sp\" using the 'all' preset import { sp } from \"@pnp/sp/presets/all\"; const r = await sp.web(); Access the web from the imported \"sp\" using the 'core' preset import { sp } from \"@pnp/sp/presets/core\"; const r = await sp.web(); Create a web instance using the factory function import { Web } from \"@pnp/sp/webs\"; const web = Web(\"https://something.sharepoint.com/sites/dev\"); const r = await web();","title":"Access a Web"},{"location":"sp/webs/#webs","text":"Access the child webs collection of this web const webs = web.webs();","title":"webs"},{"location":"sp/webs/#get-a-webs-properties","text":"// basic get of the webs properties const props = await web(); // use odata operators to get specific fields const props2 = await web.select(\"Title\")(); // type the result to match what you are requesting const props3 = await web.select(\"Title\")<{ Title: string }>();","title":"Get A Web's properties"},{"location":"sp/webs/#getparentweb","text":"Get the data and IWeb instance for the parent web for the given web instance import { IOpenWebByIdResult } from \"@pnp/sp/sites\"; const web: IOpenWebByIdResult = web.getParentWeb();","title":"getParentWeb"},{"location":"sp/webs/#getsubwebsfilteredforcurrentuser","text":"Returns a collection of objects that contain metadata about subsites of the current site in which the current user is a member. const subWebs = await web.getSubwebsFilteredForCurrentUser()(); // apply odata operations to the collection const subWebs2 = await sp.web.getSubwebsFilteredForCurrentUser().select(\"Title\", \"Language\").orderBy(\"Created\", true)(); Note: getSubwebsFilteredForCurrentUser returns IWebInfosData which is a subset of all the available fields on IWebInfo.","title":"getSubwebsFilteredForCurrentUser"},{"location":"sp/webs/#allproperties","text":"Allows access to the web's all properties collection. This is readonly in REST. const props = await web.allProperties(); // select certain props const props2 = await web.allProperties.select(\"prop1\", \"prop2\")();","title":"allProperties"},{"location":"sp/webs/#webinfos","text":"Gets a collection of WebInfos for this web's subwebs const infos = await web.webinfos(); // or select certain fields const infos2 = await web.webinfos.select(\"Title\", \"Description\")(); // or filter const infos3 = await web.webinfos.filter(\"Title eq 'MyWebTitle'\")(); // or both const infos4 = await web.webinfos.select(\"Title\", \"Description\").filter(\"Title eq 'MyWebTitle'\")(); // get the top 4 ordered by Title const infos5 = await web.webinfos.top(4).orderBy(\"Title\")(); Note: webinfos returns IWebInfosData which is a subset of all the available fields on IWebInfo.","title":"webinfos"},{"location":"sp/webs/#update","text":"Updates this web instance with the supplied properties // update the web's title and description const result = await web.update({ Title: \"New Title\", Description: \"My new description\", }); // a project implementation could wrap the update to provide type information for your expected fields: import { IWebUpdateResult } from \"@pnp/sp/webs\"; interface IWebUpdateProps { Title: string; Description: string; } function updateWeb(props: IWebUpdateProps): Promise { web.update(props); }","title":"update"},{"location":"sp/webs/#delete-a-web","text":"await web.delete();","title":"Delete a Web"},{"location":"sp/webs/#applytheme","text":"Applies the theme specified by the contents of each of the files specified in the arguments to the site import { combine } from \"@pnp/core\"; // we are going to apply the theme to this sub web as an example const web = Web(\"https://{tenant}.sharepoint.com/sites/dev/subweb\"); // the urls to the color and font need to both be from the catalog at the root // these urls can be constants or calculated from existing urls const colorUrl = combine(\"/\", \"sites/dev\", \"_catalogs/theme/15/palette011.spcolor\"); // this gives us the same result const fontUrl = \"/sites/dev/_catalogs/theme/15/fontscheme007.spfont\"; // apply the font and color, no background image, and don't share this theme await web.applyTheme(colorUrl, fontUrl, \"\", false);","title":"applyTheme"},{"location":"sp/webs/#applywebtemplate-availablewebtemplates","text":"Applies the specified site definition or site template to the Web site that has no template applied to it. This is seldom used outside provisioning scenarios. const templates = (await web.availableWebTemplates().select(\"Name\")<{ Name: string }[]>()).filter(t => /ENTERWIKI#0/i.test(t.Name)); // apply the wiki template const template = templates.length > 0 ? templates[0].Name : \"STS#0\"; await web.applyWebTemplate(template);","title":"applyWebTemplate & availableWebTemplates"},{"location":"sp/webs/#getchanges","text":"Returns the collection of changes from the change log that have occurred within the web, based on the specified query. // get the web changes including add, update, and delete const changes = await web.getChanges({ Add: true, ChangeTokenEnd: null, ChangeTokenStart: null, DeleteObject: true, Update: true, Web: true, });","title":"getChanges"},{"location":"sp/webs/#maptoicon","text":"Returns the name of the image file for the icon that is used to represent the specified file import { combine } from \"@pnp/core\"; const iconFileName = await web.mapToIcon(\"test.docx\"); // iconPath === \"icdocx.png\" // which you can need to map to a real url const iconFullPath = `https://{tenant}.sharepoint.com/sites/dev/_layouts/images/${iconFileName}`; // OR dynamically const webData = await sp.web.select(\"Url\")(); const iconFullPath2 = combine(webData.Url, \"_layouts\", \"images\", iconFileName); // OR within SPFx using the context const iconFullPath3 = combine(this.context.pageContext.web.absoluteUrl, \"_layouts\", \"images\", iconFileName); // You can also set size // 16x16 pixels = 0, 32x32 pixels = 1 const icon32FileName = await web.mapToIcon(\"test.docx\", 1);","title":"mapToIcon"},{"location":"sp/webs/#storage-entities","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/appcatalog\"; import { IStorageEntity } from \"@pnp/sp/webs\"; // needs to be unique, GUIDs are great const key = \"my-storage-key\"; // read an existing entity const entity: IStorageEntity = await web.getStorageEntity(key); // setStorageEntity and removeStorageEntity must be called in the context of the tenant app catalog site // you can get the tenant app catalog using the getTenantAppCatalogWeb const tenantAppCatalogWeb = await sp.getTenantAppCatalogWeb(); tenantAppCatalogWeb.setStorageEntity(key, \"new value\"); // set other properties tenantAppCatalogWeb.setStorageEntity(key, \"another value\", \"description\", \"comments\"); const entity2: IStorageEntity = await web.getStorageEntity(key); /* entity2 === { Value: \"another value\", Comment: \"comments\"; Description: \"description\", }; */ // you can also remove a storage entity await tenantAppCatalogWeb.removeStorageEntity(key);","title":"storage entities"},{"location":"sp/webs/#appcatalog-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/appcatalog\"; Selective 2 import \"@pnp/sp/appcatalog/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"appcatalog imports"},{"location":"sp/webs/#getappcatalog","text":"Returns this web as an IAppCatalog instance or creates a new IAppCatalog instance from the provided url. import { IApp } from \"@pnp/sp/appcatalog\"; const appWeb = web.getAppCatalog(); // appWeb url === web url const app: IApp = appWeb.getAppById(\"{your app id}\"); const appWeb2 = web.getAppCatalog(\"https://tenant.sharepoing.com/sites/someappcatalog\"); // appWeb2 url === \"https://tenant.sharepoing.com/sites/someappcatalog\"","title":"getAppCatalog"},{"location":"sp/webs/#client-side-pages-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/client-side-pages\"; Selective 2 import \"@pnp/sp/client-side-pages/web\"; Preset: All import { sp, Web, IWeb } from \"@pnp/sp/presets/all\"; You can create and load clientside page instances directly from a web. More details on working with clientside pages are available in the dedicated article. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; // simplest add a page example const page = await sp.web.addClientsidePage(\"mypage1\"); // simplest load a page example const page = await sp.web.loadClientsidePage(\"/sites/dev/sitepages/mypage3.aspx\");","title":"client-side-pages imports"},{"location":"sp/webs/#content-type-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/content-types\"; Selective 2 import \"@pnp/sp/content-types/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"content-type imports"},{"location":"sp/webs/#contenttypes","text":"Allows access to the collection of content types in this web. const cts = await web.contentTypes(); // you can also select fields and use other odata operators const cts2 = await web.contentTypes.select(\"Name\")();","title":"contentTypes"},{"location":"sp/webs/#features-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/features\"; Selective 2 import \"@pnp/sp/features/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"features imports"},{"location":"sp/webs/#features","text":"Allows access to the collection of content types in this web. const features = await web.features();","title":"features"},{"location":"sp/webs/#fields-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/fields\"; Selective 2 import \"@pnp/sp/fields/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"fields imports"},{"location":"sp/webs/#fields","text":"Allows access to the collection of fields in this web. const fields = await web.fields();","title":"fields"},{"location":"sp/webs/#files-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/files\"; Selective 2 import \"@pnp/sp/files/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"files imports"},{"location":"sp/webs/#getfilebyserverrelativeurl","text":"Gets a file by server relative url import { IFile } from \"@pnp/sp/files\"; const file: IFile = web.getFileByServerRelativeUrl(\"/sites/dev/library/myfile.docx\");","title":"getFileByServerRelativeUrl"},{"location":"sp/webs/#getfilebyserverrelativepath","text":"Gets a file by server relative url if your file name contains # and % characters import { IFile } from \"@pnp/sp/files\"; const file: IFile = web.getFileByServerRelativePath(\"/sites/dev/library/my # file%.docx\");","title":"getFileByServerRelativePath"},{"location":"sp/webs/#folders-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/folders\"; Selective 2 import \"@pnp/sp/folders/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"folders imports"},{"location":"sp/webs/#folders","text":"Gets the collection of folders in this web const folders = await web.folders(); // you can also filter and select as with any collection const folders2 = await web.folders.select(\"ServerRelativeUrl\", \"TimeLastModified\").filter(\"ItemCount gt 0\")(); // or get the most recently modified folder const folders2 = await web.folders.orderBy(\"TimeLastModified\").top(1)();","title":"folders"},{"location":"sp/webs/#rootfolder","text":"Gets the root folder of the web const folder = await web.rootFolder();","title":"rootFolder"},{"location":"sp/webs/#getfolderbyserverrelativeurl","text":"Gets a folder by server relative url import { IFolder } from \"@pnp/sp/folders\"; const folder: IFolder = web.getFolderByServerRelativeUrl(\"/sites/dev/library\");","title":"getFolderByServerRelativeUrl"},{"location":"sp/webs/#getfolderbyserverrelativepath","text":"Gets a folder by server relative url if your folder name contains # and % characters import { IFolder } from \"@pnp/sp/folders\"; const folder: IFolder = web.getFolderByServerRelativePath(\"/sites/dev/library/my # folder%/\");","title":"getFolderByServerRelativePath"},{"location":"sp/webs/#hubsites-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/hubsites\"; Selective 2 import \"@pnp/sp/hubsites/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"hubsites imports"},{"location":"sp/webs/#hubsitedata","text":"Gets hub site data for the current web import { IHubSiteWebData } from \"@pnp/sp/hubsites\"; // get the data and force a refresh const data: IHubSiteWebData = await web.hubSiteData(true);","title":"hubSiteData"},{"location":"sp/webs/#synchubsitetheme","text":"Applies theme updates from the parent hub site collection await web.syncHubSiteTheme();","title":"syncHubSiteTheme"},{"location":"sp/webs/#lists-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/lists\"; Selective 2 import \"@pnp/sp/lists/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; Preset: Core import { sp } from \"@pnp/sp/presets/core\";","title":"lists imports"},{"location":"sp/webs/#lists","text":"Gets the collection of all lists that are contained in the Web site import { ILists } from \"@pnp/sp/lists\"; const lists: ILists = web.lists; // you can always order the lists and select properties const data = await lists.select(\"Title\").orderBy(\"Title\")(); // and use other odata operators as well const data2 = await web.lists.top(3).orderBy(\"LastItemModifiedDate\")();","title":"lists"},{"location":"sp/webs/#siteuserinfolist","text":"Gets the UserInfo list of the site collection that contains the Web site import { IList } from \"@pnp/sp/lists\"; const list: IList = web.siteUserInfoList; const data = await list(); // or chain off that list to get additional details const items = await list.items.top(2)();","title":"siteUserInfoList"},{"location":"sp/webs/#defaultdocumentlibrary","text":"Get a reference the default documents library of a web import { IList } from \"@pnp/sp/lists\"; const list: IList = web.defaultDocumentLibrary;","title":"defaultDocumentLibrary"},{"location":"sp/webs/#customlisttemplates","text":"Gets the collection of all list definitions and list templates that are available import { IList } from \"@pnp/sp/lists\"; const templates = await web.customListTemplates(); // odata operators chain off the collection as expected const templates2 = await web.customListTemplates.select(\"Title\")();","title":"customListTemplates"},{"location":"sp/webs/#getlist","text":"Gets a list by server relative url (list's root folder) import { IList } from \"@pnp/sp/lists\"; const list: IList = web.getList(\"/sites/dev/lists/test\"); const listData = list();","title":"getList"},{"location":"sp/webs/#getcatalog","text":"Returns the list gallery on the site Name Value WebTemplateCatalog 111 WebPartCatalog 113 ListTemplateCatalog 114 MasterPageCatalog 116 SolutionCatalog 121 ThemeCatalog 123 DesignCatalog 124 AppDataCatalog 125 import { IList } from \"@pnp/sp/lists\"; const templateCatalog: IList = await web.getCatalog(111); const themeCatalog: IList = await web.getCatalog(123);","title":"getCatalog"},{"location":"sp/webs/#navigation-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/navigation\"; Selective 2 import \"@pnp/sp/navigation/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"navigation imports"},{"location":"sp/webs/#navigation","text":"Gets a navigation object that represents navigation on the Web site, including the Quick Launch area and the top navigation bar import { INavigation } from \"@pnp/sp/navigation\"; const nav: INavigation = web.navigation; const navData = await nav();","title":"navigation"},{"location":"sp/webs/#regional-settings-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/regional-settings\"; Selective 2 import \"@pnp/sp/regional-settings/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; import { IRegionalSettings } from \"@pnp/sp/navigation\"; const settings: IRegionalSettings = web.regionalSettings; const settingsData = await settings();","title":"regional-settings imports"},{"location":"sp/webs/#related-items-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/related-items\"; Selective 2 import \"@pnp/sp/related-items/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; import { IRelatedItemManager, IRelatedItem } from \"@pnp/sp/related-items\"; const manager: IRelatedItemManager = web.relatedItems; const data: IRelatedItem[] = await manager.getRelatedItems(\"{list name}\", 4);","title":"related-items imports"},{"location":"sp/webs/#security-imports","text":"Please see information around the available security methods in the security article .","title":"security imports"},{"location":"sp/webs/#sharing-imports","text":"Please see information around the available sharing methods in the sharing article .","title":"sharing imports"},{"location":"sp/webs/#site-groups-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/site-groups\"; Selective 2 import \"@pnp/sp/site-groups/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"site-groups imports"},{"location":"sp/webs/#sitegroups","text":"The site groups const groups = await web.siteGroups(); const groups2 = await web.siteGroups.top(2)();","title":"siteGroups"},{"location":"sp/webs/#associatedownergroup","text":"The web's owner group const group = await web.associatedOwnerGroup(); const users = await web.associatedOwnerGroup.users();","title":"associatedOwnerGroup"},{"location":"sp/webs/#associatedmembergroup","text":"The web's member group const group = await web.associatedMemberGroup(); const users = await web.associatedMemberGroup.users();","title":"associatedMemberGroup"},{"location":"sp/webs/#associatedvisitorgroup","text":"The web's visitor group const group = await web.associatedVisitorGroup(); const users = await web.associatedVisitorGroup.users();","title":"associatedVisitorGroup"},{"location":"sp/webs/#createdefaultassociatedgroups","text":"Creates the default associated groups (Members, Owners, Visitors) and gives them the default permissions on the site. The target site must have unique permissions and no associated members / owners / visitors groups await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\"); // copy the role assignments await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\", true); // don't clear sub assignments await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\", false, false); // specify secondary owner, don't copy permissions, clear sub scopes await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\", false, true, \"{second owner login}\");","title":"createDefaultAssociatedGroups"},{"location":"sp/webs/#site-users-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/site-users\"; Selective 2 import \"@pnp/sp/site-users/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"site-users imports"},{"location":"sp/webs/#siteusers","text":"The site users const users = await web.siteUsers(); const users2 = await web.siteUsers.top(5)(); const users3 = await web.siteUsers.filter(`startswith(LoginName, '${encodeURIComponent(\"i:0#.f|m\")}')`)();","title":"siteUsers"},{"location":"sp/webs/#currentuser","text":"Information on the current user const user = await web.currentUser(); // check the login name of the current user const user2 = await web.currentUser.select(\"LoginName\")();","title":"currentUser"},{"location":"sp/webs/#ensureuser","text":"Checks whether the specified login name belongs to a valid user in the web. If the user doesn't exist, adds the user to the web import { IWebEnsureUserResult } from \"@pnp/sp/site-users/\"; const result: IWebEnsureUserResult = await web.ensureUser(\"i:0#.f|membership|user@domain.onmicrosoft.com\");","title":"ensureUser"},{"location":"sp/webs/#getuserbyid","text":"Returns the user corresponding to the specified member identifier for the current web import { ISiteUser } from \"@pnp/sp/site-users/\"; const user: ISiteUser = web.getUserById(23); const userData = await user(); const userData2 = await user.select(\"LoginName\")();","title":"getUserById"},{"location":"sp/webs/#user-custom-actions-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/user-custom-actions\"; Selective 2 import \"@pnp/sp/user-custom-actions/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"user-custom-actions imports"},{"location":"sp/webs/#usercustomactions","text":"Gets a newly refreshed collection of the SPWeb's SPUserCustomActionCollection import { IUserCustomActions } from \"@pnp/sp/user-custom-actions\"; const actions: IUserCustomActions = web.userCustomActions; const actionsData = await actions();","title":"userCustomActions"},{"location":"sp/webs/#iwebinfosdata","text":"Some web operations return a subset of web information defined by the IWebInfosData interface, shown below. In those cases only these fields are available for select, orderby, and other odata operations. interface IWebInfosData { Configuration: number; Created: string; Description: string; Id: string; Language: number; LastItemModifiedDate: string; LastItemUserModifiedDate: string; ServerRelativeUrl: string; Title: string; WebTemplate: string; WebTemplateId: number; }","title":"IWebInfosData"},{"location":"sp-addinhelpers/","text":"@pnp/sp-addinhelpers \u00b6 This module contains classes to allow use of the libraries within a SharePoint add-in. Getting Started \u00b6 Install the library and all dependencies, npm install @pnp/sp @pnp/sp-addinhelpers --save Now you can make requests to the host web from your add-in using the crossDomainWeb method. // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp, SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\"; // this only needs to be done once within your application sp.setup({ sp: { fetchClientFactory: () => { return new SPRequestExecutorClient(); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\"; const hostWebUrl = \"{The host web url, likely from the query string}\"; // make requests into the host web via the SP.RequestExecutor const w = await sp.crossDomainWeb(addInWenUrl, hostWebUrl)(); console.log(JSON.stringify(w, null, 4)); Library Topics \u00b6 SPRequestExecutorClient SPRestAddIn","title":"sp-addinhelpers"},{"location":"sp-addinhelpers/#pnpsp-addinhelpers","text":"This module contains classes to allow use of the libraries within a SharePoint add-in.","title":"@pnp/sp-addinhelpers"},{"location":"sp-addinhelpers/#getting-started","text":"Install the library and all dependencies, npm install @pnp/sp @pnp/sp-addinhelpers --save Now you can make requests to the host web from your add-in using the crossDomainWeb method. // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp, SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\"; // this only needs to be done once within your application sp.setup({ sp: { fetchClientFactory: () => { return new SPRequestExecutorClient(); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\"; const hostWebUrl = \"{The host web url, likely from the query string}\"; // make requests into the host web via the SP.RequestExecutor const w = await sp.crossDomainWeb(addInWenUrl, hostWebUrl)(); console.log(JSON.stringify(w, null, 4));","title":"Getting Started"},{"location":"sp-addinhelpers/#library-topics","text":"SPRequestExecutorClient SPRestAddIn","title":"Library Topics"},{"location":"sp-addinhelpers/sp-request-executor-client/","text":"@pnp/sp-addinhelpers/sprequestexecutorclient \u00b6 The SPRequestExecutorClient is an implementation of the HttpClientImpl interface that facilitates requests to SharePoint from an add-in. It relies on the SharePoint SP product libraries being present to allow use of the SP.RequestExecutor to make the request. Setup \u00b6 To use the client you need to set it using the fetch client factory using the setup method as shown below. This is only required when working within a SharePoint add-in web. // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp, SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\"; // this only needs to be done once within your application sp.setup({ sp: { fetchClientFactory: () => { return new SPRequestExecutorClient(); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\"; const hostWebUrl = \"{The host web url, likely from the query string}\"; // make requests into the host web via the SP.RequestExecutor sp.crossDomainWeb(addInWenUrl, hostWebUrl)().then(w => { console.log(JSON.stringify(w, null, 4)); });","title":"SPRequestExecutorClient"},{"location":"sp-addinhelpers/sp-request-executor-client/#pnpsp-addinhelperssprequestexecutorclient","text":"The SPRequestExecutorClient is an implementation of the HttpClientImpl interface that facilitates requests to SharePoint from an add-in. It relies on the SharePoint SP product libraries being present to allow use of the SP.RequestExecutor to make the request.","title":"@pnp/sp-addinhelpers/sprequestexecutorclient"},{"location":"sp-addinhelpers/sp-request-executor-client/#setup","text":"To use the client you need to set it using the fetch client factory using the setup method as shown below. This is only required when working within a SharePoint add-in web. // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp, SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\"; // this only needs to be done once within your application sp.setup({ sp: { fetchClientFactory: () => { return new SPRequestExecutorClient(); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\"; const hostWebUrl = \"{The host web url, likely from the query string}\"; // make requests into the host web via the SP.RequestExecutor sp.crossDomainWeb(addInWenUrl, hostWebUrl)().then(w => { console.log(JSON.stringify(w, null, 4)); });","title":"Setup"},{"location":"sp-addinhelpers/sp-rest-addin/","text":"@pnp/sp-addinhelpers/sprestaddin \u00b6 This class extends the sp export from @pnp/sp and adds in the methods required to make cross domain calls // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp, SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\"; // this only needs to be done once within your application sp.setup({ sp: { fetchClientFactory: () => { return new SPRequestExecutorClient(); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\"; const hostWebUrl = \"{The host web url, likely from the query string}\"; // make requests into the host web via the SP.RequestExecutor const w = await sp.crossDomainWeb(addInWenUrl, hostWebUrl)(); console.log(JSON.stringify(w, null, 4));","title":"SPRestAddIn"},{"location":"sp-addinhelpers/sp-rest-addin/#pnpsp-addinhelperssprestaddin","text":"This class extends the sp export from @pnp/sp and adds in the methods required to make cross domain calls // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp, SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\"; // this only needs to be done once within your application sp.setup({ sp: { fetchClientFactory: () => { return new SPRequestExecutorClient(); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\"; const hostWebUrl = \"{The host web url, likely from the query string}\"; // make requests into the host web via the SP.RequestExecutor const w = await sp.crossDomainWeb(addInWenUrl, hostWebUrl)(); console.log(JSON.stringify(w, null, 4));","title":"@pnp/sp-addinhelpers/sprestaddin"},{"location":"v2/","text":"PnPjs is a collection of fluent libraries for consuming SharePoint, Graph, and Office 365 REST APIs in a type-safe way. You can use it within SharePoint Framework, Nodejs, or any JavaScript project. This an open source initiative and we encourage contributions and constructive feedback from the community. Animation of the library in use, note intellisense help in building your queries General Guidance \u00b6 These articles provide general guidance for working with the libraries. If you are migrating from v1 please review the transition guide . Getting Started Authentication Get Started Contributing npm scripts Polyfills Packages \u00b6 Patterns and Practices client side libraries (PnPjs) are comprised of the packages listed below. All of the packages are published as a set and depend on their peers within the @pnp scope. The latest published version is . @pnp/ adaljsclient Provides an adaljs wrapper suitable for use with PnPjs common Provides shared functionality across all pnp libraries config-store Provides a way to manage configuration within your application graph Provides a fluent api for working with Microsoft Graph logging Light-weight, subscribable logging framework msaljsclient Provides an msal wrapper suitable for use with PnPjs nodejs Provides functionality enabling the @pnp libraries within nodejs odata Provides shared odata functionality and base classes sp Provides a fluent api for working with SharePoint REST sp-addinhelpers Provides functionality for working within SharePoint add-ins Authentication \u00b6 We have a new section dedicated to helping you figure out the best way to handle authentication in your application, check it out! Issues, Questions, Ideas \u00b6 Please log an issue using our template as a guide. This will let us track your request and ensure we respond. We appreciate any constructive feedback, questions, ideas, or bug reports with our thanks for giving back to the project. Changelog \u00b6 Please review the CHANGELOG for release details on all library changes. Code of Conduct \u00b6 This project has adopted the Microsoft Open Source Code of Conduct . For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments. \"Sharing is Caring\" \u00b6 Please use http://aka.ms/sppnp for the latest updates around the whole SharePoint Patterns and Practices (PnP) program . Disclaimer \u00b6 THIS CODE IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.","title":"Index"},{"location":"v2/#general-guidance","text":"These articles provide general guidance for working with the libraries. If you are migrating from v1 please review the transition guide . Getting Started Authentication Get Started Contributing npm scripts Polyfills","title":"General Guidance"},{"location":"v2/#packages","text":"Patterns and Practices client side libraries (PnPjs) are comprised of the packages listed below. All of the packages are published as a set and depend on their peers within the @pnp scope. The latest published version is . @pnp/ adaljsclient Provides an adaljs wrapper suitable for use with PnPjs common Provides shared functionality across all pnp libraries config-store Provides a way to manage configuration within your application graph Provides a fluent api for working with Microsoft Graph logging Light-weight, subscribable logging framework msaljsclient Provides an msal wrapper suitable for use with PnPjs nodejs Provides functionality enabling the @pnp libraries within nodejs odata Provides shared odata functionality and base classes sp Provides a fluent api for working with SharePoint REST sp-addinhelpers Provides functionality for working within SharePoint add-ins","title":"Packages"},{"location":"v2/#authentication","text":"We have a new section dedicated to helping you figure out the best way to handle authentication in your application, check it out!","title":"Authentication"},{"location":"v2/#issues-questions-ideas","text":"Please log an issue using our template as a guide. This will let us track your request and ensure we respond. We appreciate any constructive feedback, questions, ideas, or bug reports with our thanks for giving back to the project.","title":"Issues, Questions, Ideas"},{"location":"v2/#changelog","text":"Please review the CHANGELOG for release details on all library changes.","title":"Changelog"},{"location":"v2/#code-of-conduct","text":"This project has adopted the Microsoft Open Source Code of Conduct . For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.","title":"Code of Conduct"},{"location":"v2/#sharing-is-caring","text":"Please use http://aka.ms/sppnp for the latest updates around the whole SharePoint Patterns and Practices (PnP) program .","title":"\"Sharing is Caring\""},{"location":"v2/#disclaimer","text":"THIS CODE IS PROVIDED AS IS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.","title":"Disclaimer"},{"location":"v2/SPFx-on-premises/","text":"Workaround for on-premises SPFx TypeScript Version (SharePoint 2016 or 2019) \u00b6 Note this article applies to version 1.4.1 SharePoint Framework projects targeting on-premises only. When using the Yeoman generator to create a SharePoint Framework 1.4.1 project targeting on-premises it installs TypeScript version 2.2.2 (SP2016) or 2.4.2/2.4.1 (SP2019). Unfortunately this library relies on 3.6.4 or later due to extensive use of default values for generic type parameters in the libraries. To work around this limitation you can follow the steps in this article. npm i npm i -g rimraf # used to remove the node_modules folder (much better/faster) Ensure that the @pnp/sp package is already installed npm i @pnp/sp Remove the package-lock.json file & node_modules rimraf node_modules folder and execute npm install Open package-lock.json from the root folder Search for \"typescript\" or similar with version 2.4.1 (SP2019) 2.2.2 (SP2016) Replace \"2.4.1\" or \"2.2.2\" with \"3.6.4\" Search for the next \"typescript\" occurrence and replace the block with: JSON \"typescript\": { \"version\": \"3.6.4\", \"resolved\": \"https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz\", \"integrity\": \"sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==\", \"dev\": true } Remove node_modules folder rimraf node_modules Run npm install Alternative using npm-force-resolutions \u00b6 Install resolutions package and TypeScript providing considered version explicitly: bash npm i -D npm-force-resolutions typescript@3.6.4 Add a resolution for TypeScript and preinstall script into package.json to a corresponding code blocks: JSON { \"scripts\": { \"preinstall\": \"npx npm-force-resolutions\" }, \"resolutions\": { \"typescript\": \"3.6.4\" } } Run npm install to trigger preinstall script and bumping TypeScript version into package-lock.json Run npm run build , should produce no errors Installing additional dependencies should be safe then.","title":"Workaround for on-premises SPFx TypeScript Version (SharePoint 2016 or 2019)"},{"location":"v2/SPFx-on-premises/#workaround-for-on-premises-spfx-typescript-version-sharepoint-2016-or-2019","text":"Note this article applies to version 1.4.1 SharePoint Framework projects targeting on-premises only. When using the Yeoman generator to create a SharePoint Framework 1.4.1 project targeting on-premises it installs TypeScript version 2.2.2 (SP2016) or 2.4.2/2.4.1 (SP2019). Unfortunately this library relies on 3.6.4 or later due to extensive use of default values for generic type parameters in the libraries. To work around this limitation you can follow the steps in this article. npm i npm i -g rimraf # used to remove the node_modules folder (much better/faster) Ensure that the @pnp/sp package is already installed npm i @pnp/sp Remove the package-lock.json file & node_modules rimraf node_modules folder and execute npm install Open package-lock.json from the root folder Search for \"typescript\" or similar with version 2.4.1 (SP2019) 2.2.2 (SP2016) Replace \"2.4.1\" or \"2.2.2\" with \"3.6.4\" Search for the next \"typescript\" occurrence and replace the block with: JSON \"typescript\": { \"version\": \"3.6.4\", \"resolved\": \"https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz\", \"integrity\": \"sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==\", \"dev\": true } Remove node_modules folder rimraf node_modules Run npm install","title":"Workaround for on-premises SPFx TypeScript Version (SharePoint 2016 or 2019)"},{"location":"v2/SPFx-on-premises/#alternative-using-npm-force-resolutions","text":"Install resolutions package and TypeScript providing considered version explicitly: bash npm i -D npm-force-resolutions typescript@3.6.4 Add a resolution for TypeScript and preinstall script into package.json to a corresponding code blocks: JSON { \"scripts\": { \"preinstall\": \"npx npm-force-resolutions\" }, \"resolutions\": { \"typescript\": \"3.6.4\" } } Run npm install to trigger preinstall script and bumping TypeScript version into package-lock.json Run npm run build , should produce no errors Installing additional dependencies should be safe then.","title":"Alternative using npm-force-resolutions"},{"location":"v2/getting-started/","text":"Getting Started \u00b6 These libraries are geared towards folks working with TypeScript but will work equally well for JavaScript projects. To get started you need to install the libraries you need via npm. Many of the packages have a peer dependency to other packages with the @pnp namespace meaning you may need to install more than one package. All packages are released together eliminating version confusion - all packages will depend on packages with the same version number. If you need to support older browsers please review the article on polyfills for required functionality. Install \u00b6 First you will need to install those libraries you want to use in your application. Here we will install the most frequently used packages. This step applies to any environment or project. npm install @pnp/sp @pnp/graph --save Next we can import and use the functionality within our application. Below is a very simple example, please see the individual package documentation for more details and examples. import { getRandomString } from \"@pnp/core\"; (function() { // get and log a random string console.log(getRandomString(20)); })() Getting Started with SharePoint Framework \u00b6 The @pnp/sp and @pnp/graph libraries are designed to work seamlessly within SharePoint Framework projects with a small amount of upfront configuration. If you are running in 2016 or 2019 on-premises please read this note on a workaround for the included TypeScript version. If you are targeting SharePoint online you do not need to take any additional steps. Establish Context \u00b6 Because SharePoint Framework provides a local context to each component we need to set that context within the library. This allows us to determine request urls as well as use the SPFx HttpGraphClient within @pnp/graph. There are two ways to provide the SPFx context to the library. Either through the setup method imported from @pnp/core or using the setup method on either the @pnp/sp or @pnp/graph main export. All three are shown below and are equivalent, meaning if you are already importing the sp variable from @pnp/sp or the graph variable from @pnp/graph you should use their setup method to reduce imports. The setup is always done in the onInit method to ensure it runs before your other life-cycle code. You can also set any other settings at this time. Using @pnp/core setup \u00b6 import { setup as pnpSetup } from \"@pnp/core\"; // ... protected onInit(): Promise { return super.onInit().then(_ => { // other init code may be present pnpSetup({ spfxContext: this.context }); }); } // ... Using @pnp/sp setup \u00b6 import { sp } from \"@pnp/sp/presets/all\"; // ... protected onInit(): Promise { return super.onInit().then(_ => { // other init code may be present sp.setup({ spfxContext: this.context }); }); } // ... Sp setup also supports passing just the SPFx context object directly as this is the most common case import { sp } from \"@pnp/sp/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present sp.setup(this.context); } // ... Using @pnp/graph setup \u00b6 import { graph } from \"@pnp/graph/presets/all\"; // ... protected onInit(): Promise { return super.onInit().then(_ => { // other init code may be present graph.setup({ spfxContext: this.context }); }); } // ... Establish context within an SPFx service \u00b6 Because you do not have full access to the context object within a service you need to setup things a little differently. If you do not need AAD tokens you can leave that part out and specify just the pageContext. import { ServiceKey, ServiceScope } from \"@microsoft/sp-core-library\"; import { PageContext } from \"@microsoft/sp-page-context\"; import { AadTokenProviderFactory } from \"@microsoft/sp-http\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; export interface ISampleService { getLists(): Promise; } export class SampleService { public static readonly serviceKey: ServiceKey = ServiceKey.create('SPFx:SampleService', SampleService); constructor(serviceScope: ServiceScope) { serviceScope.whenFinished(() => { const pageContext = serviceScope.consume(PageContext.serviceKey); const tokenProviderFactory = serviceScope.consume(AadTokenProviderFactory.serviceKey); // we need to \"spoof\" the context object with the parts we need for PnPjs sp.setup({ spfxContext: { aadTokenProviderFactory: tokenProviderFactory, pageContext: pageContext, } }); // This approach also works if you do not require AAD tokens // you don't need to do both // sp.setup({ // sp : { // baseUrl : pageContext.web.absoluteUrl // } // }); }); } public getLists(): Promise { return sp.web.lists(); } } Connect to SharePoint from Node \u00b6 Please see the main article on how we support node versions that require commonjs modules. npm i @pnp/sp-commonjs @pnp/nodejs-commonjs This will install the logging, common, odata, sp, and nodejs packages. You can read more about what each package does starting on the packages page. Once these are installed you need to import them into your project, to communicate with SharePoint from node we'll need the following imports: import { sp } from \"@pnp/sp-commonjs\"; import { SPFetchClient } from \"@pnp/nodejs-commonjs\"; Once you have imported the necessary resources you can update your code to setup the node fetch client as well as make a call to SharePoint. // configure your node options (only once in your application) sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\"); }, }, }); // make a call to SharePoint and log it in the console sp.web.select(\"Title\", \"Description\")().then(w => { console.log(JSON.stringify(w, null, 4)); }); Connect to Microsoft Graph From Node \u00b6 Similar to the above you can also make calls to the Graph api from node using the libraries. Again we start with installing the required resources. You can see ./debug/launch/graph.ts for a live example. npm i @pnp/graph-commonjs @pnp/nodejs-commonjs Now we need to import what we'll need to call graph import { graph } from \"@pnp/graph-commonjs\"; import { AdalFetchClient } from \"@pnp/nodejs-commonjs\"; Now we can make our graph calls after setting up the Adal client. Note you'll need to setup an AzureAD App registration with the necessary permissions. graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{mytenant}.onmicrosoft.com\", \"{application id}\", \"{application secret}\"); }, }, }); // make a call to Graph and get all the groups graph.groups().then(g => { console.log(JSON.stringify(g, null, 4)); }); Getting Started outside SharePoint Framework \u00b6 In some cases you may be working in a way such that we cannot determine the base url for the web. In this scenario you have two options. Set baseUrl through setup \u00b6 Here we are setting the baseUrl via the sp.setup method. We are also setting the headers to use verbose mode, something you may have to do when working against unpatched versions of SharePoint 2013 as discussed here . This is optional for 2016 or SharePoint Online. The library does not support setting the headers to use nometadata as we rely on the metadata in the response to do some of the more complicated functions. Some of the pure data calls will probably work but it is not a supported configuration. import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { headers: { Accept: \"application/json;odata=verbose\", }, baseUrl: \"{Absolute SharePoint Web URL}\" }, }); const w = await sp.web(); Create Web instances directly \u00b6 Using this method you create the web directly with the url you want to use as the base. import { Web } from \"@pnp/sp/presets/all\"; const web = Web(\"{Absolute SharePoint Web URL}\"); const w = await web(); Next Steps \u00b6 Be sure to review the article describing all of the available settings across the libraries.","title":"Getting Started"},{"location":"v2/getting-started/#getting-started","text":"These libraries are geared towards folks working with TypeScript but will work equally well for JavaScript projects. To get started you need to install the libraries you need via npm. Many of the packages have a peer dependency to other packages with the @pnp namespace meaning you may need to install more than one package. All packages are released together eliminating version confusion - all packages will depend on packages with the same version number. If you need to support older browsers please review the article on polyfills for required functionality.","title":"Getting Started"},{"location":"v2/getting-started/#install","text":"First you will need to install those libraries you want to use in your application. Here we will install the most frequently used packages. This step applies to any environment or project. npm install @pnp/sp @pnp/graph --save Next we can import and use the functionality within our application. Below is a very simple example, please see the individual package documentation for more details and examples. import { getRandomString } from \"@pnp/core\"; (function() { // get and log a random string console.log(getRandomString(20)); })()","title":"Install"},{"location":"v2/getting-started/#getting-started-with-sharepoint-framework","text":"The @pnp/sp and @pnp/graph libraries are designed to work seamlessly within SharePoint Framework projects with a small amount of upfront configuration. If you are running in 2016 or 2019 on-premises please read this note on a workaround for the included TypeScript version. If you are targeting SharePoint online you do not need to take any additional steps.","title":"Getting Started with SharePoint Framework"},{"location":"v2/getting-started/#establish-context","text":"Because SharePoint Framework provides a local context to each component we need to set that context within the library. This allows us to determine request urls as well as use the SPFx HttpGraphClient within @pnp/graph. There are two ways to provide the SPFx context to the library. Either through the setup method imported from @pnp/core or using the setup method on either the @pnp/sp or @pnp/graph main export. All three are shown below and are equivalent, meaning if you are already importing the sp variable from @pnp/sp or the graph variable from @pnp/graph you should use their setup method to reduce imports. The setup is always done in the onInit method to ensure it runs before your other life-cycle code. You can also set any other settings at this time.","title":"Establish Context"},{"location":"v2/getting-started/#using-pnpcore-setup","text":"import { setup as pnpSetup } from \"@pnp/core\"; // ... protected onInit(): Promise { return super.onInit().then(_ => { // other init code may be present pnpSetup({ spfxContext: this.context }); }); } // ...","title":"Using @pnp/core setup"},{"location":"v2/getting-started/#using-pnpsp-setup","text":"import { sp } from \"@pnp/sp/presets/all\"; // ... protected onInit(): Promise { return super.onInit().then(_ => { // other init code may be present sp.setup({ spfxContext: this.context }); }); } // ... Sp setup also supports passing just the SPFx context object directly as this is the most common case import { sp } from \"@pnp/sp/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present sp.setup(this.context); } // ...","title":"Using @pnp/sp setup"},{"location":"v2/getting-started/#using-pnpgraph-setup","text":"import { graph } from \"@pnp/graph/presets/all\"; // ... protected onInit(): Promise { return super.onInit().then(_ => { // other init code may be present graph.setup({ spfxContext: this.context }); }); } // ...","title":"Using @pnp/graph setup"},{"location":"v2/getting-started/#establish-context-within-an-spfx-service","text":"Because you do not have full access to the context object within a service you need to setup things a little differently. If you do not need AAD tokens you can leave that part out and specify just the pageContext. import { ServiceKey, ServiceScope } from \"@microsoft/sp-core-library\"; import { PageContext } from \"@microsoft/sp-page-context\"; import { AadTokenProviderFactory } from \"@microsoft/sp-http\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; export interface ISampleService { getLists(): Promise; } export class SampleService { public static readonly serviceKey: ServiceKey = ServiceKey.create('SPFx:SampleService', SampleService); constructor(serviceScope: ServiceScope) { serviceScope.whenFinished(() => { const pageContext = serviceScope.consume(PageContext.serviceKey); const tokenProviderFactory = serviceScope.consume(AadTokenProviderFactory.serviceKey); // we need to \"spoof\" the context object with the parts we need for PnPjs sp.setup({ spfxContext: { aadTokenProviderFactory: tokenProviderFactory, pageContext: pageContext, } }); // This approach also works if you do not require AAD tokens // you don't need to do both // sp.setup({ // sp : { // baseUrl : pageContext.web.absoluteUrl // } // }); }); } public getLists(): Promise { return sp.web.lists(); } }","title":"Establish context within an SPFx service"},{"location":"v2/getting-started/#connect-to-sharepoint-from-node","text":"Please see the main article on how we support node versions that require commonjs modules. npm i @pnp/sp-commonjs @pnp/nodejs-commonjs This will install the logging, common, odata, sp, and nodejs packages. You can read more about what each package does starting on the packages page. Once these are installed you need to import them into your project, to communicate with SharePoint from node we'll need the following imports: import { sp } from \"@pnp/sp-commonjs\"; import { SPFetchClient } from \"@pnp/nodejs-commonjs\"; Once you have imported the necessary resources you can update your code to setup the node fetch client as well as make a call to SharePoint. // configure your node options (only once in your application) sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\"); }, }, }); // make a call to SharePoint and log it in the console sp.web.select(\"Title\", \"Description\")().then(w => { console.log(JSON.stringify(w, null, 4)); });","title":"Connect to SharePoint from Node"},{"location":"v2/getting-started/#connect-to-microsoft-graph-from-node","text":"Similar to the above you can also make calls to the Graph api from node using the libraries. Again we start with installing the required resources. You can see ./debug/launch/graph.ts for a live example. npm i @pnp/graph-commonjs @pnp/nodejs-commonjs Now we need to import what we'll need to call graph import { graph } from \"@pnp/graph-commonjs\"; import { AdalFetchClient } from \"@pnp/nodejs-commonjs\"; Now we can make our graph calls after setting up the Adal client. Note you'll need to setup an AzureAD App registration with the necessary permissions. graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{mytenant}.onmicrosoft.com\", \"{application id}\", \"{application secret}\"); }, }, }); // make a call to Graph and get all the groups graph.groups().then(g => { console.log(JSON.stringify(g, null, 4)); });","title":"Connect to Microsoft Graph From Node"},{"location":"v2/getting-started/#getting-started-outside-sharepoint-framework","text":"In some cases you may be working in a way such that we cannot determine the base url for the web. In this scenario you have two options.","title":"Getting Started outside SharePoint Framework"},{"location":"v2/getting-started/#set-baseurl-through-setup","text":"Here we are setting the baseUrl via the sp.setup method. We are also setting the headers to use verbose mode, something you may have to do when working against unpatched versions of SharePoint 2013 as discussed here . This is optional for 2016 or SharePoint Online. The library does not support setting the headers to use nometadata as we rely on the metadata in the response to do some of the more complicated functions. Some of the pure data calls will probably work but it is not a supported configuration. import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { headers: { Accept: \"application/json;odata=verbose\", }, baseUrl: \"{Absolute SharePoint Web URL}\" }, }); const w = await sp.web();","title":"Set baseUrl through setup"},{"location":"v2/getting-started/#create-web-instances-directly","text":"Using this method you create the web directly with the url you want to use as the base. import { Web } from \"@pnp/sp/presets/all\"; const web = Web(\"{Absolute SharePoint Web URL}\"); const w = await web();","title":"Create Web instances directly"},{"location":"v2/getting-started/#next-steps","text":"Be sure to review the article describing all of the available settings across the libraries.","title":"Next Steps"},{"location":"v2/nodejs-support/","text":"Working in Nodejs \u00b6 As outlined on the getting started page you can easily use the library with Nodejs, but there are some key differences you need to consider. But first a little history, you can skip this part if you just want to see how things work but we felt some folks might be interested. To make selective imports work we need to support es module syntax for client-side environments such as SPFx development. All versions of Nodejs that are currently LTS do not support es modules without flags (as of when this was written). We thought we had a scheme to handle this following the available guidance but ultimately it didn't work across all node versions and we unpublished 2.0.1. CommonJS Libraries \u00b6 Because of the difficulties of working with es modules in node we recommend using our mirror packages providing commonjs modules. These can be installed by using the package name and appending -commonjs, such as: npm install @pnp/sp-commonjs @pnp/nodejs-commonjs These packages are built from the same source and released at the same time so all updates are included with each release. The only difference is that for the sp-commonjs and graph-commonjs packages we target the \"all\" preset as the entry point. This makes things a little easier in node where bundle sizes aren't an issue. You can see this in the nodejs-app sample . Here is that sample explained fully: Install Libraries \u00b6 We want to make a simple request to SharePoint so we need to first install the modules we need: npm install @pnp/sp-commonjs @pnp/nodejs-commonjs --save We will also install TypeScript: npm install typescript --save-dev index.ts \u00b6 We will create an index.ts file and add the following code. You will need to update the site url, client id, and client secret to your values. This should be done using a settings file or something like Azure KeyVault for production, but for this example it is good enough. // our imports come from the -commonjs libs import { SPFetchClient } from \"@pnp/nodejs-commonjs\"; import { sp } from \"@pnp/sp-commonjs\"; // we call setup to use the node client sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{ site url }\", \"{ client id }\", \"{ client secret }\"); }, }, }); async function makeRequest() { // make a request to get the web's details const w = await sp.web(); console.log(JSON.stringify(w, null, 2)); } // get past no await at root of app makeRequest(); Don't forget you will need to register an app to get the client id and secret. Add a tsconfig.json \u00b6 Not strictly necessary but very useful to include a tsconfig.json to control how tsc transpiles your code to JavaScript { \"compilerOptions\": { \"module\": \"commonjs\", \"target\": \"esnext\", \"moduleResolution\": \"node\", \"declaration\": true, \"outDir\": \"dist\", \"skipLibCheck\": true, \"sourceMap\": true, \"lib\": [ \"dom\", \"esnext\" ] }, \"files\": [ \"./index.ts\" ] } Add an script to package.json \u00b6 We add the \"start\" script to the default package.json { \"name\": \"nodejs-app\", \"version\": \"1.0.0\", \"description\": \"Sample nodejs app using PnPjs\", \"main\": \"index.js\", \"scripts\": { \"start\": \"tsc -p . && node dist/index.js\", \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\" }, \"author\": \"\", \"license\": \"MIT\", \"dependencies\": { \"@pnp/nodejs-commonjs\": \"^2.0.2-5\", \"@pnp/sp-commonjs\": \"^2.0.2-5\" }, \"devDependencies\": { \"typescript\": \"^3.7.5\" } } Run It \u00b6 You can now run your program using: npm start","title":"Working in Nodejs"},{"location":"v2/nodejs-support/#working-in-nodejs","text":"As outlined on the getting started page you can easily use the library with Nodejs, but there are some key differences you need to consider. But first a little history, you can skip this part if you just want to see how things work but we felt some folks might be interested. To make selective imports work we need to support es module syntax for client-side environments such as SPFx development. All versions of Nodejs that are currently LTS do not support es modules without flags (as of when this was written). We thought we had a scheme to handle this following the available guidance but ultimately it didn't work across all node versions and we unpublished 2.0.1.","title":"Working in Nodejs"},{"location":"v2/nodejs-support/#commonjs-libraries","text":"Because of the difficulties of working with es modules in node we recommend using our mirror packages providing commonjs modules. These can be installed by using the package name and appending -commonjs, such as: npm install @pnp/sp-commonjs @pnp/nodejs-commonjs These packages are built from the same source and released at the same time so all updates are included with each release. The only difference is that for the sp-commonjs and graph-commonjs packages we target the \"all\" preset as the entry point. This makes things a little easier in node where bundle sizes aren't an issue. You can see this in the nodejs-app sample . Here is that sample explained fully:","title":"CommonJS Libraries"},{"location":"v2/nodejs-support/#install-libraries","text":"We want to make a simple request to SharePoint so we need to first install the modules we need: npm install @pnp/sp-commonjs @pnp/nodejs-commonjs --save We will also install TypeScript: npm install typescript --save-dev","title":"Install Libraries"},{"location":"v2/nodejs-support/#indexts","text":"We will create an index.ts file and add the following code. You will need to update the site url, client id, and client secret to your values. This should be done using a settings file or something like Azure KeyVault for production, but for this example it is good enough. // our imports come from the -commonjs libs import { SPFetchClient } from \"@pnp/nodejs-commonjs\"; import { sp } from \"@pnp/sp-commonjs\"; // we call setup to use the node client sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{ site url }\", \"{ client id }\", \"{ client secret }\"); }, }, }); async function makeRequest() { // make a request to get the web's details const w = await sp.web(); console.log(JSON.stringify(w, null, 2)); } // get past no await at root of app makeRequest(); Don't forget you will need to register an app to get the client id and secret.","title":"index.ts"},{"location":"v2/nodejs-support/#add-a-tsconfigjson","text":"Not strictly necessary but very useful to include a tsconfig.json to control how tsc transpiles your code to JavaScript { \"compilerOptions\": { \"module\": \"commonjs\", \"target\": \"esnext\", \"moduleResolution\": \"node\", \"declaration\": true, \"outDir\": \"dist\", \"skipLibCheck\": true, \"sourceMap\": true, \"lib\": [ \"dom\", \"esnext\" ] }, \"files\": [ \"./index.ts\" ] }","title":"Add a tsconfig.json"},{"location":"v2/nodejs-support/#add-an-script-to-packagejson","text":"We add the \"start\" script to the default package.json { \"name\": \"nodejs-app\", \"version\": \"1.0.0\", \"description\": \"Sample nodejs app using PnPjs\", \"main\": \"index.js\", \"scripts\": { \"start\": \"tsc -p . && node dist/index.js\", \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\" }, \"author\": \"\", \"license\": \"MIT\", \"dependencies\": { \"@pnp/nodejs-commonjs\": \"^2.0.2-5\", \"@pnp/sp-commonjs\": \"^2.0.2-5\" }, \"devDependencies\": { \"typescript\": \"^3.7.5\" } }","title":"Add an script to package.json"},{"location":"v2/nodejs-support/#run-it","text":"You can now run your program using: npm start","title":"Run It"},{"location":"v2/npm-scripts/","text":"Supported NPM Scripts \u00b6 As you likely are aware you can embed scripts within package.json. Using this capability coupled with the knowledge that pretty much all of the tools we use now support code files (.js/.ts) as configuration we have removed gulp from our tooling and now execute our various actions via scripts. This is not a knock on gulp, it remains a great tool, rather an opportunity for us to remove some dependencies. This article outlines the current scripts we've implemented and how to use them, with available options and examples. Start \u00b6 Executes the serve command npm start Serve \u00b6 Starts a debugging server serving a bundled script with ./debug/serve/main.ts as the entry point. This allows you to run tests and debug code running within the context of a webpage rather than node. npm run serve Test \u00b6 Runs the tests and coverage for the library. Starting with 2.3.0 ONLY MSAL auth is supported for running the tests. More details on setting up MSAL for node. Options \u00b6 There are several options you can provide to the test command. All of these need to be separated using a \"--\" double hyphen so they are passed to the spawned sub-commands. Test a Single Package \u00b6 --package or -p This option will only run the tests associated with the package you specify. The values are the folder names within the ./packages directory. # run only sp tests npm test -- -p sp # run only logging tests npm test -- -package logging Run a Single Test File \u00b6 --single or --s You can also run a specific file with a package. This option must be used with the single package option as you are essentially specifying the folder and file. This option uses either the flags. # run only sp web tests npm test -- -p sp -s web # run only graph groups tests npm test -- -package graph -single groups Specify a Site \u00b6 --site By default every time you run the tests a new sub-site is created below the site specified in your settings file . You can choose to reuse a site for testing, which saves time when re-running a set of tests frequently. Testing content is not deleted after tests, so if you need to inspect the created content from testing you may wish to forgo this option. This option can be used with any or none of the other testing options. # run only sp web tests with a certain site npm test -- -p sp -s web --site https://some.site.com/sites/dev Cleanup \u00b6 --cleanup If you include this flag the testing web will be deleted once tests are complete. Useful for local testing where you do not need to inspect the web once the tests are complete. Works with any of the other options, be careful when specifying a web using --site as it will be deleted. # clean up our testing site npm test -- --cleanup Logging \u00b6 --logging If you include this flag a console logger will be subscribed and the log level will be set to Info. This will provide console output for all the requests being made during testing. This flag is compatible with all other flags - however unless you are trying to debug a specific test this will produce a lot of chatty output. # enable logging during testing npm test -- --logging spVerbose \u00b6 Added in 2.0.13 --spverbose This flag will enable \"verbose\" OData mode for SharePoint tests. This flag is compatible with other flags. npm test -- --spverbose build \u00b6 Invokes the pnpbuild cli to transpile the TypeScript into JavaScript. All behavior is controlled via the tsconfig.json in the root of the project and sub folders as needed. npm run build package \u00b6 Invokes the pnpbuild cli to create the package directories under the dist folder. This will allow you to see exactly what will end up in the npm packages once they are published. npm run package lint \u00b6 Runs the linter. npm run lint clean \u00b6 Removes any generated folders from the working directory. npm run clean","title":"Supported NPM Scripts"},{"location":"v2/npm-scripts/#supported-npm-scripts","text":"As you likely are aware you can embed scripts within package.json. Using this capability coupled with the knowledge that pretty much all of the tools we use now support code files (.js/.ts) as configuration we have removed gulp from our tooling and now execute our various actions via scripts. This is not a knock on gulp, it remains a great tool, rather an opportunity for us to remove some dependencies. This article outlines the current scripts we've implemented and how to use them, with available options and examples.","title":"Supported NPM Scripts"},{"location":"v2/npm-scripts/#start","text":"Executes the serve command npm start","title":"Start"},{"location":"v2/npm-scripts/#serve","text":"Starts a debugging server serving a bundled script with ./debug/serve/main.ts as the entry point. This allows you to run tests and debug code running within the context of a webpage rather than node. npm run serve","title":"Serve"},{"location":"v2/npm-scripts/#test","text":"Runs the tests and coverage for the library. Starting with 2.3.0 ONLY MSAL auth is supported for running the tests. More details on setting up MSAL for node.","title":"Test"},{"location":"v2/npm-scripts/#options","text":"There are several options you can provide to the test command. All of these need to be separated using a \"--\" double hyphen so they are passed to the spawned sub-commands.","title":"Options"},{"location":"v2/npm-scripts/#test-a-single-package","text":"--package or -p This option will only run the tests associated with the package you specify. The values are the folder names within the ./packages directory. # run only sp tests npm test -- -p sp # run only logging tests npm test -- -package logging","title":"Test a Single Package"},{"location":"v2/npm-scripts/#run-a-single-test-file","text":"--single or --s You can also run a specific file with a package. This option must be used with the single package option as you are essentially specifying the folder and file. This option uses either the flags. # run only sp web tests npm test -- -p sp -s web # run only graph groups tests npm test -- -package graph -single groups","title":"Run a Single Test File"},{"location":"v2/npm-scripts/#specify-a-site","text":"--site By default every time you run the tests a new sub-site is created below the site specified in your settings file . You can choose to reuse a site for testing, which saves time when re-running a set of tests frequently. Testing content is not deleted after tests, so if you need to inspect the created content from testing you may wish to forgo this option. This option can be used with any or none of the other testing options. # run only sp web tests with a certain site npm test -- -p sp -s web --site https://some.site.com/sites/dev","title":"Specify a Site"},{"location":"v2/npm-scripts/#cleanup","text":"--cleanup If you include this flag the testing web will be deleted once tests are complete. Useful for local testing where you do not need to inspect the web once the tests are complete. Works with any of the other options, be careful when specifying a web using --site as it will be deleted. # clean up our testing site npm test -- --cleanup","title":"Cleanup"},{"location":"v2/npm-scripts/#logging","text":"--logging If you include this flag a console logger will be subscribed and the log level will be set to Info. This will provide console output for all the requests being made during testing. This flag is compatible with all other flags - however unless you are trying to debug a specific test this will produce a lot of chatty output. # enable logging during testing npm test -- --logging","title":"Logging"},{"location":"v2/npm-scripts/#spverbose","text":"Added in 2.0.13 --spverbose This flag will enable \"verbose\" OData mode for SharePoint tests. This flag is compatible with other flags. npm test -- --spverbose","title":"spVerbose"},{"location":"v2/npm-scripts/#build","text":"Invokes the pnpbuild cli to transpile the TypeScript into JavaScript. All behavior is controlled via the tsconfig.json in the root of the project and sub folders as needed. npm run build","title":"build"},{"location":"v2/npm-scripts/#package","text":"Invokes the pnpbuild cli to create the package directories under the dist folder. This will allow you to see exactly what will end up in the npm packages once they are published. npm run package","title":"package"},{"location":"v2/npm-scripts/#lint","text":"Runs the linter. npm run lint","title":"lint"},{"location":"v2/npm-scripts/#clean","text":"Removes any generated folders from the working directory. npm run clean","title":"clean"},{"location":"v2/packages/","text":"Packages \u00b6 The following packages comprise the Patterns and Practices client side libraries. All of the packages are published as a set and depend on their peers within the @pnp scope. The latest published version is . @pnp/ adaljsclient Provides an adaljs wrapper suitable for use with PnPjs common Provides shared functionality across all pnp libraries config-store Provides a way to manage configuration within your application graph Provides a fluent api for working with Microsoft Graph logging Light-weight, subscribable logging framework msaljsclient Provides an msal wrapper suitable for use with PnPjs nodejs Provides functionality enabling the @pnp libraries within nodejs odata Provides shared odata functionality and base classes sp Provides a fluent api for working with SharePoint REST sp-addinhelpers Provides functionality for working within SharePoint add-ins","title":"Packages"},{"location":"v2/packages/#packages","text":"The following packages comprise the Patterns and Practices client side libraries. All of the packages are published as a set and depend on their peers within the @pnp scope. The latest published version is . @pnp/ adaljsclient Provides an adaljs wrapper suitable for use with PnPjs common Provides shared functionality across all pnp libraries config-store Provides a way to manage configuration within your application graph Provides a fluent api for working with Microsoft Graph logging Light-weight, subscribable logging framework msaljsclient Provides an msal wrapper suitable for use with PnPjs nodejs Provides functionality enabling the @pnp libraries within nodejs odata Provides shared odata functionality and base classes sp Provides a fluent api for working with SharePoint REST sp-addinhelpers Provides functionality for working within SharePoint add-ins","title":"Packages"},{"location":"v2/transition-guide/","text":"Transition Guide \u00b6 We have worked to make moving from @pnp library 1. to 2. as painless as possible, however there are some changes to how things work. The below guide we have provided an overview of what it takes to transition between the libraries. If we missed something, please let us know in the issues list so we can update the guide. Thanks! Installing @pnp libraries \u00b6 In version 1.* the libraries were setup as peer dependencies of each other requiring you to install each of them separately. We continue to believe this correctly describes the relationship, but recognize that basically nothing in the world accounts for peer dependencies. So we have updated the libraries to be dependencies. This makes it easier to install into your projects as you only need to install a single library: npm i --save @pnp/sp Selective Imports \u00b6 Another big change in v2 is the ability to selectively import the pieces you need from the libraries. This allows you to have smaller bundles and works well with tree-shaking. It does require you to have more import statements, which can potentially be a bit confusing at first. The selective imports apply to the sp and graph libraries. To help explain let's take the example of the Web object. In v1 Web includes a reference to pretty much everything else in the entire sp library. Meaning that if you use web (and you pretty much have to) you hold a ref to all the other pieces (like Fields, Lists, ContentTypes) even if you aren't using them. Because of that tree shaking can't do anything to reduce the bundle size because it \"thinks\" you are using them simply because they have been imported. To solve this in v2 the Web object no longer contains references to anything, it is a bare object with a few methods. If you look at the source you will see that, for example, there is no longer a \"lists\" property. These properties and methods are now added through selectively importing the functionality you need: Selectively Import Web lists functionality \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // this imports the functionality for lists associated only with web import \"@pnp/sp/lists/web\"; const r = await sp.web.lists(); import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // this imports all the functionality for lists import \"@pnp/sp/lists\"; const r = await sp.web.lists(); Each of the docs pages shows the selective import paths for each sub-module (lists, items, etc.). Presets \u00b6 In addition to the ability to selectively import functionality you can import presets. This allows you to import an entire set of functionality in a single line. At launch the sp library will support two presets \"all\" and \"core\" with the graph library supporting \"all\". Using the \"all\" preset will match the functionality of v1. This can save you time in transitioning your projects so you can update to selective imports later. For new projects we recommend using the selective imports from day 1. To update your V1 projects to V2 you can replace all instances of \"@pnp/sp\" with \"@pnp/sp/presets/all\" and things should work as before (though some class names or other things may have changed, please review the change log and the rest of this guide). // V1 way of doing things: import { sp, ClientSideWebpart, ClientSideWebpartPropertyTypes, } from \"@pnp/sp\"; // V2 way with selective imports import { sp } from \"@pnp/sp\"; import { ClientSideWebpart, ClientSideWebpartPropertyTypes } from \"@pnp/sp/clientside-pages\"; // V2 way with preset \"all\" import { sp, ClientSideWebpart, ClientSideWebpartPropertyTypes } from \"@pnp/sp/presets/all\"; Invokable Objects \u00b6 Another new feature is the addition of invokable objects. Previously where you used \"get()\" to invoke a request you can now leave it off. We have left the .get method in place so everyone's code wasn't broken immediately upon transitioning. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // old way (still works) const r1 = sp.web(); // invokable const r2 = sp.web(); The benefit is that objects can now support default actions that are not \"get\" but might be \"post\". And you save typing a few extra characters. This still work the same as with select or any of the other odata methods: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // invokable const r = sp.web.select(\"Title\", \"Url\")(); Factory Functions & Interfaces \u00b6 Another change in the library is in the structure of exports. We are no longer exporting the objects themselves, rather we are only exposing factory functions and interfaces. This allows us to decouple what developers use from our internal implementation. For folks using the fluent chain starting with sp you shouldn't need to update your code. If you are using any of the v1 classes directly you should just need to remove the \"new\" keyword and update the import path. The factory functions signature matches the constructor signature of the v1 objects. // v1 import { Web } from \"@pnp/sp\"; const web: Web = new Web(\"some absolute url\"); const r1 = web(); // v2 import { Web, IWeb } from \"@pnp/sp/webs\"; const web: IWeb = Web(\"some absolute url\"); const r2 = web(); Extension Methods \u00b6 Another new capability in v2 is the ability to extend objects and factories. This allows you to easily add methods or properties on a per-object basis. Please see the full article on extension methods describing this great new capability. CDN publishing \u00b6 Starting with v2 we will no longer create bundles for each of the packages. Historically these are not commonly used, don't work perfectly for everyone (there are a lot of ways to bundle things), and another piece we need to maintain. Instead we encourage folks to create their own bundles , optimized for their particular scenario. This will result in smaller overall bundle size and allow folks to bundle things to match their scenario. Please review the article on creating your custom bundles to see how to tailor bundles to your needs. The PnPjs bundle will remain, though it is designed only for backwards compatibility and we strongly recommend creating your own bundles, or directly importing the libraries into your projects using selective imports. Drop client-svc and sp-taxonomy libraries \u00b6 These libraries were created to allow folks to access and manage SharePoint taxonomy and manage metadata. Given that there is upcoming support for taxonomy via a supported REST API we will drop these two libraries. If working with taxonomy remains a core requirement of your application and we do not yet have support for the new apis, please remain on v1 for the time being. As of 2.0.6 we support reading the modern taxonomy API. Docs here","title":"Transition Guide"},{"location":"v2/transition-guide/#transition-guide","text":"We have worked to make moving from @pnp library 1. to 2. as painless as possible, however there are some changes to how things work. The below guide we have provided an overview of what it takes to transition between the libraries. If we missed something, please let us know in the issues list so we can update the guide. Thanks!","title":"Transition Guide"},{"location":"v2/transition-guide/#installing-pnp-libraries","text":"In version 1.* the libraries were setup as peer dependencies of each other requiring you to install each of them separately. We continue to believe this correctly describes the relationship, but recognize that basically nothing in the world accounts for peer dependencies. So we have updated the libraries to be dependencies. This makes it easier to install into your projects as you only need to install a single library: npm i --save @pnp/sp","title":"Installing @pnp libraries"},{"location":"v2/transition-guide/#selective-imports","text":"Another big change in v2 is the ability to selectively import the pieces you need from the libraries. This allows you to have smaller bundles and works well with tree-shaking. It does require you to have more import statements, which can potentially be a bit confusing at first. The selective imports apply to the sp and graph libraries. To help explain let's take the example of the Web object. In v1 Web includes a reference to pretty much everything else in the entire sp library. Meaning that if you use web (and you pretty much have to) you hold a ref to all the other pieces (like Fields, Lists, ContentTypes) even if you aren't using them. Because of that tree shaking can't do anything to reduce the bundle size because it \"thinks\" you are using them simply because they have been imported. To solve this in v2 the Web object no longer contains references to anything, it is a bare object with a few methods. If you look at the source you will see that, for example, there is no longer a \"lists\" property. These properties and methods are now added through selectively importing the functionality you need:","title":"Selective Imports"},{"location":"v2/transition-guide/#selectively-import-web-lists-functionality","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // this imports the functionality for lists associated only with web import \"@pnp/sp/lists/web\"; const r = await sp.web.lists(); import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // this imports all the functionality for lists import \"@pnp/sp/lists\"; const r = await sp.web.lists(); Each of the docs pages shows the selective import paths for each sub-module (lists, items, etc.).","title":"Selectively Import Web lists functionality"},{"location":"v2/transition-guide/#presets","text":"In addition to the ability to selectively import functionality you can import presets. This allows you to import an entire set of functionality in a single line. At launch the sp library will support two presets \"all\" and \"core\" with the graph library supporting \"all\". Using the \"all\" preset will match the functionality of v1. This can save you time in transitioning your projects so you can update to selective imports later. For new projects we recommend using the selective imports from day 1. To update your V1 projects to V2 you can replace all instances of \"@pnp/sp\" with \"@pnp/sp/presets/all\" and things should work as before (though some class names or other things may have changed, please review the change log and the rest of this guide). // V1 way of doing things: import { sp, ClientSideWebpart, ClientSideWebpartPropertyTypes, } from \"@pnp/sp\"; // V2 way with selective imports import { sp } from \"@pnp/sp\"; import { ClientSideWebpart, ClientSideWebpartPropertyTypes } from \"@pnp/sp/clientside-pages\"; // V2 way with preset \"all\" import { sp, ClientSideWebpart, ClientSideWebpartPropertyTypes } from \"@pnp/sp/presets/all\";","title":"Presets"},{"location":"v2/transition-guide/#invokable-objects","text":"Another new feature is the addition of invokable objects. Previously where you used \"get()\" to invoke a request you can now leave it off. We have left the .get method in place so everyone's code wasn't broken immediately upon transitioning. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // old way (still works) const r1 = sp.web(); // invokable const r2 = sp.web(); The benefit is that objects can now support default actions that are not \"get\" but might be \"post\". And you save typing a few extra characters. This still work the same as with select or any of the other odata methods: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // invokable const r = sp.web.select(\"Title\", \"Url\")();","title":"Invokable Objects"},{"location":"v2/transition-guide/#factory-functions-interfaces","text":"Another change in the library is in the structure of exports. We are no longer exporting the objects themselves, rather we are only exposing factory functions and interfaces. This allows us to decouple what developers use from our internal implementation. For folks using the fluent chain starting with sp you shouldn't need to update your code. If you are using any of the v1 classes directly you should just need to remove the \"new\" keyword and update the import path. The factory functions signature matches the constructor signature of the v1 objects. // v1 import { Web } from \"@pnp/sp\"; const web: Web = new Web(\"some absolute url\"); const r1 = web(); // v2 import { Web, IWeb } from \"@pnp/sp/webs\"; const web: IWeb = Web(\"some absolute url\"); const r2 = web();","title":"Factory Functions & Interfaces"},{"location":"v2/transition-guide/#extension-methods","text":"Another new capability in v2 is the ability to extend objects and factories. This allows you to easily add methods or properties on a per-object basis. Please see the full article on extension methods describing this great new capability.","title":"Extension Methods"},{"location":"v2/transition-guide/#cdn-publishing","text":"Starting with v2 we will no longer create bundles for each of the packages. Historically these are not commonly used, don't work perfectly for everyone (there are a lot of ways to bundle things), and another piece we need to maintain. Instead we encourage folks to create their own bundles , optimized for their particular scenario. This will result in smaller overall bundle size and allow folks to bundle things to match their scenario. Please review the article on creating your custom bundles to see how to tailor bundles to your needs. The PnPjs bundle will remain, though it is designed only for backwards compatibility and we strongly recommend creating your own bundles, or directly importing the libraries into your projects using selective imports.","title":"CDN publishing"},{"location":"v2/transition-guide/#drop-client-svc-and-sp-taxonomy-libraries","text":"These libraries were created to allow folks to access and manage SharePoint taxonomy and manage metadata. Given that there is upcoming support for taxonomy via a supported REST API we will drop these two libraries. If working with taxonomy remains a core requirement of your application and we do not yet have support for the new apis, please remain on v1 for the time being. As of 2.0.6 we support reading the modern taxonomy API. Docs here","title":"Drop client-svc and sp-taxonomy libraries"},{"location":"v2/authentication/","text":"Authentication \u00b6 One of the more challenging aspects of web development is ensuring you are properly authenticated to access the resources you need. This section is designed to guide you through connecting to the resources you need using the appropriate methods. There are two places the PnPjs libraries can be used to connect to various services client (browser) or server . Utility Scenarios \u00b6 BearerTokenFetchClient LambdaFetchClient Client Scenarios \u00b6 SharePoint Framework Connect As: Current User User + AAD App via MSAL User + AAD App via ADAL Connect To: SharePoint as: Current User User + AAD App via MSAL Graph as: Current User User + AAD App via MSAL Both as: Current User User + AAD App via MSAL Single Page Application User + AAD App via MSAL Server Scenarios \u00b6 NodeJS SharePoint App Registration (App-Only) ADAL (App-Only) MSAL (App-Only) - coming soon","title":"Authentication"},{"location":"v2/authentication/#authentication","text":"One of the more challenging aspects of web development is ensuring you are properly authenticated to access the resources you need. This section is designed to guide you through connecting to the resources you need using the appropriate methods. There are two places the PnPjs libraries can be used to connect to various services client (browser) or server .","title":"Authentication"},{"location":"v2/authentication/#utility-scenarios","text":"BearerTokenFetchClient LambdaFetchClient","title":"Utility Scenarios"},{"location":"v2/authentication/#client-scenarios","text":"SharePoint Framework Connect As: Current User User + AAD App via MSAL User + AAD App via ADAL Connect To: SharePoint as: Current User User + AAD App via MSAL Graph as: Current User User + AAD App via MSAL Both as: Current User User + AAD App via MSAL Single Page Application User + AAD App via MSAL","title":"Client Scenarios"},{"location":"v2/authentication/#server-scenarios","text":"NodeJS SharePoint App Registration (App-Only) ADAL (App-Only) MSAL (App-Only) - coming soon","title":"Server Scenarios"},{"location":"v2/authentication/adaljsclient/","text":"@pnp/core/adalclient \u00b6 This module contains the AdalClient class which can be used to authenticate to any AzureAD secured resource. It is designed to work seamlessly with SharePoint Framework's permissions. Where possible it is recommended to use the MSAL client . Getting Started \u00b6 Install the library and required dependencies npm install @pnp/adaljsclient --save Setup and Use inside SharePoint Framework \u00b6 Using the SharePoint Framework is the preferred way to make use of the AdalClient as we can use the AADTokenProvider to efficiently get tokens on your behalf. You can also read more about how this process works and the necessary SPFx configurations in the SharePoint Framework 1.6 release notes . This method will only work for SharePoint Framework >= 1.6. For earlier versions of SharePoint Framework you can still use the AdalClient as outlined below using the constructor to specify the values for an AAD Application you have setup. Calling the graph api \u00b6 By providing the context in the onInit we can create the adal client from known information. import { graph } from \"@pnp/graph\"; import { getRandomString } from \"@pnp/core\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present graph.setup(this.context); }); } public render(): void { // here we are creating a team with a random name, required Group ReadWrite All permissions const teamName = `ATeam.${getRandomString(4)}`; this.domElement.innerHTML = `Hello, I am creating a team named \"${teamName}\" for you...`; graph.teams.create(teamName, \"This is a description\").then(t => { this.domElement.innerHTML += \"done!\"; }).catch(e => { this.domElement.innerHTML = `Oops, I ran into a problem...${JSON.stringify(e, null, 4)}`; }); } Calling the SharePoint API \u00b6 This example shows how to use the ADALClient with the @pnp/sp library to call an API secured with AAD from within SharePoint Framework. import { SPFxAdalClient } from \"@pnp/core\"; import { sp } from \"@pnp/sp/presets/all\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present sp.setup({ spfxContext: this.context, sp: { fetchClientFactory: () => new SPFxAdalClient(this.context), }, }); }); } public render(): void { sp.web().then(t => { this.domElement.innerHTML = JSON.stringify(t); }).catch(e => { this.domElement.innerHTML = JSON.stringify(e); }); } Calling the any API \u00b6 You can also use the AdalClient to execute AAD authenticated requests to any API which is properly configured to accept the incoming tokens. This approach will only work within SharePoint Framework >= 1.6. Here we call the SharePoint REST API without the sp library as an example. import { FetchOptions } from \"@pnp/core\"; import { AdalClient } from \"@pnp/adaljsclient\"; import { ODataDefaultParser } from \"@pnp/queryable\"; // ... public render(): void { // create an ADAL Client const client = AdalClient.fromSPFxContext(this.context); // setup the request options const opts: FetchOptions = { method: \"GET\", headers: { \"Accept\": \"application/json\", }, }; // execute the request client.fetch(\"https://{tenant}.sharepoint.com/_api/web\", opts).then(response => { // create a parser to convert the response into JSON. // You can create your own, at this point you have a fetch Response to work with const parser = new ODataDefaultParser(); parser.parse(response).then(json => { this.domElement.innerHTML = JSON.stringify(json); }); }).catch(e => { this.domElement.innerHTML = JSON.stringify(e); }); } Manually Configure \u00b6 This example shows setting up and using the AdalClient to make queries using information you have setup. You can review this article for more information on setting up and securing any application using AzureAD. Setup and Use with Microsoft Graph \u00b6 This sample uses a custom AzureAd app you have created and granted the appropriate permissions. import { AdalClient } from \"@pnp/adaljsclient\"; import { graph } from \"@pnp/graph\"; // configure the graph client // parameters are: // client id - the id of the application you created in azure ad // tenant - can be id or URL (shown) // redirect url - absolute url of a page to which your application and Azure AD app allows replies graph.setup({ graph: { fetchClientFactory: () => { return new AdalClient( \"00000000-0000-0000-0000-000000000000\", \"{tenant}.onmicrosoft.com\", \"https://myapp/singlesignon.aspx\"); }, }, }); try { // call the graph API const groups = await graph.groups(); console.log(JSON.stringify(groups, null, 4)); } catch (e) { console.error(e); } Nodejs Applications \u00b6 We have a dedicated node client in @pnp/nodejs.","title":"@pnp/core/adalclient"},{"location":"v2/authentication/adaljsclient/#pnpcoreadalclient","text":"This module contains the AdalClient class which can be used to authenticate to any AzureAD secured resource. It is designed to work seamlessly with SharePoint Framework's permissions. Where possible it is recommended to use the MSAL client .","title":"@pnp/core/adalclient"},{"location":"v2/authentication/adaljsclient/#getting-started","text":"Install the library and required dependencies npm install @pnp/adaljsclient --save","title":"Getting Started"},{"location":"v2/authentication/adaljsclient/#setup-and-use-inside-sharepoint-framework","text":"Using the SharePoint Framework is the preferred way to make use of the AdalClient as we can use the AADTokenProvider to efficiently get tokens on your behalf. You can also read more about how this process works and the necessary SPFx configurations in the SharePoint Framework 1.6 release notes . This method will only work for SharePoint Framework >= 1.6. For earlier versions of SharePoint Framework you can still use the AdalClient as outlined below using the constructor to specify the values for an AAD Application you have setup.","title":"Setup and Use inside SharePoint Framework"},{"location":"v2/authentication/adaljsclient/#calling-the-graph-api","text":"By providing the context in the onInit we can create the adal client from known information. import { graph } from \"@pnp/graph\"; import { getRandomString } from \"@pnp/core\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present graph.setup(this.context); }); } public render(): void { // here we are creating a team with a random name, required Group ReadWrite All permissions const teamName = `ATeam.${getRandomString(4)}`; this.domElement.innerHTML = `Hello, I am creating a team named \"${teamName}\" for you...`; graph.teams.create(teamName, \"This is a description\").then(t => { this.domElement.innerHTML += \"done!\"; }).catch(e => { this.domElement.innerHTML = `Oops, I ran into a problem...${JSON.stringify(e, null, 4)}`; }); }","title":"Calling the graph api"},{"location":"v2/authentication/adaljsclient/#calling-the-sharepoint-api","text":"This example shows how to use the ADALClient with the @pnp/sp library to call an API secured with AAD from within SharePoint Framework. import { SPFxAdalClient } from \"@pnp/core\"; import { sp } from \"@pnp/sp/presets/all\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present sp.setup({ spfxContext: this.context, sp: { fetchClientFactory: () => new SPFxAdalClient(this.context), }, }); }); } public render(): void { sp.web().then(t => { this.domElement.innerHTML = JSON.stringify(t); }).catch(e => { this.domElement.innerHTML = JSON.stringify(e); }); }","title":"Calling the SharePoint API"},{"location":"v2/authentication/adaljsclient/#calling-the-any-api","text":"You can also use the AdalClient to execute AAD authenticated requests to any API which is properly configured to accept the incoming tokens. This approach will only work within SharePoint Framework >= 1.6. Here we call the SharePoint REST API without the sp library as an example. import { FetchOptions } from \"@pnp/core\"; import { AdalClient } from \"@pnp/adaljsclient\"; import { ODataDefaultParser } from \"@pnp/queryable\"; // ... public render(): void { // create an ADAL Client const client = AdalClient.fromSPFxContext(this.context); // setup the request options const opts: FetchOptions = { method: \"GET\", headers: { \"Accept\": \"application/json\", }, }; // execute the request client.fetch(\"https://{tenant}.sharepoint.com/_api/web\", opts).then(response => { // create a parser to convert the response into JSON. // You can create your own, at this point you have a fetch Response to work with const parser = new ODataDefaultParser(); parser.parse(response).then(json => { this.domElement.innerHTML = JSON.stringify(json); }); }).catch(e => { this.domElement.innerHTML = JSON.stringify(e); }); }","title":"Calling the any API"},{"location":"v2/authentication/adaljsclient/#manually-configure","text":"This example shows setting up and using the AdalClient to make queries using information you have setup. You can review this article for more information on setting up and securing any application using AzureAD.","title":"Manually Configure"},{"location":"v2/authentication/adaljsclient/#setup-and-use-with-microsoft-graph","text":"This sample uses a custom AzureAd app you have created and granted the appropriate permissions. import { AdalClient } from \"@pnp/adaljsclient\"; import { graph } from \"@pnp/graph\"; // configure the graph client // parameters are: // client id - the id of the application you created in azure ad // tenant - can be id or URL (shown) // redirect url - absolute url of a page to which your application and Azure AD app allows replies graph.setup({ graph: { fetchClientFactory: () => { return new AdalClient( \"00000000-0000-0000-0000-000000000000\", \"{tenant}.onmicrosoft.com\", \"https://myapp/singlesignon.aspx\"); }, }, }); try { // call the graph API const groups = await graph.groups(); console.log(JSON.stringify(groups, null, 4)); } catch (e) { console.error(e); }","title":"Setup and Use with Microsoft Graph"},{"location":"v2/authentication/adaljsclient/#nodejs-applications","text":"We have a dedicated node client in @pnp/nodejs.","title":"Nodejs Applications"},{"location":"v2/authentication/bearertokenclient/","text":"@pnp/core/BearerTokenFetchClient \u00b6 The BearerTokenFetchClient takes a single parameter representing an access token and uses it to make the requests. The disadvantage to this approach is not knowing to where the request will be sent, which in some cases is fine. An alternative is the LambdaFetchClient Static \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import { BearerTokenFetchClient } from \"@pnp/core\"; import { myTokenFactory } from \"./my-auth.js\"; graph.setup({ graph: { fetchClientFactory: () => { // note this method is not async, so your logic here cannot await. // Please see the LambdaFetchClient if you have a need for async support. const token = myTokenFactory(); return new BearerTokenFetchClient(token); }, }, });","title":"@pnp/core/BearerTokenFetchClient"},{"location":"v2/authentication/bearertokenclient/#pnpcorebearertokenfetchclient","text":"The BearerTokenFetchClient takes a single parameter representing an access token and uses it to make the requests. The disadvantage to this approach is not knowing to where the request will be sent, which in some cases is fine. An alternative is the LambdaFetchClient","title":"@pnp/core/BearerTokenFetchClient"},{"location":"v2/authentication/bearertokenclient/#static","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import { BearerTokenFetchClient } from \"@pnp/core\"; import { myTokenFactory } from \"./my-auth.js\"; graph.setup({ graph: { fetchClientFactory: () => { // note this method is not async, so your logic here cannot await. // Please see the LambdaFetchClient if you have a need for async support. const token = myTokenFactory(); return new BearerTokenFetchClient(token); }, }, });","title":"Static"},{"location":"v2/authentication/client-spa/","text":"Authentication in Single Page Application \u00b6 If you are writing a single page application deployed outside SharePoint it is recommended to use the MSAL client. You can find further details on the settings in the MSAL docs . You will need to ensure that you grant the permissions required to the application you are trying to use. import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/common\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"{your redirect uri}\", }, cache: { cacheLocation: \"sessionStorage\", }, }, [\"email\", \"Files.Read.All\", \"User.Read.All\"]), }, }); const data = await graph.me();","title":"Authentication in Single Page Application"},{"location":"v2/authentication/client-spa/#authentication-in-single-page-application","text":"If you are writing a single page application deployed outside SharePoint it is recommended to use the MSAL client. You can find further details on the settings in the MSAL docs . You will need to ensure that you grant the permissions required to the application you are trying to use. import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/common\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"{your redirect uri}\", }, cache: { cacheLocation: \"sessionStorage\", }, }, [\"email\", \"Files.Read.All\", \"User.Read.All\"]), }, }); const data = await graph.me();","title":"Authentication in Single Page Application"},{"location":"v2/authentication/client-spfx/","text":"Authentication in SharePoint Framework \u00b6 Auth as Current User \u00b6 PnPjs is designed to work as easily as possible within the SharePoint Framework so the authentication setup is very simple for the base case. Supply the current SharePoint Framework context to the library. This works for both SharePoint authentication and Graph authentication using the current user. Graph permissions are controlled by the permissions granted to the SharePoint shared application within your tenant. The below example is taken from a SharePoint Framework webpart. Connect to SharePoint as Current User \u00b6 import { sp } from \"@pnp/sp/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present sp.setup(this.context); } // ... Connect to Graph as Current User \u00b6 Permissions for this graph connection are controlled by the Shared SharePoint Application. You can target other applications using the MSAL Client . import { graph } from \"@pnp/graph/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present // this will use the ADAL client behind the scenes with no additional configuration work graph.setup(this.context); } // ... MSAL Client \u00b6 You might want/need to use a client configured to use your own AAD application and not the shared SharePoint application. You can do so using the MSAL client . Here we show this using graph, this works the same with any of the setup strategies . Please see the MSAL library docs for more details on what values to supply in the configuration. Note: you must install the @pnp/msaljsclient client package before using it import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/common\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"{your redirect uri}\", }, cache: { cacheLocation: \"sessionStorage\", }, }, [\"email\", \"Files.Read.All\", \"User.Read.All\"]), }, }); } // ... ADAL Client \u00b6 You can use the ADAL client from within SPFx, though it is recommended to transition to the MSAL client. Note: you must install the @pnp/adaljsclient client package before using it import { AdalClient } from \"@pnp/adaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present graph.setup({ graph: { fetchClientFactory: () => { return new AdalClient( \"00000000-0000-0000-0000-000000000000\", \"{tenant}.onmicrosoft.com\", \"\"); }, }); } // ...","title":"Authentication in SharePoint Framework"},{"location":"v2/authentication/client-spfx/#authentication-in-sharepoint-framework","text":"","title":"Authentication in SharePoint Framework"},{"location":"v2/authentication/client-spfx/#auth-as-current-user","text":"PnPjs is designed to work as easily as possible within the SharePoint Framework so the authentication setup is very simple for the base case. Supply the current SharePoint Framework context to the library. This works for both SharePoint authentication and Graph authentication using the current user. Graph permissions are controlled by the permissions granted to the SharePoint shared application within your tenant. The below example is taken from a SharePoint Framework webpart.","title":"Auth as Current User"},{"location":"v2/authentication/client-spfx/#connect-to-sharepoint-as-current-user","text":"import { sp } from \"@pnp/sp/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present sp.setup(this.context); } // ...","title":"Connect to SharePoint as Current User"},{"location":"v2/authentication/client-spfx/#connect-to-graph-as-current-user","text":"Permissions for this graph connection are controlled by the Shared SharePoint Application. You can target other applications using the MSAL Client . import { graph } from \"@pnp/graph/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present // this will use the ADAL client behind the scenes with no additional configuration work graph.setup(this.context); } // ...","title":"Connect to Graph as Current User"},{"location":"v2/authentication/client-spfx/#msal-client","text":"You might want/need to use a client configured to use your own AAD application and not the shared SharePoint application. You can do so using the MSAL client . Here we show this using graph, this works the same with any of the setup strategies . Please see the MSAL library docs for more details on what values to supply in the configuration. Note: you must install the @pnp/msaljsclient client package before using it import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/common\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"{your redirect uri}\", }, cache: { cacheLocation: \"sessionStorage\", }, }, [\"email\", \"Files.Read.All\", \"User.Read.All\"]), }, }); } // ...","title":"MSAL Client"},{"location":"v2/authentication/client-spfx/#adal-client","text":"You can use the ADAL client from within SPFx, though it is recommended to transition to the MSAL client. Note: you must install the @pnp/adaljsclient client package before using it import { AdalClient } from \"@pnp/adaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; // ... protected async onInit(): Promise { await super.onInit(); // other init code may be present graph.setup({ graph: { fetchClientFactory: () => { return new AdalClient( \"00000000-0000-0000-0000-000000000000\", \"{tenant}.onmicrosoft.com\", \"\"); }, }); } // ...","title":"ADAL Client"},{"location":"v2/authentication/lambdaclient/","text":"@pnp/core/LambdaFetchClient \u00b6 The LambdaFetchClient class allows you to provide an async function that returns an access token using any logic/supporting libraries you need. This provides total freedom to define how you do authentication, so long as it results in a usable Bearer token to call the target resource. The advantage to the LambdaFetchClient is that you get the url for each request, meaning your logic can account for where the request is headed. The token function should be as efficient as possible as it's logic must complete before each request will be sent. Signature \u00b6 The LambdaFetchClient accepts a single argument of type ILambdaTokenFactoryParams. // signature of method, the return string is the access token (parms: ILambdaTokenFactoryParams) => Promise // ILambdaTokenFactoryParams export interface ILambdaTokenFactoryParams { /** * Url to which the request for which we are requesting a token will be sent */ url: string; /** * Any options supplied for the request */ options: IFetchOptions; } @azure/msal-browser example \u00b6 This example shows how to use @azure/msal-browser along with LambdaFetchClient to achieve signin. msal-browser has many possible configurations which are described within their documentation. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import { LambdaFetchClient } from \"@pnp/core\"; import { PublicClientApplication, Configuration } from \"@azure/msal-browser\"; const config: Configuration = { auth: { clientId: \"{client id}\", authority: \"https://login.microsoftonline.com/common/\" } } // create a single application, could also create this within the lambda client, but it would create a new applicaiton per request const msal = new PublicClientApplication(config); // create a new instance of the lambda fetch client const client = new LambdaFetchClient(async () => { const request = { scopes: [\"User.Read.All\"], }; const response = await msal.loginPopup(request); // lamba returns the access token return response.accessToken; }); // setup graph with the client graph.setup({ graph: { fetchClientFactory: () => client, }, }); // execute the request to graph which will use the client defined above const result = await graph.users();","title":"@pnp/core/LambdaFetchClient"},{"location":"v2/authentication/lambdaclient/#pnpcorelambdafetchclient","text":"The LambdaFetchClient class allows you to provide an async function that returns an access token using any logic/supporting libraries you need. This provides total freedom to define how you do authentication, so long as it results in a usable Bearer token to call the target resource. The advantage to the LambdaFetchClient is that you get the url for each request, meaning your logic can account for where the request is headed. The token function should be as efficient as possible as it's logic must complete before each request will be sent.","title":"@pnp/core/LambdaFetchClient"},{"location":"v2/authentication/lambdaclient/#signature","text":"The LambdaFetchClient accepts a single argument of type ILambdaTokenFactoryParams. // signature of method, the return string is the access token (parms: ILambdaTokenFactoryParams) => Promise // ILambdaTokenFactoryParams export interface ILambdaTokenFactoryParams { /** * Url to which the request for which we are requesting a token will be sent */ url: string; /** * Any options supplied for the request */ options: IFetchOptions; }","title":"Signature"},{"location":"v2/authentication/lambdaclient/#azuremsal-browser-example","text":"This example shows how to use @azure/msal-browser along with LambdaFetchClient to achieve signin. msal-browser has many possible configurations which are described within their documentation. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import { LambdaFetchClient } from \"@pnp/core\"; import { PublicClientApplication, Configuration } from \"@azure/msal-browser\"; const config: Configuration = { auth: { clientId: \"{client id}\", authority: \"https://login.microsoftonline.com/common/\" } } // create a single application, could also create this within the lambda client, but it would create a new applicaiton per request const msal = new PublicClientApplication(config); // create a new instance of the lambda fetch client const client = new LambdaFetchClient(async () => { const request = { scopes: [\"User.Read.All\"], }; const response = await msal.loginPopup(request); // lamba returns the access token return response.accessToken; }); // setup graph with the client graph.setup({ graph: { fetchClientFactory: () => client, }, }); // execute the request to graph which will use the client defined above const result = await graph.users();","title":"@azure/msal-browser example"},{"location":"v2/authentication/msaljsclient/","text":"msaljsclient - MSAL Client for PnPjs \u00b6 The MSAL client is a thin wrapper around the MSAL library adapting it for use with PnPjs's request pipeline. Install \u00b6 You need to install the MSAL client before using it. This is in addition to installing the other PnPjs libraries you require. npm install @pnp/msaljsclient --save Configure \u00b6 The PnP client is a very thin wrapper around the MSAL library and you can supply any of the arguments supported. These are described in the MSAL docs . The basic configuration values you need (at least from our testing) are client id, authority, and redirectUri. The other options are settable but not required. This article is not intended to be an exhaustive discussion of all the MSAL configuration possibilities, please see the official docs to understand all of the available options. The second parameter when configuring the PnP client is the list of scope you are seeking to use. These must be configured and properly granted within AAD and you can request one or more scopes as needed for the current scenario. Use in SPFx \u00b6 Calling SharePoint via MSAL \u00b6 When calling the SharePoint REST API we must use only a special scope \"https://{tenant}.sharepoint.com/.default\" import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/mytentant.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://mytentant.sharepoint.com/sites/dev/SitePages/test.aspx\", }, }, [\"https://mytentant.sharepoint.com/.default\"]), }, }); const r = await sp.web(); Calling Graph via MSAL \u00b6 When calling the graph API you must specify the scopes you need and ensure they are configured in AAD import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/tenant.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://tenant.sharepoint.com/sites/dev/SitePages/test.aspx\", }, }, [\"Group.Read.All\"]), }, }); const r = await graph.groups(); Use in Single Page Applications \u00b6 You can also use the PnPjs MSAL client within your SPA applications. Please review the various settings to ensure you are configuring MSAL as needed for your application import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/tenant.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://myapp.com/login.aspx\", }, }, [\"Group.Read.All\"]), }, }); const r = await graph.groups(); Get a Token \u00b6 You can also use the client to get a token if you need a token for use outside the PnPjs libraries import { MsalClient } from \"@pnp/msaljsclient\"; // note we do not provide scopes here as the second parameter. We certainly could and will get a token // based on those scopes by making a call to getToken() without a param. const client = new MsalClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant}.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://{tenant}.sharepoint.com/sites/dev/SitePages/webpacktest.aspx\", }, }); const token = await client.getToken([\"Group.Read.All\"]); const token2 = await client.getToken([\"Files.Read\"]);","title":"msaljsclient - MSAL Client for PnPjs"},{"location":"v2/authentication/msaljsclient/#msaljsclient-msal-client-for-pnpjs","text":"The MSAL client is a thin wrapper around the MSAL library adapting it for use with PnPjs's request pipeline.","title":"msaljsclient - MSAL Client for PnPjs"},{"location":"v2/authentication/msaljsclient/#install","text":"You need to install the MSAL client before using it. This is in addition to installing the other PnPjs libraries you require. npm install @pnp/msaljsclient --save","title":"Install"},{"location":"v2/authentication/msaljsclient/#configure","text":"The PnP client is a very thin wrapper around the MSAL library and you can supply any of the arguments supported. These are described in the MSAL docs . The basic configuration values you need (at least from our testing) are client id, authority, and redirectUri. The other options are settable but not required. This article is not intended to be an exhaustive discussion of all the MSAL configuration possibilities, please see the official docs to understand all of the available options. The second parameter when configuring the PnP client is the list of scope you are seeking to use. These must be configured and properly granted within AAD and you can request one or more scopes as needed for the current scenario.","title":"Configure"},{"location":"v2/authentication/msaljsclient/#use-in-spfx","text":"","title":"Use in SPFx"},{"location":"v2/authentication/msaljsclient/#calling-sharepoint-via-msal","text":"When calling the SharePoint REST API we must use only a special scope \"https://{tenant}.sharepoint.com/.default\" import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/mytentant.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://mytentant.sharepoint.com/sites/dev/SitePages/test.aspx\", }, }, [\"https://mytentant.sharepoint.com/.default\"]), }, }); const r = await sp.web();","title":"Calling SharePoint via MSAL"},{"location":"v2/authentication/msaljsclient/#calling-graph-via-msal","text":"When calling the graph API you must specify the scopes you need and ensure they are configured in AAD import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/tenant.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://tenant.sharepoint.com/sites/dev/SitePages/test.aspx\", }, }, [\"Group.Read.All\"]), }, }); const r = await graph.groups();","title":"Calling Graph via MSAL"},{"location":"v2/authentication/msaljsclient/#use-in-single-page-applications","text":"You can also use the PnPjs MSAL client within your SPA applications. Please review the various settings to ensure you are configuring MSAL as needed for your application import { MsalClientSetup } from \"@pnp/msaljsclient\"; import { graph } from \"@pnp/graph/presets/all\"; graph.setup({ graph: { fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/tenant.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://myapp.com/login.aspx\", }, }, [\"Group.Read.All\"]), }, }); const r = await graph.groups();","title":"Use in Single Page Applications"},{"location":"v2/authentication/msaljsclient/#get-a-token","text":"You can also use the client to get a token if you need a token for use outside the PnPjs libraries import { MsalClient } from \"@pnp/msaljsclient\"; // note we do not provide scopes here as the second parameter. We certainly could and will get a token // based on those scopes by making a call to getToken() without a param. const client = new MsalClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant}.onmicrosoft.com/\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://{tenant}.sharepoint.com/sites/dev/SitePages/webpacktest.aspx\", }, }); const token = await client.getToken([\"Group.Read.All\"]); const token2 = await client.getToken([\"Files.Read\"]);","title":"Get a Token"},{"location":"v2/authentication/server-nodejs/","text":"Authentication in Nodejs \u00b6 SharePoint App Registration \u00b6 Due to a recent change in how SPO is configured NEW tenants will have ACS authentication disabled by default. You can read more details in this article . For testing we recommend using MSAL Certificate Auth . Within the PnPjs testing framework we make use of SharePoint App Registration. This uses the SPFetchClient client from the nodejs package. This client works based on the legacy SharePoint App Registration model making use of a client and secret granted permissions through AppInv.aspx. This method works and at the time of writing has no published end date. See: details on how to register a legacy SharePoint application . import { SPFetchClient } from \"@pnp/nodejs\"; import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\"); }, }, }); // execute a library request as normal const w = await sp.web(); MSAL \u00b6 Added in 2.0.11 You can now use the @azure/msal-node client with PnPjs using MsalFetchClient. You must configure an AAD application with the appropriate permissions for your application. At the time this article was written the msal-node package is not yet GA. Call Graph \u00b6 You can call the Microsoft Graph API with a client id and secret or certificate (see SharePoint example for cert auth) import { graph } from \"@pnp/graph/presets/all\"; // configure your node options graph.setup({ graph: { fetchClientFactory: () => { return new MsalFetchClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant id or common}/\", clientId: \"{guid}\", clientSecret: \"{client secret}\", } }); }, }, }); const userInfo = await graph.users(); Call SharePoint \u00b6 To call the SharePoint APIs via MSAL you are required to use certificate authentication with your application. Fully covering certificates is outside the scope of these docs, but the following commands were used with openssl to create testing certs for the sample code below. mkdir \\temp cd \\temp openssl req -x509 -newkey rsa:2048 -keyout keytmp.pem -out cert.pem -days 365 -passout pass:HereIsMySuperPass -subj '/C=US/ST=Washington/L=Seattle' openssl rsa -in keytmp.pem -out key.pem -passin pass:HereIsMySuperPass Using the above code you end up with three files, \"cert.pem\", \"key.pem\", and \"keytmp.pem\". The \"cert.pem\" file is uploaded to your AAD application registration. The \"key.pem\" is read as the private key for the configuration. You need to set the baseUrl property when using the MsalFetchClient import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { readFileSync } from \"fs\"; // read in our private key const buffer = readFileSync(\"c:/temp/key.pem\"); // configure node options sp.setup({ sp: { baseUrl: \"https://{my tenant}.sharepoint.com/sites/dev/\", fetchClientFactory: () => { return new MsalFetchClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant id or common}/\", clientCertificate: { thumbprint: \"{certificate thumbprint, displayed in AAD}\", privateKey: buffer.toString(), }, clientId: \"{client id}\", } }, [\"https://{my tenant}.sharepoint.com/.default\"]); // you must set the scope for SharePoint access }, }, }); const w = await sp.web(); ADAL \u00b6 The AdalFetchClient class depends on the adal-node package to authenticate against Azure AD. The example below outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected. See: More details on the node client import { AdalFetchClient } from \"@pnp/nodejs\"; import { graph } from \"@pnp/graph/presets/all\"; // setup the client using graph setup function graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{tenant}\", \"{app id}\", \"{app secret}\"); }, }, }); // execute a library request as normal const g = await graph.groups(); console.log(JSON.stringify(g, null, 4));","title":"Authentication in Nodejs"},{"location":"v2/authentication/server-nodejs/#authentication-in-nodejs","text":"","title":"Authentication in Nodejs"},{"location":"v2/authentication/server-nodejs/#sharepoint-app-registration","text":"Due to a recent change in how SPO is configured NEW tenants will have ACS authentication disabled by default. You can read more details in this article . For testing we recommend using MSAL Certificate Auth . Within the PnPjs testing framework we make use of SharePoint App Registration. This uses the SPFetchClient client from the nodejs package. This client works based on the legacy SharePoint App Registration model making use of a client and secret granted permissions through AppInv.aspx. This method works and at the time of writing has no published end date. See: details on how to register a legacy SharePoint application . import { SPFetchClient } from \"@pnp/nodejs\"; import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\"); }, }, }); // execute a library request as normal const w = await sp.web();","title":"SharePoint App Registration"},{"location":"v2/authentication/server-nodejs/#msal","text":"Added in 2.0.11 You can now use the @azure/msal-node client with PnPjs using MsalFetchClient. You must configure an AAD application with the appropriate permissions for your application. At the time this article was written the msal-node package is not yet GA.","title":"MSAL"},{"location":"v2/authentication/server-nodejs/#call-graph","text":"You can call the Microsoft Graph API with a client id and secret or certificate (see SharePoint example for cert auth) import { graph } from \"@pnp/graph/presets/all\"; // configure your node options graph.setup({ graph: { fetchClientFactory: () => { return new MsalFetchClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant id or common}/\", clientId: \"{guid}\", clientSecret: \"{client secret}\", } }); }, }, }); const userInfo = await graph.users();","title":"Call Graph"},{"location":"v2/authentication/server-nodejs/#call-sharepoint","text":"To call the SharePoint APIs via MSAL you are required to use certificate authentication with your application. Fully covering certificates is outside the scope of these docs, but the following commands were used with openssl to create testing certs for the sample code below. mkdir \\temp cd \\temp openssl req -x509 -newkey rsa:2048 -keyout keytmp.pem -out cert.pem -days 365 -passout pass:HereIsMySuperPass -subj '/C=US/ST=Washington/L=Seattle' openssl rsa -in keytmp.pem -out key.pem -passin pass:HereIsMySuperPass Using the above code you end up with three files, \"cert.pem\", \"key.pem\", and \"keytmp.pem\". The \"cert.pem\" file is uploaded to your AAD application registration. The \"key.pem\" is read as the private key for the configuration. You need to set the baseUrl property when using the MsalFetchClient import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { readFileSync } from \"fs\"; // read in our private key const buffer = readFileSync(\"c:/temp/key.pem\"); // configure node options sp.setup({ sp: { baseUrl: \"https://{my tenant}.sharepoint.com/sites/dev/\", fetchClientFactory: () => { return new MsalFetchClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant id or common}/\", clientCertificate: { thumbprint: \"{certificate thumbprint, displayed in AAD}\", privateKey: buffer.toString(), }, clientId: \"{client id}\", } }, [\"https://{my tenant}.sharepoint.com/.default\"]); // you must set the scope for SharePoint access }, }, }); const w = await sp.web();","title":"Call SharePoint"},{"location":"v2/authentication/server-nodejs/#adal","text":"The AdalFetchClient class depends on the adal-node package to authenticate against Azure AD. The example below outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected. See: More details on the node client import { AdalFetchClient } from \"@pnp/nodejs\"; import { graph } from \"@pnp/graph/presets/all\"; // setup the client using graph setup function graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{tenant}\", \"{app id}\", \"{app secret}\"); }, }, }); // execute a library request as normal const g = await graph.groups(); console.log(JSON.stringify(g, null, 4));","title":"ADAL"},{"location":"v2/authentication/sp-app-registration/","text":"Legacy SharePoint App Registration \u00b6 This section outlines how to register for a client id and secret for use in the above code. Due to a recent change in how SPO is configured NEW tenants will have ACS authentication disabled by default. You can read more details in this article . For testing we recommend using MSAL Certificate Authentication . Register An Add-In \u00b6 Before you can begin running tests you need to register a low-trust add-in with SharePoint. This is primarily designed for Office 365, but can work on-premises if you configure your farm accordingly . Navigation to {site url}/_layouts/appregnew.aspx Click \"Generate\" for both the Client Id and Secret values Give you add-in a title, this can be anything but will let you locate it in the list of add-in permissions Provide a fake value for app domain and redirect uri Click \"Create\" Copy the returned block of text containing the client id and secret as well as app name for your records and later in this article. Grant Your Add-In Permissions \u00b6 Now that we have created an add-in registration we need to tell SharePoint what permissions it can use. Due to an update in SharePoint Online you now have to register add-ins with certain permissions in the admin site . Navigate to {admin site url}/_layouts/appinv.aspx Paste your client id from the above section into the App Id box and click \"Lookup\" You should see the information populated into the form from the last section, if not ensure you have the correct id value Paste the below XML into the permissions request xml box and hit \"Create\" You should get a confirmation message. Note that the above XML will grant full tenant control. This is OK for testing, but you should grant only those permissions necessary for your application in production.","title":"Legacy SharePoint App Registration"},{"location":"v2/authentication/sp-app-registration/#legacy-sharepoint-app-registration","text":"This section outlines how to register for a client id and secret for use in the above code. Due to a recent change in how SPO is configured NEW tenants will have ACS authentication disabled by default. You can read more details in this article . For testing we recommend using MSAL Certificate Authentication .","title":"Legacy SharePoint App Registration"},{"location":"v2/authentication/sp-app-registration/#register-an-add-in","text":"Before you can begin running tests you need to register a low-trust add-in with SharePoint. This is primarily designed for Office 365, but can work on-premises if you configure your farm accordingly . Navigation to {site url}/_layouts/appregnew.aspx Click \"Generate\" for both the Client Id and Secret values Give you add-in a title, this can be anything but will let you locate it in the list of add-in permissions Provide a fake value for app domain and redirect uri Click \"Create\" Copy the returned block of text containing the client id and secret as well as app name for your records and later in this article.","title":"Register An Add-In"},{"location":"v2/authentication/sp-app-registration/#grant-your-add-in-permissions","text":"Now that we have created an add-in registration we need to tell SharePoint what permissions it can use. Due to an update in SharePoint Online you now have to register add-ins with certain permissions in the admin site . Navigate to {admin site url}/_layouts/appinv.aspx Paste your client id from the above section into the App Id box and click \"Lookup\" You should see the information populated into the form from the last section, if not ensure you have the correct id value Paste the below XML into the permissions request xml box and hit \"Create\" You should get a confirmation message. Note that the above XML will grant full tenant control. This is OK for testing, but you should grant only those permissions necessary for your application in production.","title":"Grant Your Add-In Permissions"},{"location":"v2/common/","text":"@pnp/core \u00b6 The common modules provides a set of utilities classes and reusable building blocks used throughout the @pnp modules. They can be used within your applications as well. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/core --save Import and use functionality, see details on modules below. import { getGUID } from \"@pnp/core\"; console.log(getGUID()); Exports \u00b6 collections libconfig netutil storage util Custom HttpClient","title":"@pnp/core"},{"location":"v2/common/#pnpcore","text":"The common modules provides a set of utilities classes and reusable building blocks used throughout the @pnp modules. They can be used within your applications as well.","title":"@pnp/core"},{"location":"v2/common/#getting-started","text":"Install the library and required dependencies npm install @pnp/core --save Import and use functionality, see details on modules below. import { getGUID } from \"@pnp/core\"; console.log(getGUID());","title":"Getting Started"},{"location":"v2/common/#exports","text":"collections libconfig netutil storage util Custom HttpClient","title":"Exports"},{"location":"v2/common/collections/","text":"@pnp/core/collections \u00b6 The collections module provides typings and classes related to working with dictionaries. TypedHash \u00b6 Interface used to described an object with string keys corresponding to values of type T export interface TypedHash { [key: string]: T; } objectToMap \u00b6 Converts a plain object to a Map instance const map = objectToMap({ a: \"b\", c: \"d\"}); mergeMaps \u00b6 Merges two or more maps, overwriting values with the same key. Last value in wins. const m1 = new Map(); const m2 = new Map(); const m3 = new Map(); const m4 = new Map(); const m = mergeMaps(m1, m2, m3, m4);","title":"@pnp/core/collections"},{"location":"v2/common/collections/#pnpcorecollections","text":"The collections module provides typings and classes related to working with dictionaries.","title":"@pnp/core/collections"},{"location":"v2/common/collections/#typedhash","text":"Interface used to described an object with string keys corresponding to values of type T export interface TypedHash { [key: string]: T; }","title":"TypedHash"},{"location":"v2/common/collections/#objecttomap","text":"Converts a plain object to a Map instance const map = objectToMap({ a: \"b\", c: \"d\"});","title":"objectToMap"},{"location":"v2/common/collections/#mergemaps","text":"Merges two or more maps, overwriting values with the same key. Last value in wins. const m1 = new Map(); const m2 = new Map(); const m3 = new Map(); const m4 = new Map(); const m = mergeMaps(m1, m2, m3, m4);","title":"mergeMaps"},{"location":"v2/common/custom-httpclientimpl/","text":"Custom HttpClientImpl \u00b6 This should be considered an advanced topic and creating a custom HttpClientImpl is not something you will likely need to do. Also, we don't offer support beyond this article for writing your own implementation. It is possible you may need complete control over the sending and receiving of requests. Before you get started read and understand the fetch specification as you are essentially writing a custom fetch implementation. The first step (second if you read the fetch spec as mentioned just above) is to understand the interface you need to implement, HttpClientImpl. export interface HttpClientImpl { fetch(url: string, options: FetchOptions): Promise; } There is a single method \"fetch\" which takes a url string and a set of options. These options can be just about anything but are constrained within the library to the FetchOptions interface. export interface FetchOptions { method?: string; headers?: HeadersInit | { [index: string]: string }; body?: BodyInit; mode?: string | RequestMode; credentials?: string | RequestCredentials; cache?: string | RequestCache; } So you will need to handle any of those options along with the provided url when sending your request. The library will expect your implementation to return a Promise that resolves to a Response defined by the fetch specification - which you've already read \ud83d\udc4d. Using Your Custom HttpClientImpl \u00b6 Once you have written your implementation using it on your requests is done by setting it in the global library configuration: import { setup } from \"@pnp/core\"; import { sp, Web } from \"@pnp/sp\"; import { MyAwesomeClient } from \"./awesomeclient\"; sp.setup({ sp: { fetchClientFactory: () => { return new MyAwesomeClient(); } } }); let w = new Web(\"{site url}\"); // this request will use your client. const result = await w.select(\"Title\")(); console.log(result); Subclassing is Better \u00b6 You can of course inherit from one of the implementations available within the @pnp scope if you just need to say add a header or need to do something to every request sent. Perhaps some advanced logging. This approach will save you from needing to fully write a fetch implementation. A FINAL NOTE \u00b6 Whatever you do, do not write a client that uses a client id and secret and exposes them on the client side. Client Id and Secret should only ever be used on a server, never exposed to clients as anyone with those values has the full permissions granted to that id and secret.","title":"Custom HttpClientImpl"},{"location":"v2/common/custom-httpclientimpl/#custom-httpclientimpl","text":"This should be considered an advanced topic and creating a custom HttpClientImpl is not something you will likely need to do. Also, we don't offer support beyond this article for writing your own implementation. It is possible you may need complete control over the sending and receiving of requests. Before you get started read and understand the fetch specification as you are essentially writing a custom fetch implementation. The first step (second if you read the fetch spec as mentioned just above) is to understand the interface you need to implement, HttpClientImpl. export interface HttpClientImpl { fetch(url: string, options: FetchOptions): Promise; } There is a single method \"fetch\" which takes a url string and a set of options. These options can be just about anything but are constrained within the library to the FetchOptions interface. export interface FetchOptions { method?: string; headers?: HeadersInit | { [index: string]: string }; body?: BodyInit; mode?: string | RequestMode; credentials?: string | RequestCredentials; cache?: string | RequestCache; } So you will need to handle any of those options along with the provided url when sending your request. The library will expect your implementation to return a Promise that resolves to a Response defined by the fetch specification - which you've already read \ud83d\udc4d.","title":"Custom HttpClientImpl"},{"location":"v2/common/custom-httpclientimpl/#using-your-custom-httpclientimpl","text":"Once you have written your implementation using it on your requests is done by setting it in the global library configuration: import { setup } from \"@pnp/core\"; import { sp, Web } from \"@pnp/sp\"; import { MyAwesomeClient } from \"./awesomeclient\"; sp.setup({ sp: { fetchClientFactory: () => { return new MyAwesomeClient(); } } }); let w = new Web(\"{site url}\"); // this request will use your client. const result = await w.select(\"Title\")(); console.log(result);","title":"Using Your Custom HttpClientImpl"},{"location":"v2/common/custom-httpclientimpl/#subclassing-is-better","text":"You can of course inherit from one of the implementations available within the @pnp scope if you just need to say add a header or need to do something to every request sent. Perhaps some advanced logging. This approach will save you from needing to fully write a fetch implementation.","title":"Subclassing is Better"},{"location":"v2/common/custom-httpclientimpl/#a-final-note","text":"Whatever you do, do not write a client that uses a client id and secret and exposes them on the client side. Client Id and Secret should only ever be used on a server, never exposed to clients as anyone with those values has the full permissions granted to that id and secret.","title":"A FINAL NOTE"},{"location":"v2/common/libconfig/","text":"@pnp/core/libconfig \u00b6 Contains the shared classes and interfaces used to configure the libraries. These bases classes are expanded on in dependent libraries with the core configuration defined here. This module exposes an instance of the RuntimeConfigImpl class: RuntimeConfig. This configuration object can be referenced and contains the global configuration shared across the libraries. You can also extend the configuration for use within your own applications. ILibraryConfiguration Interface \u00b6 Defines the shared configurable values used across the library as shown below. Each of these has a default value as shown below export interface ILibraryConfiguration { /** * Allows caching to be global disabled, default: false */ globalCacheDisable?: boolean; /** * Defines the default store used by the usingCaching method, default: session */ defaultCachingStore?: \"session\" | \"local\"; /** * Defines the default timeout in seconds used by the usingCaching method, default 30 */ defaultCachingTimeoutSeconds?: number; /** * If true a timeout expired items will be removed from the cache in intervals determined by cacheTimeoutInterval */ enableCacheExpiration?: boolean; /** * Determines the interval in milliseconds at which the cache is checked to see if items have expired (min: 100) */ cacheExpirationIntervalMilliseconds?: number; /** * Used to supply the current context from an SPFx webpart to the library */ spfxContext?: any; } RuntimeConfigImpl \u00b6 The class which implements the runtime configuration management as well as sets the default values used within the library. At its heart lies a Dictionary used to track the configuration values. The keys will match the values in the interface or plain object passed to the extend method. assign \u00b6 The assign method is used to add configuration to the global configuration instance. You can pass it any plain object with string keys and those values will be added. Any existing values will be overwritten based on the keys. Last value in wins. For a more detailed scenario of using the RuntimeConfig instance in your own application please see the section below \"Using RuntimeConfig within your application\". Note there are no methods to remove/clear the global config as it should be considered fairly static as frequent updates may have unpredictable side effects as it is a global shared object. Generally it should be set at the start of your application. import { RuntimeConfig } from \"@pnp/core\"; // add your custom keys to the global configuration // note you can use object hashes as values RuntimeConfig.assign({ \"myKey1\": \"value 1\", \"myKey2\": { \"subKey\": \"sub value 1\", \"subKey2\": \"sub value 2\", }, }); // read your custom values const v = RuntimeConfig.get(\"myKey1\"); // \"value 1\" Using RuntimeConfig within your Application \u00b6 If you have a set of properties you will access very frequently it may be desirable to implement your own configuration object and expose those values as properties. To do so you will need to create an interface for your configuration (optional) and a wrapper class for RuntimeConfig to expose your properties import { ILibraryConfiguration, RuntimeConfig, ITypedHash } from \"@pnp/core\"; // first we create our own interface by extending LibraryConfiguration. This allows your class to accept all the values with correct type checking. Note, because // TypeScript allows you to extend from multiple interfaces you can build a complex configuration definition from many sub definitions. // create the interface of your properties // by creating this separately you allows others to compose your parts into their own config interface MyConfigurationPart { // you can create a grouped definition and access your settings as an object // keys can be optional or required as defined by your interface my?: { prop1?: string; prop2?: string; } // and/or define multiple top level properties (beware key collision) // it is good practice to use a unique prefix myProp1: string; myProp2: number; } // now create a combined interface interface MyConfiguration extends ILibraryConfiguration, MyConfigurationPart { } // now create a wrapper object and expose your properties class MyRuntimeConfigImpl { // exposing a nested property public get prop1(): ITypedHash { const myPart = RuntimeConfig.get(\"my\"); if (myPart !== null && typeof myPart !== \"undefined\" && typeof myPart.prop1 !== \"undefined\") { return myPart.prop1; } return {}; } // exposing a root level property public get myProp1(): string | null { let myProp1 = RuntimeConfig.get(\"myProp1\"); if (myProp1 === null) { myProp1 = \"some default value\"; } return myProp1; } setup(config: MyConfiguration): void { RuntimeConfig.assign(config); } } // create a single static instance of your impl class export let MyRuntimeConfig = new MyRuntimeConfigImpl(); Now in other files you can use and set your configuration with a typed interface and properties import { MyRuntimeConfig } from \"{location of module}\"; MyRuntimeConfig.setup({ my: { prop1: \"hello\", }, }); const value = MyRuntimeConfig.myProp1; // \"hello\"","title":"@pnp/core/libconfig"},{"location":"v2/common/libconfig/#pnpcorelibconfig","text":"Contains the shared classes and interfaces used to configure the libraries. These bases classes are expanded on in dependent libraries with the core configuration defined here. This module exposes an instance of the RuntimeConfigImpl class: RuntimeConfig. This configuration object can be referenced and contains the global configuration shared across the libraries. You can also extend the configuration for use within your own applications.","title":"@pnp/core/libconfig"},{"location":"v2/common/libconfig/#ilibraryconfiguration-interface","text":"Defines the shared configurable values used across the library as shown below. Each of these has a default value as shown below export interface ILibraryConfiguration { /** * Allows caching to be global disabled, default: false */ globalCacheDisable?: boolean; /** * Defines the default store used by the usingCaching method, default: session */ defaultCachingStore?: \"session\" | \"local\"; /** * Defines the default timeout in seconds used by the usingCaching method, default 30 */ defaultCachingTimeoutSeconds?: number; /** * If true a timeout expired items will be removed from the cache in intervals determined by cacheTimeoutInterval */ enableCacheExpiration?: boolean; /** * Determines the interval in milliseconds at which the cache is checked to see if items have expired (min: 100) */ cacheExpirationIntervalMilliseconds?: number; /** * Used to supply the current context from an SPFx webpart to the library */ spfxContext?: any; }","title":"ILibraryConfiguration Interface"},{"location":"v2/common/libconfig/#runtimeconfigimpl","text":"The class which implements the runtime configuration management as well as sets the default values used within the library. At its heart lies a Dictionary used to track the configuration values. The keys will match the values in the interface or plain object passed to the extend method.","title":"RuntimeConfigImpl"},{"location":"v2/common/libconfig/#assign","text":"The assign method is used to add configuration to the global configuration instance. You can pass it any plain object with string keys and those values will be added. Any existing values will be overwritten based on the keys. Last value in wins. For a more detailed scenario of using the RuntimeConfig instance in your own application please see the section below \"Using RuntimeConfig within your application\". Note there are no methods to remove/clear the global config as it should be considered fairly static as frequent updates may have unpredictable side effects as it is a global shared object. Generally it should be set at the start of your application. import { RuntimeConfig } from \"@pnp/core\"; // add your custom keys to the global configuration // note you can use object hashes as values RuntimeConfig.assign({ \"myKey1\": \"value 1\", \"myKey2\": { \"subKey\": \"sub value 1\", \"subKey2\": \"sub value 2\", }, }); // read your custom values const v = RuntimeConfig.get(\"myKey1\"); // \"value 1\"","title":"assign"},{"location":"v2/common/libconfig/#using-runtimeconfig-within-your-application","text":"If you have a set of properties you will access very frequently it may be desirable to implement your own configuration object and expose those values as properties. To do so you will need to create an interface for your configuration (optional) and a wrapper class for RuntimeConfig to expose your properties import { ILibraryConfiguration, RuntimeConfig, ITypedHash } from \"@pnp/core\"; // first we create our own interface by extending LibraryConfiguration. This allows your class to accept all the values with correct type checking. Note, because // TypeScript allows you to extend from multiple interfaces you can build a complex configuration definition from many sub definitions. // create the interface of your properties // by creating this separately you allows others to compose your parts into their own config interface MyConfigurationPart { // you can create a grouped definition and access your settings as an object // keys can be optional or required as defined by your interface my?: { prop1?: string; prop2?: string; } // and/or define multiple top level properties (beware key collision) // it is good practice to use a unique prefix myProp1: string; myProp2: number; } // now create a combined interface interface MyConfiguration extends ILibraryConfiguration, MyConfigurationPart { } // now create a wrapper object and expose your properties class MyRuntimeConfigImpl { // exposing a nested property public get prop1(): ITypedHash { const myPart = RuntimeConfig.get(\"my\"); if (myPart !== null && typeof myPart !== \"undefined\" && typeof myPart.prop1 !== \"undefined\") { return myPart.prop1; } return {}; } // exposing a root level property public get myProp1(): string | null { let myProp1 = RuntimeConfig.get(\"myProp1\"); if (myProp1 === null) { myProp1 = \"some default value\"; } return myProp1; } setup(config: MyConfiguration): void { RuntimeConfig.assign(config); } } // create a single static instance of your impl class export let MyRuntimeConfig = new MyRuntimeConfigImpl(); Now in other files you can use and set your configuration with a typed interface and properties import { MyRuntimeConfig } from \"{location of module}\"; MyRuntimeConfig.setup({ my: { prop1: \"hello\", }, }); const value = MyRuntimeConfig.myProp1; // \"hello\"","title":"Using RuntimeConfig within your Application"},{"location":"v2/common/netutil/","text":"@pnp/core/net \u00b6 This module contains a set of classes and interfaces used to characterize shared http interactions and configuration of the libraries. Some of the interfaces are described below (many have no use outside the library) as well as several classes. Interfaces \u00b6 HttpClientImpl \u00b6 Defines an implementation of an Http Client within the context of @pnp. This being a class with a a single method \"fetch\" takes a URL and options. It returns a Promise . Used primarily with the shared request pipeline to define the client used to make the actual request. You can write your own custom implementation if needed. RequestClient \u00b6 An abstraction that contains specific methods related to each of the primary request methods get, post, patch, delete as well as fetch and fetchRaw. The difference between fetch and fetchRaw is that a client may include additional logic or processing in fetch, where fetchRaw should be a direct call to the underlying HttpClientImpl fetch method. Classes \u00b6 This module export two classes of note, FetchClient and BearerTokenFetchClient. Both implement HttpClientImpl. FetchClient \u00b6 Basic implementation that calls the global (window) fetch method with no additional processing. import { FetchClient } from \"@pnp/core\"; const client = new FetchClient(); client.fetch(\"{url}\", {}); BearerTokenFetchClient \u00b6 A simple implementation that takes a provided authentication token and adds the Authentication Bearer header to the request. No other processing is done and the token is treated as a static string. import { BearerTokenFetchClient } from \"@pnp/core\"; const client = new BearerTokenFetchClient(\"{authentication token}\"); client.fetch(\"{url}\", {});","title":"@pnp/core/net"},{"location":"v2/common/netutil/#pnpcorenet","text":"This module contains a set of classes and interfaces used to characterize shared http interactions and configuration of the libraries. Some of the interfaces are described below (many have no use outside the library) as well as several classes.","title":"@pnp/core/net"},{"location":"v2/common/netutil/#interfaces","text":"","title":"Interfaces"},{"location":"v2/common/netutil/#httpclientimpl","text":"Defines an implementation of an Http Client within the context of @pnp. This being a class with a a single method \"fetch\" takes a URL and options. It returns a Promise . Used primarily with the shared request pipeline to define the client used to make the actual request. You can write your own custom implementation if needed.","title":"HttpClientImpl"},{"location":"v2/common/netutil/#requestclient","text":"An abstraction that contains specific methods related to each of the primary request methods get, post, patch, delete as well as fetch and fetchRaw. The difference between fetch and fetchRaw is that a client may include additional logic or processing in fetch, where fetchRaw should be a direct call to the underlying HttpClientImpl fetch method.","title":"RequestClient"},{"location":"v2/common/netutil/#classes","text":"This module export two classes of note, FetchClient and BearerTokenFetchClient. Both implement HttpClientImpl.","title":"Classes"},{"location":"v2/common/netutil/#fetchclient","text":"Basic implementation that calls the global (window) fetch method with no additional processing. import { FetchClient } from \"@pnp/core\"; const client = new FetchClient(); client.fetch(\"{url}\", {});","title":"FetchClient"},{"location":"v2/common/netutil/#bearertokenfetchclient","text":"A simple implementation that takes a provided authentication token and adds the Authentication Bearer header to the request. No other processing is done and the token is treated as a static string. import { BearerTokenFetchClient } from \"@pnp/core\"; const client = new BearerTokenFetchClient(\"{authentication token}\"); client.fetch(\"{url}\", {});","title":"BearerTokenFetchClient"},{"location":"v2/common/storage/","text":"@pnp/core/storage \u00b6 This module provides a thin wrapper over the browser storage options, local and session. If neither option is available it shims storage with a non-persistent in memory polyfill. Optionally through configuration you can activate expiration. Sample usage is shown below. PnPClientStorage \u00b6 The main export of this module, contains properties representing local and session storage. import { PnPClientStorage } from \"@pnp/core\"; const storage = new PnPClientStorage(); const myvalue = storage.local.get(\"mykey\"); PnPClientStorageWrapper \u00b6 Each of the storage locations (session and local) are wrapped with this helper class. You can use it directly, but generally it would be used from an instance of PnPClientStorage as shown below. These examples all use local storage, the operations are identical for session storage. import { PnPClientStorage } from \"@pnp/core\"; const storage = new PnPClientStorage(); // get a value from storage const value = storage.local.get(\"mykey\"); // put a value into storage storage.local.put(\"mykey2\", \"my value\"); // put a value into storage with an expiration storage.local.put(\"mykey2\", \"my value\", new Date()); // put a simple object into storage // because JSON.stringify is used to package the object we do NOT do a deep rehydration of stored objects storage.local.put(\"mykey3\", { key: \"value\", key2: \"value2\", }); // remove a value from storage storage.local.delete(\"mykey3\"); // get an item or add it if it does not exist // returns a promise in case you need time to get the value for storage // optionally takes a third parameter specifying the expiration storage.local.getOrPut(\"mykey4\", () => { return Promise.resolve(\"value\"); }); // delete expired items storage.local.deleteExpired(); Cache Expiration \u00b6 The ability remove of expired items based on a configured timeout can help if the cache is filling up. This can be accomplished in two ways. The first is to explicitly call the new deleteExpired method on the cache you wish to clear. A suggested usage is to add this into your page init code as clearing expired items once per page load is likely sufficient. import { PnPClientStorage } from \"@pnp/core\"; const storage = new PnPClientStorage(); // session storage storage.session.deleteExpired(); // local storage storage.local.deleteExpired(); // this returns a promise, so you can perform some activity after the expired items are removed: storage.local.deleteExpired().then(_ => { // init my application }); The second method is to enable automated cache expiration through global config. Setting the enableCacheExpiration property to true will enable the timer. Optionally you can set the interval at which the cache is checked via the cacheExpirationIntervalMilliseconds property, by default 750 milliseconds is used. We enforce a minimum of 300 milliseconds as this functionality is enabled via setTimeout and there is little value in having an excessive number of cache checks. This method is more appropriate for a single page application where the page is infrequently reloaded and many cached operations are performed. There is no advantage to enabling cache expiration unless you are experiencing cache storage space pressure in a long running page - and you may see a performance hit due to the use of setTimeout. import { setup } from \"@pnp/core\"; setup({ enableCacheExpiration: true, cacheExpirationIntervalMilliseconds: 1000, // optional });","title":"@pnp/core/storage"},{"location":"v2/common/storage/#pnpcorestorage","text":"This module provides a thin wrapper over the browser storage options, local and session. If neither option is available it shims storage with a non-persistent in memory polyfill. Optionally through configuration you can activate expiration. Sample usage is shown below.","title":"@pnp/core/storage"},{"location":"v2/common/storage/#pnpclientstorage","text":"The main export of this module, contains properties representing local and session storage. import { PnPClientStorage } from \"@pnp/core\"; const storage = new PnPClientStorage(); const myvalue = storage.local.get(\"mykey\");","title":"PnPClientStorage"},{"location":"v2/common/storage/#pnpclientstoragewrapper","text":"Each of the storage locations (session and local) are wrapped with this helper class. You can use it directly, but generally it would be used from an instance of PnPClientStorage as shown below. These examples all use local storage, the operations are identical for session storage. import { PnPClientStorage } from \"@pnp/core\"; const storage = new PnPClientStorage(); // get a value from storage const value = storage.local.get(\"mykey\"); // put a value into storage storage.local.put(\"mykey2\", \"my value\"); // put a value into storage with an expiration storage.local.put(\"mykey2\", \"my value\", new Date()); // put a simple object into storage // because JSON.stringify is used to package the object we do NOT do a deep rehydration of stored objects storage.local.put(\"mykey3\", { key: \"value\", key2: \"value2\", }); // remove a value from storage storage.local.delete(\"mykey3\"); // get an item or add it if it does not exist // returns a promise in case you need time to get the value for storage // optionally takes a third parameter specifying the expiration storage.local.getOrPut(\"mykey4\", () => { return Promise.resolve(\"value\"); }); // delete expired items storage.local.deleteExpired();","title":"PnPClientStorageWrapper"},{"location":"v2/common/storage/#cache-expiration","text":"The ability remove of expired items based on a configured timeout can help if the cache is filling up. This can be accomplished in two ways. The first is to explicitly call the new deleteExpired method on the cache you wish to clear. A suggested usage is to add this into your page init code as clearing expired items once per page load is likely sufficient. import { PnPClientStorage } from \"@pnp/core\"; const storage = new PnPClientStorage(); // session storage storage.session.deleteExpired(); // local storage storage.local.deleteExpired(); // this returns a promise, so you can perform some activity after the expired items are removed: storage.local.deleteExpired().then(_ => { // init my application }); The second method is to enable automated cache expiration through global config. Setting the enableCacheExpiration property to true will enable the timer. Optionally you can set the interval at which the cache is checked via the cacheExpirationIntervalMilliseconds property, by default 750 milliseconds is used. We enforce a minimum of 300 milliseconds as this functionality is enabled via setTimeout and there is little value in having an excessive number of cache checks. This method is more appropriate for a single page application where the page is infrequently reloaded and many cached operations are performed. There is no advantage to enabling cache expiration unless you are experiencing cache storage space pressure in a long running page - and you may see a performance hit due to the use of setTimeout. import { setup } from \"@pnp/core\"; setup({ enableCacheExpiration: true, cacheExpirationIntervalMilliseconds: 1000, // optional });","title":"Cache Expiration"},{"location":"v2/common/util/","text":"@pnp/core/util \u00b6 This module contains utility methods that you can import individually from the common library. import { getRandomString, } from \"@pnp/core\"; // use from individually imported method console.log(getRandomString(10)); assign \u00b6 Merges a source object's own enumerable properties into a single target object. Similar to Object.assign, but allows control of overwriting of existing properties. import { assign } from \"@pnp/core\"; let obj1 = { prop: 1, prop2: 2, }; const obj2 = { prop: 4, prop3: 9, }; const example1 = assign(obj1, obj2); // example1 = { prop: 4, prop2: 2, prop3: 9 } //noOverwrite = true stops overwriting existing properties const example2 = assign(obj1, obj2, true); // example2 = { prop: 1, prop2: 2, prop3: 9 } combine \u00b6 Combines any number of paths, normalizing the slashes as required import { combine } from \"@pnp/core\"; // \"https://microsoft.com/something/more\" const paths = combine(\"https://microsoft.com\", \"something\", \"more\"); // \"also/works/with/relative\" const paths2 = combine(\"/also/\", \"/works\", \"with/\", \"/relative\\\\\"); dateAdd \u00b6 Manipulates a date, please see the Stack Overflow discussion from where this method was taken. import { dateAdd } from \"@pnp/core\"; const testDate = new Date(); dateAdd(testDate,'minute',10); getCtxCallback \u00b6 Gets a callback function which will maintain context across async calls. import { getCtxCallback } from \"@pnp/core\"; const contextThis = { myProp: 6, }; function theFunction() { // \"this\" within this function will be the context object supplied // in this case the variable contextThis, so myProp will exist return this.myProp; } const callback = getCtxCallback(contextThis, theFunction); callback(); // returns 6 // You can also supply additional parameters if needed function theFunction2(g: number) { // \"this\" within this function will be the context object supplied // in this case the variable contextThis, so myProp will exist return this.myProp + g; } const callback2 = getCtxCallback(contextThis, theFunction2, 4); callback2(); // returns 10 (6 + 4) getGUID \u00b6 Creates a random guid, please see the Stack Overflow discussion from where this method was taken. import { getGUID } from \"@pnp/core\"; const newGUID = getGUID(); getRandomString \u00b6 Gets a random string consisting of the number of characters requested. import { getRandomString } from \"@pnp/core\"; const randomString = getRandomString(10); hOP \u00b6 Shortcut for Object.hasOwnProperty. Determines if an object has a specified property. import { HttpRequestError } from \"@pnp/queryable\"; import { hOP } from \"@pnp/core\"; export async function handleError(e: Error | HttpRequestError): Promise { //Checks to see if the error object has a property called isHttpRequestError. Returns a bool. if (hOP(e, \"isHttpRequestError\")) { // Handle this type or error } else { // not an HttpRequestError so we do something else } } isArray \u00b6 Determines if a supplied variable represents an array. import { isArray } from \"@pnp/core\"; let x:String[] = [1,2,3]]; if (isArray(x)){ console.log(\"I am an array\"); }else{ console.log(\"I am not an array\"); } isFunc \u00b6 Determines if a supplied variable represents a function. import { isFunc } from \"@pnp/core\"; public testFunction() { console.log(\"test function\"); return } if (isFunc(testFunction)){ console.log(\"this is a function\"); testFunction(); } isUrlAbsolute \u00b6 Determines if a supplied url is absolute and returns true; otherwise returns false. import { isUrlAbsolute } from \"@pnp/core\"; const webPath = 'https://{tenant}.sharepoint.com/sites/dev/'; if (isUrlAbsolute(webPath)){ console.log(\"URL is absolute\"); }else{ console.log(\"URL is not absolute\"); } objectDefinedNotNull \u00b6 Determines if an object is defined and not null. import { objectDefinedNotNull } from \"@pnp/core\"; let obj = { prop: 1 }; if (objectDefinedNotNull(obj)){ console.log(\"Not null\"); }else{ console.log(\"Null\"); } stringIsNullOrEmpty \u00b6 Determines if a supplied string is null or empty. import { stringIsNullOrEmpty } from \"@pnp/core\"; let x:String = \"hello\"; if (stringIsNullOrEmpty(x)){ console.log(\"Null or empty\"); }else{ console.log(\"Not null or empty\"); } Removed \u00b6 Some methods that were no longer used internally by the @pnp libraries have been removed. You can find the source for those methods below for use in your projects should you require. /** * Loads a stylesheet into the current page * * @param path The url to the stylesheet * @param avoidCache If true a value will be appended as a query string to avoid browser caching issues */ public static loadStylesheet(path: string, avoidCache: boolean): void { if (avoidCache) { path += \"?\" + encodeURIComponent((new Date()).getTime().toString()); } const head = document.getElementsByTagName(\"head\"); if (head.length > 0) { const e = document.createElement(\"link\"); head[0].appendChild(e); e.setAttribute(\"type\", \"text/css\"); e.setAttribute(\"rel\", \"stylesheet\"); e.setAttribute(\"href\", path); } } /** * Tests if a url param exists * * @param name The name of the url parameter to check */ public static urlParamExists(name: string): boolean { name = name.replace(/[\\[]/, \"\\\\[\").replace(/[\\]]/, \"\\\\]\"); const regex = new RegExp(\"[\\\\?&]\" + name + \"=([^&#]*)\"); return regex.test(location.search); } /** * Gets a url param value by name * * @param name The name of the parameter for which we want the value */ public static getUrlParamByName(name: string): string { name = name.replace(/[\\[]/, \"\\\\[\").replace(/[\\]]/, \"\\\\]\"); const regex = new RegExp(\"[\\\\?&]\" + name + \"=([^&#]*)\"); const results = regex.exec(location.search); return results == null ? \"\" : decodeURIComponent(results[1].replace(/\\+/g, \" \")); } /** * Gets a url param by name and attempts to parse a bool value * * @param name The name of the parameter for which we want the boolean value */ public static getUrlParamBoolByName(name: string): boolean { const p = this.getUrlParamByName(name); const isFalse = (p === \"\" || /false|0/i.test(p)); return !isFalse; } /** * Inserts the string s into the string target as the index specified by index * * @param target The string into which we will insert s * @param index The location in target to insert s (zero based) * @param s The string to insert into target at position index */ public static stringInsert(target: string, index: number, s: string): string { if (index > 0) { return target.substring(0, index) + s + target.substring(index, target.length); } return s + target; }","title":"@pnp/core/util"},{"location":"v2/common/util/#pnpcoreutil","text":"This module contains utility methods that you can import individually from the common library. import { getRandomString, } from \"@pnp/core\"; // use from individually imported method console.log(getRandomString(10));","title":"@pnp/core/util"},{"location":"v2/common/util/#assign","text":"Merges a source object's own enumerable properties into a single target object. Similar to Object.assign, but allows control of overwriting of existing properties. import { assign } from \"@pnp/core\"; let obj1 = { prop: 1, prop2: 2, }; const obj2 = { prop: 4, prop3: 9, }; const example1 = assign(obj1, obj2); // example1 = { prop: 4, prop2: 2, prop3: 9 } //noOverwrite = true stops overwriting existing properties const example2 = assign(obj1, obj2, true); // example2 = { prop: 1, prop2: 2, prop3: 9 }","title":"assign"},{"location":"v2/common/util/#combine","text":"Combines any number of paths, normalizing the slashes as required import { combine } from \"@pnp/core\"; // \"https://microsoft.com/something/more\" const paths = combine(\"https://microsoft.com\", \"something\", \"more\"); // \"also/works/with/relative\" const paths2 = combine(\"/also/\", \"/works\", \"with/\", \"/relative\\\\\");","title":"combine"},{"location":"v2/common/util/#dateadd","text":"Manipulates a date, please see the Stack Overflow discussion from where this method was taken. import { dateAdd } from \"@pnp/core\"; const testDate = new Date(); dateAdd(testDate,'minute',10);","title":"dateAdd"},{"location":"v2/common/util/#getctxcallback","text":"Gets a callback function which will maintain context across async calls. import { getCtxCallback } from \"@pnp/core\"; const contextThis = { myProp: 6, }; function theFunction() { // \"this\" within this function will be the context object supplied // in this case the variable contextThis, so myProp will exist return this.myProp; } const callback = getCtxCallback(contextThis, theFunction); callback(); // returns 6 // You can also supply additional parameters if needed function theFunction2(g: number) { // \"this\" within this function will be the context object supplied // in this case the variable contextThis, so myProp will exist return this.myProp + g; } const callback2 = getCtxCallback(contextThis, theFunction2, 4); callback2(); // returns 10 (6 + 4)","title":"getCtxCallback"},{"location":"v2/common/util/#getguid","text":"Creates a random guid, please see the Stack Overflow discussion from where this method was taken. import { getGUID } from \"@pnp/core\"; const newGUID = getGUID();","title":"getGUID"},{"location":"v2/common/util/#getrandomstring","text":"Gets a random string consisting of the number of characters requested. import { getRandomString } from \"@pnp/core\"; const randomString = getRandomString(10);","title":"getRandomString"},{"location":"v2/common/util/#hop","text":"Shortcut for Object.hasOwnProperty. Determines if an object has a specified property. import { HttpRequestError } from \"@pnp/queryable\"; import { hOP } from \"@pnp/core\"; export async function handleError(e: Error | HttpRequestError): Promise { //Checks to see if the error object has a property called isHttpRequestError. Returns a bool. if (hOP(e, \"isHttpRequestError\")) { // Handle this type or error } else { // not an HttpRequestError so we do something else } }","title":"hOP"},{"location":"v2/common/util/#isarray","text":"Determines if a supplied variable represents an array. import { isArray } from \"@pnp/core\"; let x:String[] = [1,2,3]]; if (isArray(x)){ console.log(\"I am an array\"); }else{ console.log(\"I am not an array\"); }","title":"isArray"},{"location":"v2/common/util/#isfunc","text":"Determines if a supplied variable represents a function. import { isFunc } from \"@pnp/core\"; public testFunction() { console.log(\"test function\"); return } if (isFunc(testFunction)){ console.log(\"this is a function\"); testFunction(); }","title":"isFunc"},{"location":"v2/common/util/#isurlabsolute","text":"Determines if a supplied url is absolute and returns true; otherwise returns false. import { isUrlAbsolute } from \"@pnp/core\"; const webPath = 'https://{tenant}.sharepoint.com/sites/dev/'; if (isUrlAbsolute(webPath)){ console.log(\"URL is absolute\"); }else{ console.log(\"URL is not absolute\"); }","title":"isUrlAbsolute"},{"location":"v2/common/util/#objectdefinednotnull","text":"Determines if an object is defined and not null. import { objectDefinedNotNull } from \"@pnp/core\"; let obj = { prop: 1 }; if (objectDefinedNotNull(obj)){ console.log(\"Not null\"); }else{ console.log(\"Null\"); }","title":"objectDefinedNotNull"},{"location":"v2/common/util/#stringisnullorempty","text":"Determines if a supplied string is null or empty. import { stringIsNullOrEmpty } from \"@pnp/core\"; let x:String = \"hello\"; if (stringIsNullOrEmpty(x)){ console.log(\"Null or empty\"); }else{ console.log(\"Not null or empty\"); }","title":"stringIsNullOrEmpty"},{"location":"v2/common/util/#removed","text":"Some methods that were no longer used internally by the @pnp libraries have been removed. You can find the source for those methods below for use in your projects should you require. /** * Loads a stylesheet into the current page * * @param path The url to the stylesheet * @param avoidCache If true a value will be appended as a query string to avoid browser caching issues */ public static loadStylesheet(path: string, avoidCache: boolean): void { if (avoidCache) { path += \"?\" + encodeURIComponent((new Date()).getTime().toString()); } const head = document.getElementsByTagName(\"head\"); if (head.length > 0) { const e = document.createElement(\"link\"); head[0].appendChild(e); e.setAttribute(\"type\", \"text/css\"); e.setAttribute(\"rel\", \"stylesheet\"); e.setAttribute(\"href\", path); } } /** * Tests if a url param exists * * @param name The name of the url parameter to check */ public static urlParamExists(name: string): boolean { name = name.replace(/[\\[]/, \"\\\\[\").replace(/[\\]]/, \"\\\\]\"); const regex = new RegExp(\"[\\\\?&]\" + name + \"=([^&#]*)\"); return regex.test(location.search); } /** * Gets a url param value by name * * @param name The name of the parameter for which we want the value */ public static getUrlParamByName(name: string): string { name = name.replace(/[\\[]/, \"\\\\[\").replace(/[\\]]/, \"\\\\]\"); const regex = new RegExp(\"[\\\\?&]\" + name + \"=([^&#]*)\"); const results = regex.exec(location.search); return results == null ? \"\" : decodeURIComponent(results[1].replace(/\\+/g, \" \")); } /** * Gets a url param by name and attempts to parse a bool value * * @param name The name of the parameter for which we want the boolean value */ public static getUrlParamBoolByName(name: string): boolean { const p = this.getUrlParamByName(name); const isFalse = (p === \"\" || /false|0/i.test(p)); return !isFalse; } /** * Inserts the string s into the string target as the index specified by index * * @param target The string into which we will insert s * @param index The location in target to insert s (zero based) * @param s The string to insert into target at position index */ public static stringInsert(target: string, index: number, s: string): string { if (index > 0) { return target.substring(0, index) + s + target.substring(index, target.length); } return s + target; }","title":"Removed"},{"location":"v2/concepts/configuration/","text":"PnPjs Configuration \u00b6 This article describes the configuration architecture used by the library as well as the settings available. Starting with version 2.1.0 we updated our configuration design to support the ability to isolate settings to individual objects. The first part of this article discusses the newer design, you can read about the pre v2.1.0 configuration further down. Post v2.1.0 \u00b6 Architecture \u00b6 Starting from v2.1.0 we have modified our configuration design to allow for configuring individual queryable objects. Backward Compatibility \u00b6 If you have no need to use the isolated runtimes introduced in 2.1.0 then you should see no change in library behavior from prior versions. You can continue to refer to the pre v2.1.0 configuration section - and if you see any issues please let us know. All of the available settings as described below remain, unchanged. If you previously used our internal configuration classes directly RuntimeConfigImpl, SPRuntimeConfigImpl, or GraphRuntimeConfigImpl they no longer exist. We do not consider this a breaking change as they were meant to be internal and their direct use was not documented. This includes the concrete default instances RuntimeConfig, SPRuntimeConfig, and GraphRuntimeConfig. Isolated Runtimes \u00b6 You can create an isolated runtime when using either the sp or graph libraries. What this does is create an isolated set of properties and behaviors specific to a given fluent chain. Have a look at this basic example below: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // create an isolated sp root instance const isolatedSP = await sp.createIsolated(); // this configuration applies to all objects created from \"sp\" sp.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/\", }, }); // this configuration applies to all objects created from \"isolatedSP\" isolatedSP.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/sites/dev\", }, }); // details for the web at https://mytenant.sharepoint.com/ const web1 = await sp.web(); // details for the web at https://mytenant.sharepoint.com/sites/dev const web2 = await isolatedSP.web(); This configuration is supplied to all objects down a given fluent chain: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // create an isolated sp root instance const isolatedSP = await sp.createIsolated(); // this configuraiton applies to all objects created from \"sp\" sp.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/\", }, }); // this configuraiton applies to all objects created from \"isolatedSP\" isolatedSP.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/sites/dev\", }, }); // details for the lists at https://mytenant.sharepoint.com/ const lists1 = await sp.web.lists(); // details for the lists at https://mytenant.sharepoint.com/sites/dev const lists2 = await isolatedSP.web.lists(); createIsolated \u00b6 The createIsolated method is used to establish the isolated runtime for a given instance of either the sp or graph libraries. Once created it is no longer connected to the default instance and if you have common settings that must be updated you would need to update them across each isolated instance, this is by design. Currently sp and graph createIsolated methods accept the same init, but we have broken them out to make thing clear. All properties of the init object are optional. Any properties provided will overwrite those cloned from the default if cloneGlobal is true. If cloneGlobal is false you start with an empty config containing only the core defaults . sp.createIsolated \u00b6 import { sp, ISPConfiguration } from \"@pnp/sp\"; // accept all the defaults, will clone any settings from sp const isolatedSP = await sp.createIsolated(); // - specify all the config options, using the ISPConfiguration interface to type the config // - setting baseUrl in the root is equivelent to setting it with sp: { baseUrl: }, it is provided as a shortcut as this seemed to be a common use case // - if you set them both the baseUrl in the root will be used. // - you can set some or all of the settings in config and if you clone from the global the ones you specify will overwrite the cloned values // - for example your global config can specify everything and your isolated config could specify a different fetchClientFactory, see node example below const isolatedSP = await sp.createIsolated({ baseUrl: \"https://mytenant.sharepoint.com\", cloneGlobal: false, config: { cacheExpirationIntervalMilliseconds: 1000, sp: { baseUrl: \"https://mytenant.sharepoint.com\", fetchClientFactory: () => void(0), headers: { \"X-AnotherHeader\": \"54321\", }, }, spfxContext: this.context, // only valid within SPFx }, options: { headers: { \"X-SomeHeader\": \"12345\", }, }, }); Defaults Name Default baseUrl \"\" cloneGlobal true config {} options {} graph.createIsolated \u00b6 import { graph, IGraphConfiguration } from \"@pnp/graph\"; // - specify all the config options, using the IGraphConfiguration interface to type the config // - setting baseUrl in the root is restricted to \"v1.0\" or \"beta\". If you need to specify a different absolute url should use config.graph.baseUrl // - in practice you should use one or the other. You can always swap Graph api version using IGraphQueryable.setEndpoint // - you can set some or all of the settings in config and if you clone from the global the ones you specify will overwrite the cloned values // - for example your global config can specify everything and your isolated config could specify a different fetchClientFactory, see node example below const isolatedGraph = await graph.createIsolated({ baseUrl: \"v1.0\", cloneGlobal: false, config: { cacheExpirationIntervalMilliseconds: 1000, graph: { baseUrl: \"https://graph.microsoft.com\", fetchClientFactory: () => void(0), headers: { \"X-AnotherHeader\": \"54321\", }, }, spfxContext: this.context, // only valid within SPFx }, options: { headers: { \"X-SomeHeader\": \"12345\", }, }, }); Defaults \u00b6 name Default baseUrl \"v1.0\" cloneGlobal true config {} options {} Additional Examples \u00b6 MSAL with Node multiple site requests \u00b6 MSAL Support Added in 2.0.11 In this example you can see how you can setup the MSAL client once and then set a different baseUrl for an isolated instance. More information specific to setting up the MSAL client is available . import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { readFileSync } from \"fs\"; // read in our private key const buffer = readFileSync(\"c:/temp/key.pem\"); // configure node options sp.setup({ sp: { baseUrl: \"https://{my tenant}.sharepoint.com/sites/dev/\", fetchClientFactory: () => { return new MsalFetchClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant id or common}\", clientCertificate: { thumbprint: \"{certificate thumbprint, displayed in AAD}\", privateKey: buffer.toString(), }, clientId: \"{client id}\", } }, [\"https://{my tenant}.sharepoint.com/.default\"]); // you must set the scope for SharePoint access }, }, }); const isolatedSP = await sp.createIsolated({ config: { sp: { baseUrl: \"https://{my tenant}.sharepoint.com/sites/dev2/\", }, }, }); Node multiple site requests \u00b6 Isolated configuration was most requested for scenarios in node where you need to access information in multiple sites. This example shows setting up the global configuration and then creating an isolated config with only the baseUrl updated. import { SPFetchClient } from \"@pnp/nodejs\"; import { ISPConfigurationPart, sp } from \"@pnp/sp\"; sp.setup({ cacheExpirationIntervalMilliseconds: 1000, defaultCachingStore: \"local\", sp: { fetchClientFactory: () => { return new SPFetchClient(\"https://mytenant.sharepoint.com/\", \"id\", \"secret\"); }, headers: { \"X-MyRequiredHeader\": \"SomeValue\", \"X-MyRequiredHeader2\": \"SomeValue\", }, }, }); const isolatedSP = await sp.createIsolated({ config: { sp: { fetchClientFactory: () => { return new SPFetchClient(\"https://mytenant.sharepoint.com/site/dev\", \"id\", \"secret\"); }, }, }, }); Batching \u00b6 All batching functionality works as expected, but you must take care to only associate requests from the same isolated instance as you create the batch. Mixing requests across isolation boundaries is not supported. This applies to sp and graph batching. sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"url1\", \"id\", \"secret\"); }, }, }); const isolated = await sp.createIsolated({ config: { sp: { fetchClientFactory: () => { return new SPFetchClient(\"url2\", \"id\", \"secret\"); }, }, }, }); const batch1 = sp.createBatch(); sp.web.lists.select(\"Title\").top(3).inBatch(batch1)().then(r => console.log(`here 1: ${JSON.stringify(r, null, 2)}`)); sp.web.select(\"Title\").inBatch(batch1)().then(r => console.log(`here 2: ${JSON.stringify(r, null, 2)}`)); await batch1.execute(); const batch2 = isolated.createBatch(); isolated.web.lists.select(\"Title\").top(3).inBatch(batch2)().then(r => console.log(`here 3: ${JSON.stringify(r, null, 2)}`)); isolated.web.select(\"Title\").inBatch(batch2)().then(r => console.log(`here 4: ${JSON.stringify(r, null, 2)}`)); await batch2.execute(); IE11 Mode \u00b6 The IE11 mode setting is always global. There is no scenario we care to support where once instance needs to run in ie11 mode and another does not. Your code either does or does not run in ie11. Prior to v2.1.0 \u00b6 Architecture \u00b6 PnPjs uses an additive configuration design with multiple libraries sharing a single global configuration instance. If you need non-global configuration please see this section . There are three ways to access the setup functionality - through either the common, sp, or graph library's setup method. While the configuration is global the various methods have different typing on their input parameter. You can review the libconfig article for more details on storing your own configuration. Common Configuration \u00b6 The common libary's setup method takes parameters defined by ILibraryConfiguration . The properties and their defaults are listed below, followed by a code sample. You can call setup multiple times and any new values will be added to the existing configuration or replace the previous value if one existed. All values are optional. Name Description Default defaultCachingStore Where will PnPjs store cached data by default (session or local) session defaultCachingTimeoutSeconds The global default value used for cached data timeouts in seconds 60 globalCacheDisable Provides a way to globally within PnPjs disable all caching false enableCacheExpiration If true a timeout expired items will be removed from the cache in intervals determined by cacheTimeoutInterval false cacheExpirationIntervalMilliseconds Determines the interval in milliseconds at which the cache is checked to see if items have expired (min: 100) 750 spfxContext When running in SPFx the current context should always be supplied to PnPjs when available null ie11 If true the library downgrades functionality to work in IE11 false For more information on setting up in SPFx please see the authentication section For more details on ie11 mode please see the topic article import { setup } from \"@pnp/core\"; // called before other code setup({ cacheExpirationIntervalMilliseconds: 15000, defaultCachingStore: \"local\", defaultCachingTimeoutSeconds: 600, enableCacheExpiration: true, globalCacheDisable: false, ie11: false, spfxContext: this.context, // if in SPFx, otherwise leave it out }); SP Configuration \u00b6 The sp library's configuration is defined by the ISPConfiguration interface which extends ILibraryConfiguration. All of the sp values are contained in a top level property named \"sp\". The following table describes the properties with a code sample following. All values are optional. Name Description Default headers Allows you to apply any headers to all calls made by the sp library none baseUrl Allows you to define a base site url for all requests, takes precedence over all other url logic. Must be absolute. none fetchClientFactory Allows you to specify a factory function used to produce IHttpClientImpl instances none There are many examples of using fetchClientFactory available in the authentication section . import { sp } from \"@pnp/sp\"; import { SPFxAdalClient } from \"@pnp/core\"; // note you can still set the global configuration such as ie11 using the same object as // the interface extends ILibraryConfiguration sp.setup({ ie11: false, sp: { baseUrl: \"https://tenant.sharepoint.com/sites/dev\", fetchClientFactory: () => { return new SPFxAdalClient(this.context); }, headers: { \"Accept\": \"application/json;odata=verbose\", \"X-Something\": \"header-value\", }, }, spfxContext: this.context, }); SharePoint Framework \u00b6 You can optionally supply only the SPFx context to the sp configure method. import { sp } from \"@pnp/sp\"; // in SPFx only sp.setup(this.context); Graph Configuration \u00b6 The graph configuration works exactly the same as the sp configuration but is defined by the IGraphConfiguration interface which extends ILibraryConfiguration. All of the graph values are contained in a top level property named \"graph\". The following table describes the properties with a code sample following. All values are optional. Name Description Default headers Allows you to apply any headers to all calls made by the sp library none baseUrl Allows you to define a base site url for all requests, takes precedence over all other url logic. Must be absolute. ( Added in 2.0.8 ) none fetchClientFactory Allows you to specify a factory function used to produce IHttpClientImpl instances none There are many examples of using fetchClientFactory available in the authentication section . import { graph } from \"@pnp/graph\"; import { MsalClientSetup } from \"@pnp/msaljsclient\"; // note you can still set the global configuration such as ie11 using the same object as // the interface extends ILibraryConfiguration graph.setup({ ie11: false, graph: { // we set the GCC url baseUrl: \"https://graph.microsoft.us\", fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/tenant.onmicrosoft.com\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://tenant.sharepoint.com/sites/dev/SitePages/test.aspx\", }, }, [\"Group.Read.All\"]), headers: { \"Accept\": \"application/json;odata=verbose\", \"X-Something\": \"header-value\", }, }, spfxContext: this.context, }); SharePoint Framework \u00b6 You can optionally supply only the SPFx context to the graph configure method. We will attempt to set the baseUrl property from the context - but if that is failing in your environment and you need to call a special cloud (i.e. graph.microsoft.us) please set the baseUrl property. import { graph } from \"@pnp/graph\"; // in SPFx only graph.setup(this.context); Configure Everything At Once \u00b6 In some cases you might want to configure everything in one go. Because the configuration is stored in a single location you can use the common library's setup method and adjust the typings to ensure you are using the correct property names while only having to setup things with a single call. In versions before 2.0.8 ISPConfigurationPart, IGraphConfigurationPart, and ILibraryConfiguration incorrectly were missing the \"I\" prefix. That was fixed in 2.0.8 - but note if you are using an older version of the library you'll need to use the old names. Everything else in the below example works as expected. import { ISPConfigurationPart } from \"@pnp/sp\"; import { IGraphConfigurationPart } from \"@pnp/graph\"; import { ILibraryConfiguration, setup } from \"@pnp/core\"; // you could also include your custom configuration parts export interface AllConfig extends ILibraryConfiguration, ISPConfigurationPart, IGraphConfigurationPart { } // create a single big configuration entry const config: AllConfig = { graph: { baseUrl: \"https://graph.microsoft.us\", }, ie11: false, sp: { baseUrl: \"https://tenant.sharepoint.com/sites/dev\", }, }; setup(config);","title":"PnPjs Configuration"},{"location":"v2/concepts/configuration/#pnpjs-configuration","text":"This article describes the configuration architecture used by the library as well as the settings available. Starting with version 2.1.0 we updated our configuration design to support the ability to isolate settings to individual objects. The first part of this article discusses the newer design, you can read about the pre v2.1.0 configuration further down.","title":"PnPjs Configuration"},{"location":"v2/concepts/configuration/#post-v210","text":"","title":"Post v2.1.0"},{"location":"v2/concepts/configuration/#architecture","text":"Starting from v2.1.0 we have modified our configuration design to allow for configuring individual queryable objects.","title":"Architecture"},{"location":"v2/concepts/configuration/#backward-compatibility","text":"If you have no need to use the isolated runtimes introduced in 2.1.0 then you should see no change in library behavior from prior versions. You can continue to refer to the pre v2.1.0 configuration section - and if you see any issues please let us know. All of the available settings as described below remain, unchanged. If you previously used our internal configuration classes directly RuntimeConfigImpl, SPRuntimeConfigImpl, or GraphRuntimeConfigImpl they no longer exist. We do not consider this a breaking change as they were meant to be internal and their direct use was not documented. This includes the concrete default instances RuntimeConfig, SPRuntimeConfig, and GraphRuntimeConfig.","title":"Backward Compatibility"},{"location":"v2/concepts/configuration/#isolated-runtimes","text":"You can create an isolated runtime when using either the sp or graph libraries. What this does is create an isolated set of properties and behaviors specific to a given fluent chain. Have a look at this basic example below: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // create an isolated sp root instance const isolatedSP = await sp.createIsolated(); // this configuration applies to all objects created from \"sp\" sp.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/\", }, }); // this configuration applies to all objects created from \"isolatedSP\" isolatedSP.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/sites/dev\", }, }); // details for the web at https://mytenant.sharepoint.com/ const web1 = await sp.web(); // details for the web at https://mytenant.sharepoint.com/sites/dev const web2 = await isolatedSP.web(); This configuration is supplied to all objects down a given fluent chain: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // create an isolated sp root instance const isolatedSP = await sp.createIsolated(); // this configuraiton applies to all objects created from \"sp\" sp.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/\", }, }); // this configuraiton applies to all objects created from \"isolatedSP\" isolatedSP.setup({ sp: { baseUrl: \"https://mytenant.sharepoint.com/sites/dev\", }, }); // details for the lists at https://mytenant.sharepoint.com/ const lists1 = await sp.web.lists(); // details for the lists at https://mytenant.sharepoint.com/sites/dev const lists2 = await isolatedSP.web.lists();","title":"Isolated Runtimes"},{"location":"v2/concepts/configuration/#createisolated","text":"The createIsolated method is used to establish the isolated runtime for a given instance of either the sp or graph libraries. Once created it is no longer connected to the default instance and if you have common settings that must be updated you would need to update them across each isolated instance, this is by design. Currently sp and graph createIsolated methods accept the same init, but we have broken them out to make thing clear. All properties of the init object are optional. Any properties provided will overwrite those cloned from the default if cloneGlobal is true. If cloneGlobal is false you start with an empty config containing only the core defaults .","title":"createIsolated"},{"location":"v2/concepts/configuration/#spcreateisolated","text":"import { sp, ISPConfiguration } from \"@pnp/sp\"; // accept all the defaults, will clone any settings from sp const isolatedSP = await sp.createIsolated(); // - specify all the config options, using the ISPConfiguration interface to type the config // - setting baseUrl in the root is equivelent to setting it with sp: { baseUrl: }, it is provided as a shortcut as this seemed to be a common use case // - if you set them both the baseUrl in the root will be used. // - you can set some or all of the settings in config and if you clone from the global the ones you specify will overwrite the cloned values // - for example your global config can specify everything and your isolated config could specify a different fetchClientFactory, see node example below const isolatedSP = await sp.createIsolated({ baseUrl: \"https://mytenant.sharepoint.com\", cloneGlobal: false, config: { cacheExpirationIntervalMilliseconds: 1000, sp: { baseUrl: \"https://mytenant.sharepoint.com\", fetchClientFactory: () => void(0), headers: { \"X-AnotherHeader\": \"54321\", }, }, spfxContext: this.context, // only valid within SPFx }, options: { headers: { \"X-SomeHeader\": \"12345\", }, }, }); Defaults Name Default baseUrl \"\" cloneGlobal true config {} options {}","title":"sp.createIsolated"},{"location":"v2/concepts/configuration/#graphcreateisolated","text":"import { graph, IGraphConfiguration } from \"@pnp/graph\"; // - specify all the config options, using the IGraphConfiguration interface to type the config // - setting baseUrl in the root is restricted to \"v1.0\" or \"beta\". If you need to specify a different absolute url should use config.graph.baseUrl // - in practice you should use one or the other. You can always swap Graph api version using IGraphQueryable.setEndpoint // - you can set some or all of the settings in config and if you clone from the global the ones you specify will overwrite the cloned values // - for example your global config can specify everything and your isolated config could specify a different fetchClientFactory, see node example below const isolatedGraph = await graph.createIsolated({ baseUrl: \"v1.0\", cloneGlobal: false, config: { cacheExpirationIntervalMilliseconds: 1000, graph: { baseUrl: \"https://graph.microsoft.com\", fetchClientFactory: () => void(0), headers: { \"X-AnotherHeader\": \"54321\", }, }, spfxContext: this.context, // only valid within SPFx }, options: { headers: { \"X-SomeHeader\": \"12345\", }, }, });","title":"graph.createIsolated"},{"location":"v2/concepts/configuration/#defaults","text":"name Default baseUrl \"v1.0\" cloneGlobal true config {} options {}","title":"Defaults"},{"location":"v2/concepts/configuration/#additional-examples","text":"","title":"Additional Examples"},{"location":"v2/concepts/configuration/#msal-with-node-multiple-site-requests","text":"MSAL Support Added in 2.0.11 In this example you can see how you can setup the MSAL client once and then set a different baseUrl for an isolated instance. More information specific to setting up the MSAL client is available . import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { readFileSync } from \"fs\"; // read in our private key const buffer = readFileSync(\"c:/temp/key.pem\"); // configure node options sp.setup({ sp: { baseUrl: \"https://{my tenant}.sharepoint.com/sites/dev/\", fetchClientFactory: () => { return new MsalFetchClient({ auth: { authority: \"https://login.microsoftonline.com/{tenant id or common}\", clientCertificate: { thumbprint: \"{certificate thumbprint, displayed in AAD}\", privateKey: buffer.toString(), }, clientId: \"{client id}\", } }, [\"https://{my tenant}.sharepoint.com/.default\"]); // you must set the scope for SharePoint access }, }, }); const isolatedSP = await sp.createIsolated({ config: { sp: { baseUrl: \"https://{my tenant}.sharepoint.com/sites/dev2/\", }, }, });","title":"MSAL with Node multiple site requests"},{"location":"v2/concepts/configuration/#node-multiple-site-requests","text":"Isolated configuration was most requested for scenarios in node where you need to access information in multiple sites. This example shows setting up the global configuration and then creating an isolated config with only the baseUrl updated. import { SPFetchClient } from \"@pnp/nodejs\"; import { ISPConfigurationPart, sp } from \"@pnp/sp\"; sp.setup({ cacheExpirationIntervalMilliseconds: 1000, defaultCachingStore: \"local\", sp: { fetchClientFactory: () => { return new SPFetchClient(\"https://mytenant.sharepoint.com/\", \"id\", \"secret\"); }, headers: { \"X-MyRequiredHeader\": \"SomeValue\", \"X-MyRequiredHeader2\": \"SomeValue\", }, }, }); const isolatedSP = await sp.createIsolated({ config: { sp: { fetchClientFactory: () => { return new SPFetchClient(\"https://mytenant.sharepoint.com/site/dev\", \"id\", \"secret\"); }, }, }, });","title":"Node multiple site requests"},{"location":"v2/concepts/configuration/#batching","text":"All batching functionality works as expected, but you must take care to only associate requests from the same isolated instance as you create the batch. Mixing requests across isolation boundaries is not supported. This applies to sp and graph batching. sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"url1\", \"id\", \"secret\"); }, }, }); const isolated = await sp.createIsolated({ config: { sp: { fetchClientFactory: () => { return new SPFetchClient(\"url2\", \"id\", \"secret\"); }, }, }, }); const batch1 = sp.createBatch(); sp.web.lists.select(\"Title\").top(3).inBatch(batch1)().then(r => console.log(`here 1: ${JSON.stringify(r, null, 2)}`)); sp.web.select(\"Title\").inBatch(batch1)().then(r => console.log(`here 2: ${JSON.stringify(r, null, 2)}`)); await batch1.execute(); const batch2 = isolated.createBatch(); isolated.web.lists.select(\"Title\").top(3).inBatch(batch2)().then(r => console.log(`here 3: ${JSON.stringify(r, null, 2)}`)); isolated.web.select(\"Title\").inBatch(batch2)().then(r => console.log(`here 4: ${JSON.stringify(r, null, 2)}`)); await batch2.execute();","title":"Batching"},{"location":"v2/concepts/configuration/#ie11-mode","text":"The IE11 mode setting is always global. There is no scenario we care to support where once instance needs to run in ie11 mode and another does not. Your code either does or does not run in ie11.","title":"IE11 Mode"},{"location":"v2/concepts/configuration/#prior-to-v210","text":"","title":"Prior to v2.1.0"},{"location":"v2/concepts/configuration/#architecture_1","text":"PnPjs uses an additive configuration design with multiple libraries sharing a single global configuration instance. If you need non-global configuration please see this section . There are three ways to access the setup functionality - through either the common, sp, or graph library's setup method. While the configuration is global the various methods have different typing on their input parameter. You can review the libconfig article for more details on storing your own configuration.","title":"Architecture"},{"location":"v2/concepts/configuration/#common-configuration","text":"The common libary's setup method takes parameters defined by ILibraryConfiguration . The properties and their defaults are listed below, followed by a code sample. You can call setup multiple times and any new values will be added to the existing configuration or replace the previous value if one existed. All values are optional. Name Description Default defaultCachingStore Where will PnPjs store cached data by default (session or local) session defaultCachingTimeoutSeconds The global default value used for cached data timeouts in seconds 60 globalCacheDisable Provides a way to globally within PnPjs disable all caching false enableCacheExpiration If true a timeout expired items will be removed from the cache in intervals determined by cacheTimeoutInterval false cacheExpirationIntervalMilliseconds Determines the interval in milliseconds at which the cache is checked to see if items have expired (min: 100) 750 spfxContext When running in SPFx the current context should always be supplied to PnPjs when available null ie11 If true the library downgrades functionality to work in IE11 false For more information on setting up in SPFx please see the authentication section For more details on ie11 mode please see the topic article import { setup } from \"@pnp/core\"; // called before other code setup({ cacheExpirationIntervalMilliseconds: 15000, defaultCachingStore: \"local\", defaultCachingTimeoutSeconds: 600, enableCacheExpiration: true, globalCacheDisable: false, ie11: false, spfxContext: this.context, // if in SPFx, otherwise leave it out });","title":"Common Configuration"},{"location":"v2/concepts/configuration/#sp-configuration","text":"The sp library's configuration is defined by the ISPConfiguration interface which extends ILibraryConfiguration. All of the sp values are contained in a top level property named \"sp\". The following table describes the properties with a code sample following. All values are optional. Name Description Default headers Allows you to apply any headers to all calls made by the sp library none baseUrl Allows you to define a base site url for all requests, takes precedence over all other url logic. Must be absolute. none fetchClientFactory Allows you to specify a factory function used to produce IHttpClientImpl instances none There are many examples of using fetchClientFactory available in the authentication section . import { sp } from \"@pnp/sp\"; import { SPFxAdalClient } from \"@pnp/core\"; // note you can still set the global configuration such as ie11 using the same object as // the interface extends ILibraryConfiguration sp.setup({ ie11: false, sp: { baseUrl: \"https://tenant.sharepoint.com/sites/dev\", fetchClientFactory: () => { return new SPFxAdalClient(this.context); }, headers: { \"Accept\": \"application/json;odata=verbose\", \"X-Something\": \"header-value\", }, }, spfxContext: this.context, });","title":"SP Configuration"},{"location":"v2/concepts/configuration/#sharepoint-framework","text":"You can optionally supply only the SPFx context to the sp configure method. import { sp } from \"@pnp/sp\"; // in SPFx only sp.setup(this.context);","title":"SharePoint Framework"},{"location":"v2/concepts/configuration/#graph-configuration","text":"The graph configuration works exactly the same as the sp configuration but is defined by the IGraphConfiguration interface which extends ILibraryConfiguration. All of the graph values are contained in a top level property named \"graph\". The following table describes the properties with a code sample following. All values are optional. Name Description Default headers Allows you to apply any headers to all calls made by the sp library none baseUrl Allows you to define a base site url for all requests, takes precedence over all other url logic. Must be absolute. ( Added in 2.0.8 ) none fetchClientFactory Allows you to specify a factory function used to produce IHttpClientImpl instances none There are many examples of using fetchClientFactory available in the authentication section . import { graph } from \"@pnp/graph\"; import { MsalClientSetup } from \"@pnp/msaljsclient\"; // note you can still set the global configuration such as ie11 using the same object as // the interface extends ILibraryConfiguration graph.setup({ ie11: false, graph: { // we set the GCC url baseUrl: \"https://graph.microsoft.us\", fetchClientFactory: MsalClientSetup({ auth: { authority: \"https://login.microsoftonline.com/tenant.onmicrosoft.com\", clientId: \"00000000-0000-0000-0000-000000000000\", redirectUri: \"https://tenant.sharepoint.com/sites/dev/SitePages/test.aspx\", }, }, [\"Group.Read.All\"]), headers: { \"Accept\": \"application/json;odata=verbose\", \"X-Something\": \"header-value\", }, }, spfxContext: this.context, });","title":"Graph Configuration"},{"location":"v2/concepts/configuration/#sharepoint-framework_1","text":"You can optionally supply only the SPFx context to the graph configure method. We will attempt to set the baseUrl property from the context - but if that is failing in your environment and you need to call a special cloud (i.e. graph.microsoft.us) please set the baseUrl property. import { graph } from \"@pnp/graph\"; // in SPFx only graph.setup(this.context);","title":"SharePoint Framework"},{"location":"v2/concepts/configuration/#configure-everything-at-once","text":"In some cases you might want to configure everything in one go. Because the configuration is stored in a single location you can use the common library's setup method and adjust the typings to ensure you are using the correct property names while only having to setup things with a single call. In versions before 2.0.8 ISPConfigurationPart, IGraphConfigurationPart, and ILibraryConfiguration incorrectly were missing the \"I\" prefix. That was fixed in 2.0.8 - but note if you are using an older version of the library you'll need to use the old names. Everything else in the below example works as expected. import { ISPConfigurationPart } from \"@pnp/sp\"; import { IGraphConfigurationPart } from \"@pnp/graph\"; import { ILibraryConfiguration, setup } from \"@pnp/core\"; // you could also include your custom configuration parts export interface AllConfig extends ILibraryConfiguration, ISPConfigurationPart, IGraphConfigurationPart { } // create a single big configuration entry const config: AllConfig = { graph: { baseUrl: \"https://graph.microsoft.us\", }, ie11: false, sp: { baseUrl: \"https://tenant.sharepoint.com/sites/dev\", }, }; setup(config);","title":"Configure Everything At Once"},{"location":"v2/concepts/custom-bundle/","text":"Custom Bundling \u00b6 With the introduction of selective imports it is now possible to create your own bundle to exactly fit your needs. This provides much greater control over how your solutions are deployed and what is included in your bundles. Scenarios could include: Deploying a company-wide PnPjs custom bundle shared by all your components so it only needs to be downloaded once. Creating SPFx libraries either for one project or a single webpart. Create a single library containing the PnPjs code you need bundled along with your custom extensions . Create a custom bundle \u00b6 Webpack \u00b6 You can see/clone a sample project of this example here . Rollup \u00b6 You can see/clone a sample project of this example here .","title":"Custom Bundling"},{"location":"v2/concepts/custom-bundle/#custom-bundling","text":"With the introduction of selective imports it is now possible to create your own bundle to exactly fit your needs. This provides much greater control over how your solutions are deployed and what is included in your bundles. Scenarios could include: Deploying a company-wide PnPjs custom bundle shared by all your components so it only needs to be downloaded once. Creating SPFx libraries either for one project or a single webpart. Create a single library containing the PnPjs code you need bundled along with your custom extensions .","title":"Custom Bundling"},{"location":"v2/concepts/custom-bundle/#create-a-custom-bundle","text":"","title":"Create a custom bundle"},{"location":"v2/concepts/custom-bundle/#webpack","text":"You can see/clone a sample project of this example here .","title":"Webpack"},{"location":"v2/concepts/custom-bundle/#rollup","text":"You can see/clone a sample project of this example here .","title":"Rollup"},{"location":"v2/concepts/error-handling/","text":"Error Handling \u00b6 This article describes the most common types of errors generated by the library. It provides context on the error object, and ways to handle the errors. As always you should tailor your error handling to what your application needs. These are ideas that can be applied to many different patterns. For 429, 503, and 504 errors we include retry logic within the library The HttpRequestError \u00b6 All errors resulting from executed web requests will be returned as an HttpRequestError object which extends the base Error . In addition to the standard Error properties it has some other properties to help you figure out what went wrong. We used a custom error to attempt to normalize what can be a wide assortment of http related errors, while also seeking to provide as much information to library consumers as possible. Property Name Description name Standard Error.name property. Always 'Error' message Normalized string containing the status, status text, and the full response text stack The callstack producing the error isHttpRequestError Always true, allows you to reliably determine if you have an HttpRequestError instance response Unread copy of the Response object resulting in the thrown error status The Response.status value (such as 404) statusText The Response.statusText value (such as 'Not Found') Basic Handling \u00b6 For all operations involving a web request you should account for the possibility they might fail. That failure might be transient or permanent - you won't know until they happen \ud83d\ude09. The most basic type of error handling involves a simple try-catch. import { sp } from \"@pnp/sp/presets/all\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { console.error(e); } This will produce output like: Error making HttpClient request in queryable [404] Not Found ::> {\"odata.error\":{\"code\":\"-1, System.ArgumentException\",\"message\":{\"lang\":\"en-US\",\"value\":\"List 'no' does not exist at site with URL 'https://tenant.sharepoint.com/sites/dev'.\"}}} Data: {\"response\":{\"size\":0,\"timeout\":0},\"status\":404,\"statusText\":\"Not Found\",\"isHttpRequestError\":true} This is very descriptive and provides full details as to what happened, but you might want to handle things a little more cleanly. Reading the Response \u00b6 In some cases the response body will have additional details such as a localized error messages which can be nicer to display rather than our normalized string. You can read the response directly and process it however you desire: import { sp } from \"@pnp/sp/presets/all\"; import { HttpRequestError } from \"@pnp/queryable\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { // are we dealing with an HttpRequestError? if (e?.isHttpRequestError) { // we can read the json from the response const json = await (e).response.json(); // if we have a value property we can show it console.log(typeof json[\"odata.error\"] === \"object\" ? json[\"odata.error\"].message.value : e.message); // add of course you have access to the other properties and can make choices on how to act if ((e).status === 404) { console.error((e).statusText); // maybe create the resource, or redirect, or fallback to a secondary data source // just ideas, handle any of the status codes uniquely as needed } } else { // not an HttpRequestError so we just log message console.log(e.message); } } Logging errors \u00b6 Using the PnPjs Logging Framework you can directly pass the error object and the normalized message will be logged. These techniques can be applied to any logging framework. import { Logger } from \"@pnp/logging\"; import { sp } from \"@pnp/sp/presets/all\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { Logger.error(e); } You may want to read the response and customize the message as described above: import { Logger } from \"@pnp/logging\"; import { sp } from \"@pnp/sp/presets/all\"; import { HttpRequestError } from \"@pnp/queryable\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { if (e?.isHttpRequestError) { // we can read the json from the response const data = await (e).response.json(); // parse this however you want const message = typeof data[\"odata.error\"] === \"object\" ? data[\"odata.error\"].message.value : e.message; // we use the status to determine a custom logging level const level: LogLevel = (e).status === 404 ? LogLevel.Warning : LogLevel.Info; // create a custom log entry Logger.log({ data, level, message, }); } else { // not an HttpRequestError so we just log message Logger.error(e); } } Putting it All Together \u00b6 After reviewing the above section you might have thought it seems like a lot of work to include all that logic for every error. One approach is to establish a single function you use application wide to process errors. This allows all the error handling logic to be easily updated and consistent across the application. errorhandler.ts \u00b6 import { Logger } from \"@pnp/logging\"; import { HttpRequestError } from \"@pnp/queryable\"; import { hOP } from \"@pnp/core\"; export async function handleError(e: Error | HttpRequestError): Promise { if (hOP(e, \"isHttpRequestError\")) { // we can read the json from the response const data = await (e).response.json(); // parse this however you want const message = typeof data[\"odata.error\"] === \"object\" ? data[\"odata.error\"].message.value : e.message; // we use the status to determine a custom logging level const level: LogLevel = (e).status === 404 ? LogLevel.Warning : LogLevel.Info; // create a custom log entry Logger.log({ data, level, message, }); } else { // not an HttpRequestError so we just log message Logger.error(e); } } web-request.ts \u00b6 import { sp } from \"@pnp/sp/presets/all\"; import { handleError } from \"./errorhandler\"; try { const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { await handleError(e); } web-request2.ts \u00b6 import { sp } from \"@pnp/sp/presets/all\"; import { handleError } from \"./errorhandler\"; try { const w = await sp.web.lists(); } catch (e) { await handleError(e); }","title":"Error Handling"},{"location":"v2/concepts/error-handling/#error-handling","text":"This article describes the most common types of errors generated by the library. It provides context on the error object, and ways to handle the errors. As always you should tailor your error handling to what your application needs. These are ideas that can be applied to many different patterns. For 429, 503, and 504 errors we include retry logic within the library","title":"Error Handling"},{"location":"v2/concepts/error-handling/#the-httprequesterror","text":"All errors resulting from executed web requests will be returned as an HttpRequestError object which extends the base Error . In addition to the standard Error properties it has some other properties to help you figure out what went wrong. We used a custom error to attempt to normalize what can be a wide assortment of http related errors, while also seeking to provide as much information to library consumers as possible. Property Name Description name Standard Error.name property. Always 'Error' message Normalized string containing the status, status text, and the full response text stack The callstack producing the error isHttpRequestError Always true, allows you to reliably determine if you have an HttpRequestError instance response Unread copy of the Response object resulting in the thrown error status The Response.status value (such as 404) statusText The Response.statusText value (such as 'Not Found')","title":"The HttpRequestError"},{"location":"v2/concepts/error-handling/#basic-handling","text":"For all operations involving a web request you should account for the possibility they might fail. That failure might be transient or permanent - you won't know until they happen \ud83d\ude09. The most basic type of error handling involves a simple try-catch. import { sp } from \"@pnp/sp/presets/all\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { console.error(e); } This will produce output like: Error making HttpClient request in queryable [404] Not Found ::> {\"odata.error\":{\"code\":\"-1, System.ArgumentException\",\"message\":{\"lang\":\"en-US\",\"value\":\"List 'no' does not exist at site with URL 'https://tenant.sharepoint.com/sites/dev'.\"}}} Data: {\"response\":{\"size\":0,\"timeout\":0},\"status\":404,\"statusText\":\"Not Found\",\"isHttpRequestError\":true} This is very descriptive and provides full details as to what happened, but you might want to handle things a little more cleanly.","title":"Basic Handling"},{"location":"v2/concepts/error-handling/#reading-the-response","text":"In some cases the response body will have additional details such as a localized error messages which can be nicer to display rather than our normalized string. You can read the response directly and process it however you desire: import { sp } from \"@pnp/sp/presets/all\"; import { HttpRequestError } from \"@pnp/queryable\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { // are we dealing with an HttpRequestError? if (e?.isHttpRequestError) { // we can read the json from the response const json = await (e).response.json(); // if we have a value property we can show it console.log(typeof json[\"odata.error\"] === \"object\" ? json[\"odata.error\"].message.value : e.message); // add of course you have access to the other properties and can make choices on how to act if ((e).status === 404) { console.error((e).statusText); // maybe create the resource, or redirect, or fallback to a secondary data source // just ideas, handle any of the status codes uniquely as needed } } else { // not an HttpRequestError so we just log message console.log(e.message); } }","title":"Reading the Response"},{"location":"v2/concepts/error-handling/#logging-errors","text":"Using the PnPjs Logging Framework you can directly pass the error object and the normalized message will be logged. These techniques can be applied to any logging framework. import { Logger } from \"@pnp/logging\"; import { sp } from \"@pnp/sp/presets/all\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { Logger.error(e); } You may want to read the response and customize the message as described above: import { Logger } from \"@pnp/logging\"; import { sp } from \"@pnp/sp/presets/all\"; import { HttpRequestError } from \"@pnp/queryable\"; try { // get a list that doesn't exist const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { if (e?.isHttpRequestError) { // we can read the json from the response const data = await (e).response.json(); // parse this however you want const message = typeof data[\"odata.error\"] === \"object\" ? data[\"odata.error\"].message.value : e.message; // we use the status to determine a custom logging level const level: LogLevel = (e).status === 404 ? LogLevel.Warning : LogLevel.Info; // create a custom log entry Logger.log({ data, level, message, }); } else { // not an HttpRequestError so we just log message Logger.error(e); } }","title":"Logging errors"},{"location":"v2/concepts/error-handling/#putting-it-all-together","text":"After reviewing the above section you might have thought it seems like a lot of work to include all that logic for every error. One approach is to establish a single function you use application wide to process errors. This allows all the error handling logic to be easily updated and consistent across the application.","title":"Putting it All Together"},{"location":"v2/concepts/error-handling/#errorhandlerts","text":"import { Logger } from \"@pnp/logging\"; import { HttpRequestError } from \"@pnp/queryable\"; import { hOP } from \"@pnp/core\"; export async function handleError(e: Error | HttpRequestError): Promise { if (hOP(e, \"isHttpRequestError\")) { // we can read the json from the response const data = await (e).response.json(); // parse this however you want const message = typeof data[\"odata.error\"] === \"object\" ? data[\"odata.error\"].message.value : e.message; // we use the status to determine a custom logging level const level: LogLevel = (e).status === 404 ? LogLevel.Warning : LogLevel.Info; // create a custom log entry Logger.log({ data, level, message, }); } else { // not an HttpRequestError so we just log message Logger.error(e); } }","title":"errorhandler.ts"},{"location":"v2/concepts/error-handling/#web-requestts","text":"import { sp } from \"@pnp/sp/presets/all\"; import { handleError } from \"./errorhandler\"; try { const w = await sp.web.lists.getByTitle(\"no\")(); } catch (e) { await handleError(e); }","title":"web-request.ts"},{"location":"v2/concepts/error-handling/#web-request2ts","text":"import { sp } from \"@pnp/sp/presets/all\"; import { handleError } from \"./errorhandler\"; try { const w = await sp.web.lists(); } catch (e) { await handleError(e); }","title":"web-request2.ts"},{"location":"v2/concepts/ie11-mode/","text":"IE11 Mode \u00b6 Starting with v2 we have made the decision to no longer support IE11. Because we know this affects folks we have introduced IE11 compatibility mode. Configuring the library will allow it to work within IE11, however at a possibly reduced level of functionality depending on your use case. Please see the list below of known limitations. Limitations \u00b6 When required to use IE11 mode there is certain functionality which may not work correctly or at all. Unavailable: Extension Methods Unavailable: OData Debugging Configure IE11 Mode \u00b6 To enable IE11 Mode set the ie11 flag to true in the setup object. Optionally, supply the context object when working in SharePoint Framework . import { sp } from \"@pnp/sp\"; sp.setup({ // set ie 11 mode ie11: true, // only needed when working within SharePoint Framework spfxContext: this.context }); If you are supporting IE 11, please see the article on required polyfills . A note on ie11 mode and support \u00b6 Because IE11 is no longer a primary supported browser our policy moving forward will be doing our best not to break anything in ie11 mode, but not all features will work and new features may never come to ie11 mode. Also, if you find an ie11 bug we expect you to work with us on helping to fix it. If you aren't willing to invest some time to support an old browser it seems we shouldn't either.","title":"IE11 Mode"},{"location":"v2/concepts/ie11-mode/#ie11-mode","text":"Starting with v2 we have made the decision to no longer support IE11. Because we know this affects folks we have introduced IE11 compatibility mode. Configuring the library will allow it to work within IE11, however at a possibly reduced level of functionality depending on your use case. Please see the list below of known limitations.","title":"IE11 Mode"},{"location":"v2/concepts/ie11-mode/#limitations","text":"When required to use IE11 mode there is certain functionality which may not work correctly or at all. Unavailable: Extension Methods Unavailable: OData Debugging","title":"Limitations"},{"location":"v2/concepts/ie11-mode/#configure-ie11-mode","text":"To enable IE11 Mode set the ie11 flag to true in the setup object. Optionally, supply the context object when working in SharePoint Framework . import { sp } from \"@pnp/sp\"; sp.setup({ // set ie 11 mode ie11: true, // only needed when working within SharePoint Framework spfxContext: this.context }); If you are supporting IE 11, please see the article on required polyfills .","title":"Configure IE11 Mode"},{"location":"v2/concepts/ie11-mode/#a-note-on-ie11-mode-and-support","text":"Because IE11 is no longer a primary supported browser our policy moving forward will be doing our best not to break anything in ie11 mode, but not all features will work and new features may never come to ie11 mode. Also, if you find an ie11 bug we expect you to work with us on helping to fix it. If you aren't willing to invest some time to support an old browser it seems we shouldn't either.","title":"A note on ie11 mode and support"},{"location":"v2/concepts/invokable/","text":"Invokables \u00b6 For people who have been using the library since the early days you are familiar with the need to use the () method to invoke a method chain: // an example of get const lists = await sp.web.lists(); Starting with v2 this is no longer required, you can invoke the object directly to execute the default action for that class - typically a get. const lists = await sp.web.lists(); This has two main benefits for people using the library: you can write less code, and we now have a way to model default actions for objects that might do something other than a get. The way we designed the library prior to v2 hid the post, put, delete operations as protected methods attached to the Queryable classes. Without diving into why we did this, having a rethink seemed appropriate for v2. Based on that, the entire queryable chain is now invokable as well for any of the operations. Other Operations (post, put, delete) \u00b6 import { sp, spPost } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // do a post to a web - just an example doesn't do anything fancy spPost(sp.web); Things get a little more interesting in that you can now do posts (or any of the operations) to any of the urls defined by a fluent chain. Meaning you can easily implement methods that are not yet part of the library. For this example I have made up a method called \"MagicFieldCreationMethod\" that doesn't exist. Imagine it was just added to the SharePoint API and we do not yet have support for it. You can now write code like so: import { sp, spPost, SharePointQueryable } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/fields/web\"; // call our made up example method spPost(SharePointQueryable(sp.web.fields, \"MagicFieldCreationMethod\"), { body: JSON.stringify({ // ... this would be the post body }), });","title":"Invokables"},{"location":"v2/concepts/invokable/#invokables","text":"For people who have been using the library since the early days you are familiar with the need to use the () method to invoke a method chain: // an example of get const lists = await sp.web.lists(); Starting with v2 this is no longer required, you can invoke the object directly to execute the default action for that class - typically a get. const lists = await sp.web.lists(); This has two main benefits for people using the library: you can write less code, and we now have a way to model default actions for objects that might do something other than a get. The way we designed the library prior to v2 hid the post, put, delete operations as protected methods attached to the Queryable classes. Without diving into why we did this, having a rethink seemed appropriate for v2. Based on that, the entire queryable chain is now invokable as well for any of the operations.","title":"Invokables"},{"location":"v2/concepts/invokable/#other-operations-post-put-delete","text":"import { sp, spPost } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // do a post to a web - just an example doesn't do anything fancy spPost(sp.web); Things get a little more interesting in that you can now do posts (or any of the operations) to any of the urls defined by a fluent chain. Meaning you can easily implement methods that are not yet part of the library. For this example I have made up a method called \"MagicFieldCreationMethod\" that doesn't exist. Imagine it was just added to the SharePoint API and we do not yet have support for it. You can now write code like so: import { sp, spPost, SharePointQueryable } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/fields/web\"; // call our made up example method spPost(SharePointQueryable(sp.web.fields, \"MagicFieldCreationMethod\"), { body: JSON.stringify({ // ... this would be the post body }), });","title":"Other Operations (post, put, delete)"},{"location":"v2/concepts/polyfill/","text":"Polyfills \u00b6 These libraries may make use of some features not found in older browsers. This primarily affects Internet Explorer 11, which requires that we provide this missing functionality. If you are supporting IE11 enable IE11 mode . IE 11 Polyfill package \u00b6 We created a package you try and help provide this missing functionality. This package is independent of the other @pnp/* packages and does not need to be updated monthly unless we introduce additional polyfills and publish a new version. This package is only needed if you are required to support IE 11. Install \u00b6 npm install @pnp/polyfill-ie11 --save Use \u00b6 import \"@pnp/polyfill-ie11\"; import { sp } from \"@pnp/sp/presets/all\"; sp.web.lists.getByTitle(\"BigList\").items.filter(`ID gt 6000`)().then(r => { this.domElement.innerHTML += r.map(l => `${l.Title}
      `); }); Selective Use \u00b6 Starting with version 2.0.2 you can selectively include the polyfills from the package. Depending on your needs it may make sense in your application to use the underlying libraries directly. We have added an expanded statement on our polyfills . // individually include polyfills as needed to match your requirements import \"@pnp/polyfill-ie11/dist/fetch\"; import \"@pnp/polyfill-ie11/dist/fill\"; import \"@pnp/polyfill-ie11/dist/from\"; import \"@pnp/polyfill-ie11/dist/iterator\"; import \"@pnp/polyfill-ie11/dist/map\"; import \"@pnp/polyfill-ie11/dist/promise\"; import \"@pnp/polyfill-ie11/dist/reflect\"; import \"@pnp/polyfill-ie11/dist/symbol\"; // works in IE11 and other browsers sp.web.lists.getByTitle(\"BigList\").items.filter(`ID gt 6000`)().then(r => { this.domElement.innerHTML += r.map(l => `${l.Title}
      `); }); SearchQueryBuilder \u00b6 Because the latest version of SearchQueryBuilder uses Proxy internally you can fall back on the older version as shown below. import \"@pnp/polyfill-ie11\"; import { SearchQueryBuilder } from \"@pnp/polyfill-ie11/dist/searchquerybuilder\"; import { sp, ISearchQueryBuilder } from \"@pnp/sp/presets/all\"; // works in IE11 and other browsers const builder: ISearchQueryBuilder = SearchQueryBuilder().text(\"test\"); sp.search(builder).then(r => { this.domElement.innerHTML = JSON.stringify(r); }); General Statement on Polyfills \u00b6 Internet Explorer 11 (IE11) has been an enterprise standard browser for many years. Given the complexity in changing technical platforms in many organizations, it is no surprise standardization on this out-of-date browser continues. Unfortunately, for those organizations, the Internet has moved on and many - if not all - SaaS platforms are embracing modern standards and no longer supporting the legacy IE11 browser. Even Microsoft states in their official documentation that Microsoft 365 is best experienced with a modern browser. They have even gone so far to build the latest version of Microsoft Edge based on Chromium (Edge Chromium), with an \"Internet Explorer mode\" allowing organizations to load legacy sites which require IE automatically. PnPjs is now \"modern\" as well, and by that we mean we have moved to using capabilities of current browsers and JavaScript which are not present in IE11. We understand as a developer your ability to require an organization to switch browsers is unrealistic. We want to do everything we can to support you, but it is up to you to ensure your application is properly supported in IE11. There are many polyfills available, depending on the platform you're running on, the frameworks you are using, and the libraries you consume. Although the majority of PnPjs users build for SharePoint Online, a significant number build for earlier versions of the platform as well as for their own node-based solutions or websites. Unfortunately, there is no way our polyfill library can support all these scenarios. What we intended with the @pnp/polyfill-ie11 package was to provide a comprehensive group of all the polyfills that would be needed based on the complete PnPjs library. We are finding when we aggregate our polyfills with the polyfills provided in the SharePoint page and from other sources, things don't always work well. We cannot solve this for your specific situations except by providing you transparency into the polyfills which we know are necessary for our packages. You may need to adjust what polyfills your application uses based on the other libraries you are using. To that end, we want to provide the list of polyfills we recommend here - along with the associated packages \u2013 with the goal of helping you to work out what combination of polyfills might work with your code. Also, if you haven't reviewed it yet, please check out the information on IE11 Mode for how to configure IE11 mode in the sp.setup as well as what limitations doing so will have on your usage of PnPjs. imports import \"core-js/stable/array/from\"; import \"core-js/stable/array/fill\"; import \"core-js/stable/array/iterator\"; import \"core-js/stable/promise\"; import \"core-js/stable/reflect\"; import \"es6-map/implement\"; import \"core-js/stable/symbol\"; import \"whatwg-fetch\"; The following NPM packages are what we use to do the above indicated imports |package| |---| | core-js | | es6-map | | whatwg-fetch |","title":"Polyfills"},{"location":"v2/concepts/polyfill/#polyfills","text":"These libraries may make use of some features not found in older browsers. This primarily affects Internet Explorer 11, which requires that we provide this missing functionality. If you are supporting IE11 enable IE11 mode .","title":"Polyfills"},{"location":"v2/concepts/polyfill/#ie-11-polyfill-package","text":"We created a package you try and help provide this missing functionality. This package is independent of the other @pnp/* packages and does not need to be updated monthly unless we introduce additional polyfills and publish a new version. This package is only needed if you are required to support IE 11.","title":"IE 11 Polyfill package"},{"location":"v2/concepts/polyfill/#install","text":"npm install @pnp/polyfill-ie11 --save","title":"Install"},{"location":"v2/concepts/polyfill/#use","text":"import \"@pnp/polyfill-ie11\"; import { sp } from \"@pnp/sp/presets/all\"; sp.web.lists.getByTitle(\"BigList\").items.filter(`ID gt 6000`)().then(r => { this.domElement.innerHTML += r.map(l => `${l.Title}
      `); });","title":"Use"},{"location":"v2/concepts/polyfill/#selective-use","text":"Starting with version 2.0.2 you can selectively include the polyfills from the package. Depending on your needs it may make sense in your application to use the underlying libraries directly. We have added an expanded statement on our polyfills . // individually include polyfills as needed to match your requirements import \"@pnp/polyfill-ie11/dist/fetch\"; import \"@pnp/polyfill-ie11/dist/fill\"; import \"@pnp/polyfill-ie11/dist/from\"; import \"@pnp/polyfill-ie11/dist/iterator\"; import \"@pnp/polyfill-ie11/dist/map\"; import \"@pnp/polyfill-ie11/dist/promise\"; import \"@pnp/polyfill-ie11/dist/reflect\"; import \"@pnp/polyfill-ie11/dist/symbol\"; // works in IE11 and other browsers sp.web.lists.getByTitle(\"BigList\").items.filter(`ID gt 6000`)().then(r => { this.domElement.innerHTML += r.map(l => `${l.Title}
      `); });","title":"Selective Use"},{"location":"v2/concepts/polyfill/#searchquerybuilder","text":"Because the latest version of SearchQueryBuilder uses Proxy internally you can fall back on the older version as shown below. import \"@pnp/polyfill-ie11\"; import { SearchQueryBuilder } from \"@pnp/polyfill-ie11/dist/searchquerybuilder\"; import { sp, ISearchQueryBuilder } from \"@pnp/sp/presets/all\"; // works in IE11 and other browsers const builder: ISearchQueryBuilder = SearchQueryBuilder().text(\"test\"); sp.search(builder).then(r => { this.domElement.innerHTML = JSON.stringify(r); });","title":"SearchQueryBuilder"},{"location":"v2/concepts/polyfill/#general-statement-on-polyfills","text":"Internet Explorer 11 (IE11) has been an enterprise standard browser for many years. Given the complexity in changing technical platforms in many organizations, it is no surprise standardization on this out-of-date browser continues. Unfortunately, for those organizations, the Internet has moved on and many - if not all - SaaS platforms are embracing modern standards and no longer supporting the legacy IE11 browser. Even Microsoft states in their official documentation that Microsoft 365 is best experienced with a modern browser. They have even gone so far to build the latest version of Microsoft Edge based on Chromium (Edge Chromium), with an \"Internet Explorer mode\" allowing organizations to load legacy sites which require IE automatically. PnPjs is now \"modern\" as well, and by that we mean we have moved to using capabilities of current browsers and JavaScript which are not present in IE11. We understand as a developer your ability to require an organization to switch browsers is unrealistic. We want to do everything we can to support you, but it is up to you to ensure your application is properly supported in IE11. There are many polyfills available, depending on the platform you're running on, the frameworks you are using, and the libraries you consume. Although the majority of PnPjs users build for SharePoint Online, a significant number build for earlier versions of the platform as well as for their own node-based solutions or websites. Unfortunately, there is no way our polyfill library can support all these scenarios. What we intended with the @pnp/polyfill-ie11 package was to provide a comprehensive group of all the polyfills that would be needed based on the complete PnPjs library. We are finding when we aggregate our polyfills with the polyfills provided in the SharePoint page and from other sources, things don't always work well. We cannot solve this for your specific situations except by providing you transparency into the polyfills which we know are necessary for our packages. You may need to adjust what polyfills your application uses based on the other libraries you are using. To that end, we want to provide the list of polyfills we recommend here - along with the associated packages \u2013 with the goal of helping you to work out what combination of polyfills might work with your code. Also, if you haven't reviewed it yet, please check out the information on IE11 Mode for how to configure IE11 mode in the sp.setup as well as what limitations doing so will have on your usage of PnPjs. imports import \"core-js/stable/array/from\"; import \"core-js/stable/array/fill\"; import \"core-js/stable/array/iterator\"; import \"core-js/stable/promise\"; import \"core-js/stable/reflect\"; import \"es6-map/implement\"; import \"core-js/stable/symbol\"; import \"whatwg-fetch\"; The following NPM packages are what we use to do the above indicated imports |package| |---| | core-js | | es6-map | | whatwg-fetch |","title":"General Statement on Polyfills"},{"location":"v2/concepts/selective-imports/","text":"Selective Imports \u00b6 As the libraries have grown to support more of the SharePoint and Graph API they have also grown in size. On one hand this is good as more functionality becomes available but you had to include lots of code you didn't use if you were only doing simple operations. To solve this we introduced selective imports in v2. This allows you to only import the parts of the sp or graph library you need, allowing you to greatly reduce your overall solution bundle size - and enables treeshaking . This concept works well with custom bundling to create a shared package tailored exactly to your needs. If you would prefer to not worry about selective imports please see the section on presets . Old way \u00b6 // the sp var came with all library functionality already attached // meaning treeshaking couldn't reduce the size import { sp } from \"@pnp/sp\"; const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)(); New Way \u00b6 // the sp var now has almost nothing attached at import time and relies on import { sp } from \"@pnp/sp\"; // we need to import each of the pieces we need to \"attach\" them for chaining // here we are importing the specific sub modules we need and attaching the functionality for lists to web and items to list import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items/list\"; const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)(); Above we are being very specific in what we are importing, but you can also import entire sub-modules and be slightly less specific // the sp var now has almost nothing attached at import time and relies on import { sp } from \"@pnp/sp\"; // we need to import each of the pieces we need to \"attach\" them for chaining // here we are importing the specific sub modules we need and attaching the functionality for lists to web and items to list import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)(); The above two examples both work just fine but you may end up with slightly smaller bundle sizes using the first. Consider this example: // this import statement will attach content-type functionality to list, web, and item import \"@pnp/sp/content-types\"; // this import statement will only attach content-type functionality to web import \"@pnp/sp/content-types/web\"; If you only need to access content types on the web object you can reduce size by only importing that piece. // this will fail import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IList } from \"@pnp/sp/lists\"; // do this instead import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { IList } from \"@pnp/sp/lists\"; const lists = await sp.web.lists(); Presets \u00b6 Sometimes you don't care as much about bundle size - testing or node development for example. In these cases we have provided what we are calling presets to allow you to skip importing each module individually. SP \u00b6 For the sp library there are two presets \"all\" and \"core\". The all preset mimics the behavior in v1 and includes everything in the library already attached to the sp var. import { sp } from \"@pnp/sp/presets/all\"; // sp.* exists as it did in v1, tree shaking will not work const lists = await sp.web.lists(); The \"core\" preset includes sites, webs, lists, and items. import { sp } from \"@pnp/sp/presets/core\"; // sp.* exists as it did in v1, tree shaking will not work const lists = await sp.web.lists(); Graph \u00b6 The graph library contains a single preset, \"all\" mimicking the v1 structure. import { graph } from \"@pnp/graph/presets/all\"; // graph.* exists as it did in v1, tree shaking will not work While we may look to add additional presets in the future you are encouraged to look at making your own custom bundles as a preferred solution.","title":"Selective Imports"},{"location":"v2/concepts/selective-imports/#selective-imports","text":"As the libraries have grown to support more of the SharePoint and Graph API they have also grown in size. On one hand this is good as more functionality becomes available but you had to include lots of code you didn't use if you were only doing simple operations. To solve this we introduced selective imports in v2. This allows you to only import the parts of the sp or graph library you need, allowing you to greatly reduce your overall solution bundle size - and enables treeshaking . This concept works well with custom bundling to create a shared package tailored exactly to your needs. If you would prefer to not worry about selective imports please see the section on presets .","title":"Selective Imports"},{"location":"v2/concepts/selective-imports/#old-way","text":"// the sp var came with all library functionality already attached // meaning treeshaking couldn't reduce the size import { sp } from \"@pnp/sp\"; const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)();","title":"Old way"},{"location":"v2/concepts/selective-imports/#new-way","text":"// the sp var now has almost nothing attached at import time and relies on import { sp } from \"@pnp/sp\"; // we need to import each of the pieces we need to \"attach\" them for chaining // here we are importing the specific sub modules we need and attaching the functionality for lists to web and items to list import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items/list\"; const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)(); Above we are being very specific in what we are importing, but you can also import entire sub-modules and be slightly less specific // the sp var now has almost nothing attached at import time and relies on import { sp } from \"@pnp/sp\"; // we need to import each of the pieces we need to \"attach\" them for chaining // here we are importing the specific sub modules we need and attaching the functionality for lists to web and items to list import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; const itemData = await sp.web.lists.getById('00000000-0000-0000-0000-000000000000').items.getById(1)(); The above two examples both work just fine but you may end up with slightly smaller bundle sizes using the first. Consider this example: // this import statement will attach content-type functionality to list, web, and item import \"@pnp/sp/content-types\"; // this import statement will only attach content-type functionality to web import \"@pnp/sp/content-types/web\"; If you only need to access content types on the web object you can reduce size by only importing that piece. // this will fail import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IList } from \"@pnp/sp/lists\"; // do this instead import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { IList } from \"@pnp/sp/lists\"; const lists = await sp.web.lists();","title":"New Way"},{"location":"v2/concepts/selective-imports/#presets","text":"Sometimes you don't care as much about bundle size - testing or node development for example. In these cases we have provided what we are calling presets to allow you to skip importing each module individually.","title":"Presets"},{"location":"v2/concepts/selective-imports/#sp","text":"For the sp library there are two presets \"all\" and \"core\". The all preset mimics the behavior in v1 and includes everything in the library already attached to the sp var. import { sp } from \"@pnp/sp/presets/all\"; // sp.* exists as it did in v1, tree shaking will not work const lists = await sp.web.lists(); The \"core\" preset includes sites, webs, lists, and items. import { sp } from \"@pnp/sp/presets/core\"; // sp.* exists as it did in v1, tree shaking will not work const lists = await sp.web.lists();","title":"SP"},{"location":"v2/concepts/selective-imports/#graph","text":"The graph library contains a single preset, \"all\" mimicking the v1 structure. import { graph } from \"@pnp/graph/presets/all\"; // graph.* exists as it did in v1, tree shaking will not work While we may look to add additional presets in the future you are encouraged to look at making your own custom bundles as a preferred solution.","title":"Graph"},{"location":"v2/concepts/settings/","text":"Project Settings \u00b6 This article discusses creating a project settings file for use in local development and debugging of the libraries. The settings file contains authentication and other settings to enable you to run and debug the project locally. The settings file is a JavaScript file that exports a single object representing the settings of your project. You can view the example settings file in the project root . Settings File Format (>= 2.0.13) \u00b6 Starting with version 2.0.13 we have added support within the settings file for MSAL authentication for both SharePoint and Graph. You are NOT required to update your existing settings file unless you want to use MSAL authentication with a Graph application. The existing id/secret settings continue to work however we recommend updating when you have an opportunity. For more information coinfiguring MSAL please review the section in the authentication section for node . MSAL configuration has two parts, these are the initialization which is passed directly to the MsalFetchClient (and on to the underlying msal-node instance) and the scopes. The scopes are always \"https://{tenant}.sharepoint.com/.default\" or \"https://graph.microsoft.com/.default\" depending on what you are calling. If you are calling Microsoft Graph sovereign or gov clouds the scope may need to be updated. const privateKey = `-----BEGIN RSA PRIVATE KEY----- your private key, read from a file or included here -----END RSA PRIVATE KEY----- `; var msalInit = { auth: { authority: \"https://login.microsoftonline.com/{tenant id}\", clientCertificate: { thumbprint: \"{certificate thumbnail}\", privateKey: privateKey, }, clientId: \"{AAD App registration id}\", } } var settings = { testing: { enableWebTests: true, testUser: \"i:0#.f|membership|user@consto.com\", sp: { url: \"{required for MSAL - absolute url of test site}\", notificationUrl: \"{ optional: notification url }\", msal: { init: msalInit, scopes: [\"https://{tenant}.sharepoint.com/.default\"] }, }, graph: { msal: { init: msalInit, scopes: [\"https://graph.microsoft.com/.default\"] }, }, }, } module.exports = settings; The settings object has a single sub-object testing which contains the configuration used for debugging and testing PnPjs. The parts of this object are described in detail below. enableWebTests Flag to toggle if tests are run against the live services or not. If this is set to false none of the other sections are required. testUser AAD login account to be used when running tests. sp Settings used to configure SharePoint (sp library) debugging and tests graph Settings used to configure Microsoft Graph (graph library) debugging and tests SP values \u00b6 name description url The url of the site to use for all requests. If a site parameter is not specified a child web will be created under the web at this url. See scripts article for more details. notificationUrl Url used when registering test subscriptions msal Information about MSAL authentication setup Graph value \u00b6 The graph values are described in the table below and come from registering an AAD Application . The permissions required by the registered application are dictated by the tests you want to run or resources you wish to test against. name description msal Information about MSAL authentication setup Settings File Format (<= 2.0.12) \u00b6 var settings = { testing: { enableWebTests: true, sp: { id: \"{ client id }\", secret: \"{ client secret }\", url: \"{ site collection url }\", notificationUrl: \"{ optional: notification url }\", }, graph: { tenant: \"{tenant.onmicrosoft.com}\", id: \"{your app id}\", secret: \"{your secret}\" }, } } module.exports = settings; enableWebTests Flag to toggle if tests are run against the live services or not. If this is set to false none of the other sections are required. sp Settings used to configure SharePoint (sp library) debugging and tests graph Settings used to configure Microsoft Graph (graph library) debugging and tests SP values \u00b6 The sp values are described in the table below and come from registering a legacy SharePoint add-in . name description id The client id of the registered application secret The client secret of the registered application url The url of the site to use for all requests. If a site parameter is not specified a child web will be created under the web at this url. See scripts article for more details. notificationUrl Url used when registering test subscriptions Graph values \u00b6 The graph values are described in the table below and come from registering an AAD Application . The permissions required by the registered application are dictated by the tests you want to run or resources you wish to test against. name description tenant Tenant to target for authentication and data (ex: contoso.onmicrosoft.com) id The application id secret The application secret Create Settings.js file \u00b6 Copy the example file and rename it settings.js. Place the file in the root of your project. Update the settings as needed for your environment. If you are only doing SharePoint testing you can leave the graph section off and vice-versa. Also, if you are not testing anything with hooks you can leave off the notificationUrl.","title":"Project Settings"},{"location":"v2/concepts/settings/#project-settings","text":"This article discusses creating a project settings file for use in local development and debugging of the libraries. The settings file contains authentication and other settings to enable you to run and debug the project locally. The settings file is a JavaScript file that exports a single object representing the settings of your project. You can view the example settings file in the project root .","title":"Project Settings"},{"location":"v2/concepts/settings/#settings-file-format-2013","text":"Starting with version 2.0.13 we have added support within the settings file for MSAL authentication for both SharePoint and Graph. You are NOT required to update your existing settings file unless you want to use MSAL authentication with a Graph application. The existing id/secret settings continue to work however we recommend updating when you have an opportunity. For more information coinfiguring MSAL please review the section in the authentication section for node . MSAL configuration has two parts, these are the initialization which is passed directly to the MsalFetchClient (and on to the underlying msal-node instance) and the scopes. The scopes are always \"https://{tenant}.sharepoint.com/.default\" or \"https://graph.microsoft.com/.default\" depending on what you are calling. If you are calling Microsoft Graph sovereign or gov clouds the scope may need to be updated. const privateKey = `-----BEGIN RSA PRIVATE KEY----- your private key, read from a file or included here -----END RSA PRIVATE KEY----- `; var msalInit = { auth: { authority: \"https://login.microsoftonline.com/{tenant id}\", clientCertificate: { thumbprint: \"{certificate thumbnail}\", privateKey: privateKey, }, clientId: \"{AAD App registration id}\", } } var settings = { testing: { enableWebTests: true, testUser: \"i:0#.f|membership|user@consto.com\", sp: { url: \"{required for MSAL - absolute url of test site}\", notificationUrl: \"{ optional: notification url }\", msal: { init: msalInit, scopes: [\"https://{tenant}.sharepoint.com/.default\"] }, }, graph: { msal: { init: msalInit, scopes: [\"https://graph.microsoft.com/.default\"] }, }, }, } module.exports = settings; The settings object has a single sub-object testing which contains the configuration used for debugging and testing PnPjs. The parts of this object are described in detail below. enableWebTests Flag to toggle if tests are run against the live services or not. If this is set to false none of the other sections are required. testUser AAD login account to be used when running tests. sp Settings used to configure SharePoint (sp library) debugging and tests graph Settings used to configure Microsoft Graph (graph library) debugging and tests","title":"Settings File Format (>= 2.0.13)"},{"location":"v2/concepts/settings/#sp-values","text":"name description url The url of the site to use for all requests. If a site parameter is not specified a child web will be created under the web at this url. See scripts article for more details. notificationUrl Url used when registering test subscriptions msal Information about MSAL authentication setup","title":"SP values"},{"location":"v2/concepts/settings/#graph-value","text":"The graph values are described in the table below and come from registering an AAD Application . The permissions required by the registered application are dictated by the tests you want to run or resources you wish to test against. name description msal Information about MSAL authentication setup","title":"Graph value"},{"location":"v2/concepts/settings/#settings-file-format-2012","text":"var settings = { testing: { enableWebTests: true, sp: { id: \"{ client id }\", secret: \"{ client secret }\", url: \"{ site collection url }\", notificationUrl: \"{ optional: notification url }\", }, graph: { tenant: \"{tenant.onmicrosoft.com}\", id: \"{your app id}\", secret: \"{your secret}\" }, } } module.exports = settings; enableWebTests Flag to toggle if tests are run against the live services or not. If this is set to false none of the other sections are required. sp Settings used to configure SharePoint (sp library) debugging and tests graph Settings used to configure Microsoft Graph (graph library) debugging and tests","title":"Settings File Format (<= 2.0.12)"},{"location":"v2/concepts/settings/#sp-values_1","text":"The sp values are described in the table below and come from registering a legacy SharePoint add-in . name description id The client id of the registered application secret The client secret of the registered application url The url of the site to use for all requests. If a site parameter is not specified a child web will be created under the web at this url. See scripts article for more details. notificationUrl Url used when registering test subscriptions","title":"SP values"},{"location":"v2/concepts/settings/#graph-values","text":"The graph values are described in the table below and come from registering an AAD Application . The permissions required by the registered application are dictated by the tests you want to run or resources you wish to test against. name description tenant Tenant to target for authentication and data (ex: contoso.onmicrosoft.com) id The application id secret The application secret","title":"Graph values"},{"location":"v2/concepts/settings/#create-settingsjs-file","text":"Copy the example file and rename it settings.js. Place the file in the root of your project. Update the settings as needed for your environment. If you are only doing SharePoint testing you can leave the graph section off and vice-versa. Also, if you are not testing anything with hooks you can leave off the notificationUrl.","title":"Create Settings.js file"},{"location":"v2/config-store/","text":"@pnp/config-store \u00b6 This module provides a way to load application configuration from one or more providers and share it across an application in a consistent way. A provider can be anything - but we have included one to load information from a SharePoint list. This library is most helpful for larger applications where a formal configuration model is needed. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/sp @pnp/config-store --save See the topics below for usage: configuration providers","title":"@pnp/config-store"},{"location":"v2/config-store/#pnpconfig-store","text":"This module provides a way to load application configuration from one or more providers and share it across an application in a consistent way. A provider can be anything - but we have included one to load information from a SharePoint list. This library is most helpful for larger applications where a formal configuration model is needed.","title":"@pnp/config-store"},{"location":"v2/config-store/#getting-started","text":"Install the library and required dependencies npm install @pnp/sp @pnp/config-store --save See the topics below for usage: configuration providers","title":"Getting Started"},{"location":"v2/config-store/configuration/","text":"@pnp/config-store/configuration \u00b6 The main class exported from the config-store package is Settings. This is the class through which you will load and access your settings via providers . import { Web } from \"@pnp/sp/presets/all\"; import { Settings, SPListConfigurationProvider } from \"@pnp/config-store\"; // create an instance of the settings class, could be static and shared across your application // or built as needed. const settings = new Settings(); // you can add/update a single value using add settings.add(\"mykey\", \"myvalue\"); // you can also add/update a JSON value which will be stringified for you as a shorthand settings.addJSON(\"mykey2\", { field: 1, field2: 2, field3: 3, }); // and you can apply a plain object of keys/values that will be written as single values // this results in each enumerable property of the supplied object being added to the settings collection settings.apply({ field: 1, field2: 2, field3: 3, }); // and finally you can load values from a configuration provider const w = Web(\"https://mytenant.sharepoint.com/sites/dev\"); const provider = new SPListConfigurationProvider(w, \"myconfiglistname\"); // this will load values from the supplied list // by default the key will be from the Title field and the value from a column named Value await settings.load(provider); // once we have loaded values we can then read them const value = settings.get(\"mykey\"); // or read JSON that will be parsed for you from the store const value2 = settings.getJSON(\"mykey2\");","title":"@pnp/config-store/configuration"},{"location":"v2/config-store/configuration/#pnpconfig-storeconfiguration","text":"The main class exported from the config-store package is Settings. This is the class through which you will load and access your settings via providers . import { Web } from \"@pnp/sp/presets/all\"; import { Settings, SPListConfigurationProvider } from \"@pnp/config-store\"; // create an instance of the settings class, could be static and shared across your application // or built as needed. const settings = new Settings(); // you can add/update a single value using add settings.add(\"mykey\", \"myvalue\"); // you can also add/update a JSON value which will be stringified for you as a shorthand settings.addJSON(\"mykey2\", { field: 1, field2: 2, field3: 3, }); // and you can apply a plain object of keys/values that will be written as single values // this results in each enumerable property of the supplied object being added to the settings collection settings.apply({ field: 1, field2: 2, field3: 3, }); // and finally you can load values from a configuration provider const w = Web(\"https://mytenant.sharepoint.com/sites/dev\"); const provider = new SPListConfigurationProvider(w, \"myconfiglistname\"); // this will load values from the supplied list // by default the key will be from the Title field and the value from a column named Value await settings.load(provider); // once we have loaded values we can then read them const value = settings.get(\"mykey\"); // or read JSON that will be parsed for you from the store const value2 = settings.getJSON(\"mykey2\");","title":"@pnp/config-store/configuration"},{"location":"v2/config-store/providers/","text":"@pnp/config-store/providers \u00b6 Currently there is a single provider included in the library, but contributions of additional providers are welcome. SPListConfigurationProvider \u00b6 This provider is based on a SharePoint list it reads all of the rows and makes them available as a TypedHash . By default the column names used are Title for key and \"Value\" for value, but you can update these as needed. Additionally, the settings class supports the idea of last value in wins - so you can easily load multiple configurations. This helps to support a common scenario in the enterprise where you might have one main list for global configuration but some settings can be set at the web level. In this case you would first load the global, then the local settings and any local values will take precedence. import { Web } from \"@pnp/sp/presets/all\"; import { Settings, SPListConfigurationProvider } from \"@pnp/config-store\"; // create a new provider instance const w = Web(\"https://mytenant.sharepoint.com/sites/dev\"); const provider = new SPListConfigurationProvider(w, \"myconfiglistname\"); const settings = new Settings(); // load our values from the list await settings.load(provider); CachingConfigurationProvider \u00b6 Because making requests on each page load is very inefficient you can optionally use the caching configuration provider, which wraps a provider and caches the configuration in local or session storage. import { Web } from \"@pnp/sp/presets/all\"; import { Settings, SPListConfigurationProvider } from \"@pnp/config-store\"; // create a new provider instance const w = Web(\"https://mytenant.sharepoint.com/sites/dev\"); const provider = new SPListConfigurationProvider(w, \"myconfiglistname\"); // get an instance of the provider wrapped // you can optionally provide a key that will be used in the cache to the asCaching method const wrappedProvider = provider.asCaching(); // use that wrapped provider to populate the settings await settings.load(wrappedProvider);","title":"@pnp/config-store/providers"},{"location":"v2/config-store/providers/#pnpconfig-storeproviders","text":"Currently there is a single provider included in the library, but contributions of additional providers are welcome.","title":"@pnp/config-store/providers"},{"location":"v2/config-store/providers/#splistconfigurationprovider","text":"This provider is based on a SharePoint list it reads all of the rows and makes them available as a TypedHash . By default the column names used are Title for key and \"Value\" for value, but you can update these as needed. Additionally, the settings class supports the idea of last value in wins - so you can easily load multiple configurations. This helps to support a common scenario in the enterprise where you might have one main list for global configuration but some settings can be set at the web level. In this case you would first load the global, then the local settings and any local values will take precedence. import { Web } from \"@pnp/sp/presets/all\"; import { Settings, SPListConfigurationProvider } from \"@pnp/config-store\"; // create a new provider instance const w = Web(\"https://mytenant.sharepoint.com/sites/dev\"); const provider = new SPListConfigurationProvider(w, \"myconfiglistname\"); const settings = new Settings(); // load our values from the list await settings.load(provider);","title":"SPListConfigurationProvider"},{"location":"v2/config-store/providers/#cachingconfigurationprovider","text":"Because making requests on each page load is very inefficient you can optionally use the caching configuration provider, which wraps a provider and caches the configuration in local or session storage. import { Web } from \"@pnp/sp/presets/all\"; import { Settings, SPListConfigurationProvider } from \"@pnp/config-store\"; // create a new provider instance const w = Web(\"https://mytenant.sharepoint.com/sites/dev\"); const provider = new SPListConfigurationProvider(w, \"myconfiglistname\"); // get an instance of the provider wrapped // you can optionally provide a key that will be used in the cache to the asCaching method const wrappedProvider = provider.asCaching(); // use that wrapped provider to populate the settings await settings.load(wrappedProvider);","title":"CachingConfigurationProvider"},{"location":"v2/contributing/","text":"Contributing to PnPjs \u00b6 Thank you for your interest in contributing to PnPjs. We have updated our contribution section to make things easier to get started, debug the library locally, and learn how to extend the functionality. Section Description Setup Dev Machine Covers setting up your machine to ensure you are ready to debug the solution Local Debug Configuration Discusses the steps required to establish local configuration used for debugging and running tests Debugging Describes how to debug PnPjs locally Extending the library Basic examples on how to extend the library such as adding a method or property Writing Tests How to write and debug tests Update Documentation Describes the steps required to edit and locally view the documentation Submit a Pull Request Outlines guidance for submitting a pull request Need Help? \u00b6 The PnP \"Sharing Is Caring\" initiative teaches the basics around making changes in GitHub, submitting pull requests to the PnP & Microsoft 365 open-source repositories such as PnPjs. Every month, we provide multiple live hands-on sessions that walk attendees through the process of using and contributing to PnP initiatives. To learn more and register for an upcoming session, please visit the Sharing is Caring website.","title":"Contributing to PnPjs"},{"location":"v2/contributing/#contributing-to-pnpjs","text":"Thank you for your interest in contributing to PnPjs. We have updated our contribution section to make things easier to get started, debug the library locally, and learn how to extend the functionality. Section Description Setup Dev Machine Covers setting up your machine to ensure you are ready to debug the solution Local Debug Configuration Discusses the steps required to establish local configuration used for debugging and running tests Debugging Describes how to debug PnPjs locally Extending the library Basic examples on how to extend the library such as adding a method or property Writing Tests How to write and debug tests Update Documentation Describes the steps required to edit and locally view the documentation Submit a Pull Request Outlines guidance for submitting a pull request","title":"Contributing to PnPjs"},{"location":"v2/contributing/#need-help","text":"The PnP \"Sharing Is Caring\" initiative teaches the basics around making changes in GitHub, submitting pull requests to the PnP & Microsoft 365 open-source repositories such as PnPjs. Every month, we provide multiple live hands-on sessions that walk attendees through the process of using and contributing to PnP initiatives. To learn more and register for an upcoming session, please visit the Sharing is Caring website.","title":"Need Help?"},{"location":"v2/contributing/debug-tests/","text":"Writing Tests \u00b6 With version 2 we have made a significant effort to improve out test coverage. To keep that up, all changes submitted will require one or more tests be included. For new functionality at least a basic test that the method executes is required. For bug fixes please include a test that would have caught the bug (i.e. fail before your fix) and passes with your fix in place. How to write Tests \u00b6 We use Mocha and Chai for our testing framework. You can see many examples of writing tests within the ./test folder. Here is a sample with extra comments to help explain what's happening, taken from ./test/sp/items.ts : import { getRandomString } from \"@pnp/core\"; import { testSettings } from \"../main\"; import { expect } from \"chai\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items/list\"; import { IList } from \"@pnp/sp/lists\"; describe(\"Items\", () => { // any tests that make a web request should be withing a block checking if web tests are enabled if (testSettings.enableWebTests) { // a block scoped var we will use across our tests let list: IList = null; // we use the before block to setup // executed before all the tests in this block, see the mocha docs for more details // mocha prefers using function vs arrow functions and this is recommended before(async function () { // execute a request to ensure we have a list const ler = await sp.web.lists.ensure(\"ItemTestList\", \"Used to test item operations\"); list = ler.list; // in this case we want to have some items in the list for testing so we add those // only if the list was just created if (ler.created) { // add a few items to get started const batch = sp.web.createBatch(); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); await batch.execute(); } }); // this test has a label \"get items\" and is run via an async function it(\"get items\", async function () { // make a request for the list's items const items = await list.items(); // report that we expect that result to be an array with more than 0 items expect(items.length).to.be.gt(0); }); // ... remainder of code removed } } General Guidelines for Writing Tests \u00b6 Tests should operate within the site defined in testSettings Tests should be able to run multiple times on the same site, but do not need to cleanup after themselves Each test should be self contained and not depend on other tests, they can depend on work done in before or beforeAll When writing tests you can use \"only\" and \"skip\" from mochajs to focus on only the tests you are writing Be sure to review the various options when running your tests If you are writing a test and the endpoint doesn't support app only permissions, you can skip writing a test - but please note that in the PR description Next Steps \u00b6 Now that you've written tests to cover your changes you'll need to update the docs .","title":"Writing Tests"},{"location":"v2/contributing/debug-tests/#writing-tests","text":"With version 2 we have made a significant effort to improve out test coverage. To keep that up, all changes submitted will require one or more tests be included. For new functionality at least a basic test that the method executes is required. For bug fixes please include a test that would have caught the bug (i.e. fail before your fix) and passes with your fix in place.","title":"Writing Tests"},{"location":"v2/contributing/debug-tests/#how-to-write-tests","text":"We use Mocha and Chai for our testing framework. You can see many examples of writing tests within the ./test folder. Here is a sample with extra comments to help explain what's happening, taken from ./test/sp/items.ts : import { getRandomString } from \"@pnp/core\"; import { testSettings } from \"../main\"; import { expect } from \"chai\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items/list\"; import { IList } from \"@pnp/sp/lists\"; describe(\"Items\", () => { // any tests that make a web request should be withing a block checking if web tests are enabled if (testSettings.enableWebTests) { // a block scoped var we will use across our tests let list: IList = null; // we use the before block to setup // executed before all the tests in this block, see the mocha docs for more details // mocha prefers using function vs arrow functions and this is recommended before(async function () { // execute a request to ensure we have a list const ler = await sp.web.lists.ensure(\"ItemTestList\", \"Used to test item operations\"); list = ler.list; // in this case we want to have some items in the list for testing so we add those // only if the list was just created if (ler.created) { // add a few items to get started const batch = sp.web.createBatch(); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); list.items.inBatch(batch).add({ Title: `Item ${getRandomString(4)}` }); await batch.execute(); } }); // this test has a label \"get items\" and is run via an async function it(\"get items\", async function () { // make a request for the list's items const items = await list.items(); // report that we expect that result to be an array with more than 0 items expect(items.length).to.be.gt(0); }); // ... remainder of code removed } }","title":"How to write Tests"},{"location":"v2/contributing/debug-tests/#general-guidelines-for-writing-tests","text":"Tests should operate within the site defined in testSettings Tests should be able to run multiple times on the same site, but do not need to cleanup after themselves Each test should be self contained and not depend on other tests, they can depend on work done in before or beforeAll When writing tests you can use \"only\" and \"skip\" from mochajs to focus on only the tests you are writing Be sure to review the various options when running your tests If you are writing a test and the endpoint doesn't support app only permissions, you can skip writing a test - but please note that in the PR description","title":"General Guidelines for Writing Tests"},{"location":"v2/contributing/debug-tests/#next-steps","text":"Now that you've written tests to cover your changes you'll need to update the docs .","title":"Next Steps"},{"location":"v2/contributing/debugging/","text":"Debugging \u00b6 Using the steps in this article you will be able to locally debug the library internals as well as new features you are working on. Before proceeding be sure you have reviewed how to setup for local configuration and debugging. Debugging Library Features \u00b6 The easiest way to debug the library when working on new features is using F5 in Visual Studio Code. This uses launch.json to build and run the library using ./debug/launch/main.ts as the entry point. Basic SharePoint Testing \u00b6 You can start the base debugging case by hitting F5. Before you do place a break point in ./debug/launch/sp.ts. You can also place a break point within any of the libraries or modules. Feel free to edit the sp.ts file to try things out, debug suspected issues, or test new features, etc - but please don't commit any changes as this is a shared file. See the section on creating your own debug modules . All of the setup for the node client is handled within sp.ts using the settings from the local configuration . Basic Graph Testing \u00b6 Testing and debugging Graph calls follows the same process as outlined for SharePoint, however you need to update main.ts to import graph instead of sp. You can place break points anywhere within the library code and they should be hit. All of the setup for the node client is handled within graph.ts using the settings from the local configuration . How to: Create a Debug Module \u00b6 If you are working on multiple features or want to save sample code for various tasks you can create your own debugging modules and leave them in the debug/launch folder locally. The gitignore file is setup to ignore any files that aren't already in git. Using ./debug/launch/sp.ts as a reference create a file in the debug/launch folder, let's call it mydebug.ts and add this content: // note we can use the actual package names for our imports (ex: @pnp/logging) import { Logger, LogLevel, ConsoleListener } from \"@pnp/logging\"; // using the all preset for simplicity in the example, selective imports work as expected import { sp, ListEnsureResult } from \"@pnp/sp/presets/all\"; declare var process: { exit(code?: number): void }; export async function MyDebug() { // configure your options // you can have different configs in different modules as needed for your testing/dev work sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret); }, }, }); // run some debugging const list = await sp.web.lists.ensure(\"MyFirstList\"); Logger.log({ data: list.created, level: LogLevel.Info, message: \"Was list created?\", }); if (list.created) { Logger.log({ data: list.data, level: LogLevel.Info, message: \"Raw data from list creation.\", }); } else { Logger.log({ data: null, level: LogLevel.Info, message: \"List already existed!\", }); } process.exit(0); } Update main.ts to launch your module \u00b6 First comment out the import for the default example and then add the import and function call for yours, the updated launch/main.ts should look like this: // ... // comment out the example // import { Example } from \"./example\"; // Example(); import { MyDebug } from \"./mydebug\" MyDebug(); // ... Remember, please don't commit any changes to the shared files within the debug folder. (Unless you've found a bug that needs fixing in the original file) Debug \u00b6 Place a break point within the mydebug.ts file and hit F5. Your module should run and your break point hit. You can then examine the contents of the objects and see the run time state. Remember, you can also set breakpoints within the package src folders to see exactly how things are working during your debugging scenarios. Debug Module Next Steps \u00b6 Using this pattern you can create and preserve multiple debugging scenarios in separate modules locally - they won't be added to git. You just have to update main.ts to point to the one you want to run. In Browser Debugging \u00b6 You can also serve files locally to debug as a user in the browser by serving code using ./debug/serve/main.ts as the entry. The file is served as https://localhost:8080/assets/pnp.js , allowing you to create a single page in your tenant for in browser testing. The remainder of this section describes the process to setup a SharePoint page to debug in this manner. Start the local serve \u00b6 This will serve a package with ./debug/serve/main.ts as the entry. gulp serve Add reference to library \u00b6 Within a SharePoint page add a script editor web part and then paste in the following code. The div is to give you a place to target with visual updates should you desire.
      You should see an alert with the current web's title using the default main.ts. Feel free to update main.ts to do whatever you would like, but remember not to commit changes to the shared files. Debug \u00b6 Refresh the page and open the developer tools in your browser of choice. If the pnp.js file is blocked due to security restrictions you will need to allow it. Next Steps \u00b6 You can make changes to the library and immediately see them reflected in the browser. All files are watched so changes will be available as soon as webpack reloads the package. This allows you to rapidly test the library in the browser. Now you can learn about extending the library .","title":"Debugging"},{"location":"v2/contributing/debugging/#debugging","text":"Using the steps in this article you will be able to locally debug the library internals as well as new features you are working on. Before proceeding be sure you have reviewed how to setup for local configuration and debugging.","title":"Debugging"},{"location":"v2/contributing/debugging/#debugging-library-features","text":"The easiest way to debug the library when working on new features is using F5 in Visual Studio Code. This uses launch.json to build and run the library using ./debug/launch/main.ts as the entry point.","title":"Debugging Library Features"},{"location":"v2/contributing/debugging/#basic-sharepoint-testing","text":"You can start the base debugging case by hitting F5. Before you do place a break point in ./debug/launch/sp.ts. You can also place a break point within any of the libraries or modules. Feel free to edit the sp.ts file to try things out, debug suspected issues, or test new features, etc - but please don't commit any changes as this is a shared file. See the section on creating your own debug modules . All of the setup for the node client is handled within sp.ts using the settings from the local configuration .","title":"Basic SharePoint Testing"},{"location":"v2/contributing/debugging/#basic-graph-testing","text":"Testing and debugging Graph calls follows the same process as outlined for SharePoint, however you need to update main.ts to import graph instead of sp. You can place break points anywhere within the library code and they should be hit. All of the setup for the node client is handled within graph.ts using the settings from the local configuration .","title":"Basic Graph Testing"},{"location":"v2/contributing/debugging/#how-to-create-a-debug-module","text":"If you are working on multiple features or want to save sample code for various tasks you can create your own debugging modules and leave them in the debug/launch folder locally. The gitignore file is setup to ignore any files that aren't already in git. Using ./debug/launch/sp.ts as a reference create a file in the debug/launch folder, let's call it mydebug.ts and add this content: // note we can use the actual package names for our imports (ex: @pnp/logging) import { Logger, LogLevel, ConsoleListener } from \"@pnp/logging\"; // using the all preset for simplicity in the example, selective imports work as expected import { sp, ListEnsureResult } from \"@pnp/sp/presets/all\"; declare var process: { exit(code?: number): void }; export async function MyDebug() { // configure your options // you can have different configs in different modules as needed for your testing/dev work sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret); }, }, }); // run some debugging const list = await sp.web.lists.ensure(\"MyFirstList\"); Logger.log({ data: list.created, level: LogLevel.Info, message: \"Was list created?\", }); if (list.created) { Logger.log({ data: list.data, level: LogLevel.Info, message: \"Raw data from list creation.\", }); } else { Logger.log({ data: null, level: LogLevel.Info, message: \"List already existed!\", }); } process.exit(0); }","title":"How to: Create a Debug Module"},{"location":"v2/contributing/debugging/#update-maints-to-launch-your-module","text":"First comment out the import for the default example and then add the import and function call for yours, the updated launch/main.ts should look like this: // ... // comment out the example // import { Example } from \"./example\"; // Example(); import { MyDebug } from \"./mydebug\" MyDebug(); // ... Remember, please don't commit any changes to the shared files within the debug folder. (Unless you've found a bug that needs fixing in the original file)","title":"Update main.ts to launch your module"},{"location":"v2/contributing/debugging/#debug","text":"Place a break point within the mydebug.ts file and hit F5. Your module should run and your break point hit. You can then examine the contents of the objects and see the run time state. Remember, you can also set breakpoints within the package src folders to see exactly how things are working during your debugging scenarios.","title":"Debug"},{"location":"v2/contributing/debugging/#debug-module-next-steps","text":"Using this pattern you can create and preserve multiple debugging scenarios in separate modules locally - they won't be added to git. You just have to update main.ts to point to the one you want to run.","title":"Debug Module Next Steps"},{"location":"v2/contributing/debugging/#in-browser-debugging","text":"You can also serve files locally to debug as a user in the browser by serving code using ./debug/serve/main.ts as the entry. The file is served as https://localhost:8080/assets/pnp.js , allowing you to create a single page in your tenant for in browser testing. The remainder of this section describes the process to setup a SharePoint page to debug in this manner.","title":"In Browser Debugging"},{"location":"v2/contributing/debugging/#start-the-local-serve","text":"This will serve a package with ./debug/serve/main.ts as the entry. gulp serve","title":"Start the local serve"},{"location":"v2/contributing/debugging/#add-reference-to-library","text":"Within a SharePoint page add a script editor web part and then paste in the following code. The div is to give you a place to target with visual updates should you desire.
      You should see an alert with the current web's title using the default main.ts. Feel free to update main.ts to do whatever you would like, but remember not to commit changes to the shared files.","title":"Add reference to library"},{"location":"v2/contributing/debugging/#debug_1","text":"Refresh the page and open the developer tools in your browser of choice. If the pnp.js file is blocked due to security restrictions you will need to allow it.","title":"Debug"},{"location":"v2/contributing/debugging/#next-steps","text":"You can make changes to the library and immediately see them reflected in the browser. All files are watched so changes will be available as soon as webpack reloads the package. This allows you to rapidly test the library in the browser. Now you can learn about extending the library .","title":"Next Steps"},{"location":"v2/contributing/documentation/","text":"Documentation \u00b6 Just like with tests we have invested much time in updating the documentation and when you make a change to the library you should update the associated documentation as part of the pull request. Writing Docs \u00b6 Our docs are all written in markdown and processed using MkDocs. You can use code blocks, tables, and other markdown formatting. You can review the other articles for examples on writing docs. Generally articles should focus on how to use the library and where appropriate link to official outside documents as needed. Official documentation could be Microsoft, other library project docs such as MkDocs, or other sources. Building Docs Locally \u00b6 Building the documentation locally can help you visualize change you are making to the docs. What you see locally will be what you see online. Documentation is built using MkDocs. You will need to latest version of Python (tested on version 3.7.1) and pip. If you're on the Windows operating system, make sure you have added Python to your Path environment variable . When executing the pip module on Windows you can prefix it with python -m . For example: python -m pip install mkdocs-material Install MkDocs pip install mkdocs Install the Material theme pip install mkdocs-material install the mkdocs-markdownextradata-plugin - this is used for the version variable pip install mkdocs-markdownextradata-plugin (doesn't work on Python v2.7) install redirect plugin - used to redirect from moved pages pip install mkdocs-redirects Serve it up mkdocs serve Open a browser to http://127.0.0.1:8000/ Please see the official mkdocs site for more details on working with mkdocs Next Steps \u00b6 After your changes are made, you've added/updated tests, and updated the docs you're ready to submit a pull request !","title":"Documentation"},{"location":"v2/contributing/documentation/#documentation","text":"Just like with tests we have invested much time in updating the documentation and when you make a change to the library you should update the associated documentation as part of the pull request.","title":"Documentation"},{"location":"v2/contributing/documentation/#writing-docs","text":"Our docs are all written in markdown and processed using MkDocs. You can use code blocks, tables, and other markdown formatting. You can review the other articles for examples on writing docs. Generally articles should focus on how to use the library and where appropriate link to official outside documents as needed. Official documentation could be Microsoft, other library project docs such as MkDocs, or other sources.","title":"Writing Docs"},{"location":"v2/contributing/documentation/#building-docs-locally","text":"Building the documentation locally can help you visualize change you are making to the docs. What you see locally will be what you see online. Documentation is built using MkDocs. You will need to latest version of Python (tested on version 3.7.1) and pip. If you're on the Windows operating system, make sure you have added Python to your Path environment variable . When executing the pip module on Windows you can prefix it with python -m . For example: python -m pip install mkdocs-material Install MkDocs pip install mkdocs Install the Material theme pip install mkdocs-material install the mkdocs-markdownextradata-plugin - this is used for the version variable pip install mkdocs-markdownextradata-plugin (doesn't work on Python v2.7) install redirect plugin - used to redirect from moved pages pip install mkdocs-redirects Serve it up mkdocs serve Open a browser to http://127.0.0.1:8000/ Please see the official mkdocs site for more details on working with mkdocs","title":"Building Docs Locally"},{"location":"v2/contributing/documentation/#next-steps","text":"After your changes are made, you've added/updated tests, and updated the docs you're ready to submit a pull request !","title":"Next Steps"},{"location":"v2/contributing/extending-the-library/","text":"Extending PnPjs \u00b6 This article is targeted at people wishing to extend PnPjs itself, usually by adding a method or property. At the most basic level PnPjs is a set of libraries used to build and execute a web request and handle the response from that request. Conceptually each object in the fluent chain serves as input when creating the next object in the chain. This is how configuration, url, query, and other values are passed along. To get a sense for what this looks like see the code below. This is taken from inside the webs submodule and shows how the \"webs\" property is added to the web class. // TypeScript property, returning an interface public get webs(): IWebs { // using the Webs factory function and providing \"this\" as the first parameter return Webs(this); } Understanding Factory Functions \u00b6 PnPjs v2 is designed to only expose interfaces and factory functions. Let's look at the Webs factory function, used above as an example. All factory functions in sp and graph have a similar form. // create a constant which is a function of type ISPInvokableFactory having the name Webs // this is bound by the generic type param to return an IWebs instance // and it will use the _Webs concrete class to form the internal type of the invocable export const Webs = spInvokableFactory(_Webs); The ISPInvokableFactory type looks like: export type ISPInvokableFactory = (baseUrl: string | ISharePointQueryable, path?: string) => R; And the matching graph type: (f: any): (baseUrl: string | IGraphQueryable, path?: string) => R The general idea of a factory function is that it takes two parameters. The first is either a string or Queryable derivative which forms base for the new object. The second is the next part of the url. In some cases (like the webs property example above) you will note there is no second parameter. Some classes are decorated with defaultPath, which automatically fills the second param. Don't worry too much right now about the deep internals of the library, let's instead focus on some concrete examples. import { Web } from \"@pnp/sp/webs\"; // create a web from an absolute url const web = Web(\"https://tenant.sharepoint.com\"); // as an example, create a new web using the first as a base // targets: https://tenant.sharepoint.com/sites/dev const web2 = Web(web, \"sites/dev\"); // or you can add any path components you want, here as an example we access the current user property const cu = Web(web, \"currentuser\"); const currentUserInfo = cu(); Now hey you might say - you can't create a request to current user using the Web factory. Well you can, since everything is just based on urls under the covers the actual factory names don't mean anything other than they have the appropriate properties and method hung off them. This is brought up as you will see in many cases objects being used to create queries within methods and properties that don't match their \"type\". It is an important concept when working with the library to always remember we are just building strings. Class structure \u00b6 Internally to the library we have a bit of complexity to make the whole invocable proxy architecture work and provide the typings folks expect. Here is an example implementation with extra comments explaining what is happening. You don't need to understand the entire stack to add a property or method /* The concrete class implementation. This is never exported or shown directly to consumers of the library. It is wrapped by the Proxy we do expose. It extends the _SharePointQueryableInstance class for which there is a matching _SharePointQueryableCollection. The generic parameter defines the return type of a get operation and the invoked result. Classes can have methods and properties as normal. This one has a single property as a simple example */ export class _HubSite extends _SharePointQueryableInstance { /** * Gets the ISite instance associated with this hub site */ // the tag decorator is used to provide some additional telemetry on what methods are // being called. @tag(\"hs.getSite\") public async getSite(): Promise { // we execute a request using this instance, selecting the SiteUrl property, and invoking it immediately and awaiting the result const d = await this.select(\"SiteUrl\")(); // we then return a new ISite instance created from the Site factory using the returned SiteUrl property as the baseUrl return Site(d.SiteUrl); } } /* This defines the interface we export and expose to consumers. In most cases this extends the concrete object but may add or remove some methods/properties in special cases */ export interface IHubSite extends _HubSite { } /* This defines the HubSite factory function as discussed above binding the spInvokableFactory to a generic param of IHubSite and a param of _HubSite. This is understood to mean that HubSite is a factory function that returns a types of IHubSite which the spInvokableFactory will create using _HubSite as the concrete underlying type. */ export const HubSite = spInvokableFactory(_HubSite); Add a Property \u00b6 In most cases you won't need to create the class, interface, or factory - you just want to add a property or method. An example of this is sp.web.lists. web is a property of sp and lists is a property of web. You can have a look at those classes as examples. Let's have a look at the fields on the _View class. export class _View extends _SharePointQueryableInstance { // ... other code removed // add the property, and provide a return type // return types should be interfaces public get fields(): IViewFields { // we use the ViewFields factory function supplying \"this\" as the first parameter // this will create a url like \".../fields/viewfields\" due to the defaultPath decorator // on the _ViewFields class. This is equivalent to: ViewFields(this, \"viewfields\") return ViewFields(this); } // ... other code removed } There are many examples throughout the library that follow this pattern. Add a Method \u00b6 Adding a method is just like adding a property with the key difference that a method usually does something like make a web request or act like a property but take parameters. Let's look at the _Items getById method: @defaultPath(\"items\") export class _Items extends _SharePointQueryableCollection { /** * Gets an Item by id * * @param id The integer id of the item to retrieve */ // we declare a method and set the return type to an interface public getById(id: number): IItem { // here we use the tag helper to add some telemetry to our request // we create a new IItem using the factory and appending the id value to the end // this gives us a valid url path to a single item .../items/getById(2) // we can then use the returned IItem to extend our chain or execute a request return tag.configure(Item(this).concat(`(${id})`), \"is.getById\"); } // ... other code removed } Web Request Method \u00b6 A second example is a method that performs a request. Here we use the _Item recycle method as an example: /** * Moves the list item to the Recycle Bin and returns the identifier of the new Recycle Bin item. */ // we use the tag decorator to add telemetry @tag(\"i.recycle\") // we return a promise public recycle(): Promise { // we use the spPost method to post the request created by cloning our current instance IItem using // the Item factory and adding the path \"recycle\" to the end. Url will look like .../items/getById(2)/recycle return spPost(this.clone(Item, \"recycle\")); } Augment Using Selective Imports \u00b6 To understand is how to extend functionality within the selective imports structures look at list.ts file in the items submodule. Here you can see the code below, with extra comments to explain what is happening. Again, you will see this pattern repeated throughout the library so there are many examples available. // import the addProp helper import { addProp } from \"@pnp/queryable\"; // import the _List concrete class from the types module (not the index!) import { _List } from \"../lists/types\"; // import the interface and factory we are going to add to the List import { Items, IItems } from \"./types\"; // This module declaration fixes up the types, allowing .items to appear in intellisense // when you import \"@pnp/sp/items/list\"; declare module \"../lists/types\" { // we need to extend the concrete type interface _List { readonly items: IItems; } // we need to extend the interface // this may not be strictly necessary as the IList interface extends _List so it // should pick up the same additions, but we have seen in some cases this does seem // to be required. So we include it for safety as it will all be removed during // transpilation we don't need to care about the extra code interface IList { readonly items: IItems; } } // finally we add the property to the _List class // this method call says add a property to _List named \"items\" and that property returns a result using the Items factory // The factory will be called with \"this\" when the property is accessed. If needed there is a fourth parameter to append additional path // information to the property url addProp(_List, \"items\", Items); General Rules for Extending PnPjs \u00b6 Only expose interfaces to consumers Use the factory functions except in very special cases Look for other properties and methods as examples Simple is always preferable, but not always possible - use your best judgement If you find yourself writing a ton of code to solve a problem you think should be easy, ask If you find yourself deep within the core classes or odata library trying to make a change, ask - changes to the core classes are rarely needed Next Steps \u00b6 Now that you have extended the library you need to write a test to cover it!","title":"Extending PnPjs"},{"location":"v2/contributing/extending-the-library/#extending-pnpjs","text":"This article is targeted at people wishing to extend PnPjs itself, usually by adding a method or property. At the most basic level PnPjs is a set of libraries used to build and execute a web request and handle the response from that request. Conceptually each object in the fluent chain serves as input when creating the next object in the chain. This is how configuration, url, query, and other values are passed along. To get a sense for what this looks like see the code below. This is taken from inside the webs submodule and shows how the \"webs\" property is added to the web class. // TypeScript property, returning an interface public get webs(): IWebs { // using the Webs factory function and providing \"this\" as the first parameter return Webs(this); }","title":"Extending PnPjs"},{"location":"v2/contributing/extending-the-library/#understanding-factory-functions","text":"PnPjs v2 is designed to only expose interfaces and factory functions. Let's look at the Webs factory function, used above as an example. All factory functions in sp and graph have a similar form. // create a constant which is a function of type ISPInvokableFactory having the name Webs // this is bound by the generic type param to return an IWebs instance // and it will use the _Webs concrete class to form the internal type of the invocable export const Webs = spInvokableFactory(_Webs); The ISPInvokableFactory type looks like: export type ISPInvokableFactory = (baseUrl: string | ISharePointQueryable, path?: string) => R; And the matching graph type: (f: any): (baseUrl: string | IGraphQueryable, path?: string) => R The general idea of a factory function is that it takes two parameters. The first is either a string or Queryable derivative which forms base for the new object. The second is the next part of the url. In some cases (like the webs property example above) you will note there is no second parameter. Some classes are decorated with defaultPath, which automatically fills the second param. Don't worry too much right now about the deep internals of the library, let's instead focus on some concrete examples. import { Web } from \"@pnp/sp/webs\"; // create a web from an absolute url const web = Web(\"https://tenant.sharepoint.com\"); // as an example, create a new web using the first as a base // targets: https://tenant.sharepoint.com/sites/dev const web2 = Web(web, \"sites/dev\"); // or you can add any path components you want, here as an example we access the current user property const cu = Web(web, \"currentuser\"); const currentUserInfo = cu(); Now hey you might say - you can't create a request to current user using the Web factory. Well you can, since everything is just based on urls under the covers the actual factory names don't mean anything other than they have the appropriate properties and method hung off them. This is brought up as you will see in many cases objects being used to create queries within methods and properties that don't match their \"type\". It is an important concept when working with the library to always remember we are just building strings.","title":"Understanding Factory Functions"},{"location":"v2/contributing/extending-the-library/#class-structure","text":"Internally to the library we have a bit of complexity to make the whole invocable proxy architecture work and provide the typings folks expect. Here is an example implementation with extra comments explaining what is happening. You don't need to understand the entire stack to add a property or method /* The concrete class implementation. This is never exported or shown directly to consumers of the library. It is wrapped by the Proxy we do expose. It extends the _SharePointQueryableInstance class for which there is a matching _SharePointQueryableCollection. The generic parameter defines the return type of a get operation and the invoked result. Classes can have methods and properties as normal. This one has a single property as a simple example */ export class _HubSite extends _SharePointQueryableInstance { /** * Gets the ISite instance associated with this hub site */ // the tag decorator is used to provide some additional telemetry on what methods are // being called. @tag(\"hs.getSite\") public async getSite(): Promise { // we execute a request using this instance, selecting the SiteUrl property, and invoking it immediately and awaiting the result const d = await this.select(\"SiteUrl\")(); // we then return a new ISite instance created from the Site factory using the returned SiteUrl property as the baseUrl return Site(d.SiteUrl); } } /* This defines the interface we export and expose to consumers. In most cases this extends the concrete object but may add or remove some methods/properties in special cases */ export interface IHubSite extends _HubSite { } /* This defines the HubSite factory function as discussed above binding the spInvokableFactory to a generic param of IHubSite and a param of _HubSite. This is understood to mean that HubSite is a factory function that returns a types of IHubSite which the spInvokableFactory will create using _HubSite as the concrete underlying type. */ export const HubSite = spInvokableFactory(_HubSite);","title":"Class structure"},{"location":"v2/contributing/extending-the-library/#add-a-property","text":"In most cases you won't need to create the class, interface, or factory - you just want to add a property or method. An example of this is sp.web.lists. web is a property of sp and lists is a property of web. You can have a look at those classes as examples. Let's have a look at the fields on the _View class. export class _View extends _SharePointQueryableInstance { // ... other code removed // add the property, and provide a return type // return types should be interfaces public get fields(): IViewFields { // we use the ViewFields factory function supplying \"this\" as the first parameter // this will create a url like \".../fields/viewfields\" due to the defaultPath decorator // on the _ViewFields class. This is equivalent to: ViewFields(this, \"viewfields\") return ViewFields(this); } // ... other code removed } There are many examples throughout the library that follow this pattern.","title":"Add a Property"},{"location":"v2/contributing/extending-the-library/#add-a-method","text":"Adding a method is just like adding a property with the key difference that a method usually does something like make a web request or act like a property but take parameters. Let's look at the _Items getById method: @defaultPath(\"items\") export class _Items extends _SharePointQueryableCollection { /** * Gets an Item by id * * @param id The integer id of the item to retrieve */ // we declare a method and set the return type to an interface public getById(id: number): IItem { // here we use the tag helper to add some telemetry to our request // we create a new IItem using the factory and appending the id value to the end // this gives us a valid url path to a single item .../items/getById(2) // we can then use the returned IItem to extend our chain or execute a request return tag.configure(Item(this).concat(`(${id})`), \"is.getById\"); } // ... other code removed }","title":"Add a Method"},{"location":"v2/contributing/extending-the-library/#web-request-method","text":"A second example is a method that performs a request. Here we use the _Item recycle method as an example: /** * Moves the list item to the Recycle Bin and returns the identifier of the new Recycle Bin item. */ // we use the tag decorator to add telemetry @tag(\"i.recycle\") // we return a promise public recycle(): Promise { // we use the spPost method to post the request created by cloning our current instance IItem using // the Item factory and adding the path \"recycle\" to the end. Url will look like .../items/getById(2)/recycle return spPost(this.clone(Item, \"recycle\")); }","title":"Web Request Method"},{"location":"v2/contributing/extending-the-library/#augment-using-selective-imports","text":"To understand is how to extend functionality within the selective imports structures look at list.ts file in the items submodule. Here you can see the code below, with extra comments to explain what is happening. Again, you will see this pattern repeated throughout the library so there are many examples available. // import the addProp helper import { addProp } from \"@pnp/queryable\"; // import the _List concrete class from the types module (not the index!) import { _List } from \"../lists/types\"; // import the interface and factory we are going to add to the List import { Items, IItems } from \"./types\"; // This module declaration fixes up the types, allowing .items to appear in intellisense // when you import \"@pnp/sp/items/list\"; declare module \"../lists/types\" { // we need to extend the concrete type interface _List { readonly items: IItems; } // we need to extend the interface // this may not be strictly necessary as the IList interface extends _List so it // should pick up the same additions, but we have seen in some cases this does seem // to be required. So we include it for safety as it will all be removed during // transpilation we don't need to care about the extra code interface IList { readonly items: IItems; } } // finally we add the property to the _List class // this method call says add a property to _List named \"items\" and that property returns a result using the Items factory // The factory will be called with \"this\" when the property is accessed. If needed there is a fourth parameter to append additional path // information to the property url addProp(_List, \"items\", Items);","title":"Augment Using Selective Imports"},{"location":"v2/contributing/extending-the-library/#general-rules-for-extending-pnpjs","text":"Only expose interfaces to consumers Use the factory functions except in very special cases Look for other properties and methods as examples Simple is always preferable, but not always possible - use your best judgement If you find yourself writing a ton of code to solve a problem you think should be easy, ask If you find yourself deep within the core classes or odata library trying to make a change, ask - changes to the core classes are rarely needed","title":"General Rules for Extending PnPjs"},{"location":"v2/contributing/extending-the-library/#next-steps","text":"Now that you have extended the library you need to write a test to cover it!","title":"Next Steps"},{"location":"v2/contributing/local-debug-configuration/","text":"Local Debugging Configuration \u00b6 This article covers the local setup required to debug the library and run tests. This only needs to be done once (unless you update the app registrations, then you just need to update the settings.js file accordingly). Create settings.js \u00b6 Both local debugging and tests make use of a settings.js file located in the root of the project. Ensure you create a settings.js files by copying settings.example.js and renaming it to settings.js. For more information the settings file please see Settings Minimal Configuration \u00b6 You can control which tests are run by including or omitting sp and graph sections. If sp is present and graph is not, only sp tests are run. Include both and all tests are run, respecting the enableWebTests flag. The following configuration file allows you to run all the tests that do not contact services. var sets = { testing: { enableWebTests: false, } } module.exports = sets; Test your setup \u00b6 If you hit F5 in VSCode now you should be able to see the full response from getting the web's title in the internal console window. If not, ensure that you have properly updated the settings file and registered the add-in perms correctly.","title":"Local Debugging Configuration"},{"location":"v2/contributing/local-debug-configuration/#local-debugging-configuration","text":"This article covers the local setup required to debug the library and run tests. This only needs to be done once (unless you update the app registrations, then you just need to update the settings.js file accordingly).","title":"Local Debugging Configuration"},{"location":"v2/contributing/local-debug-configuration/#create-settingsjs","text":"Both local debugging and tests make use of a settings.js file located in the root of the project. Ensure you create a settings.js files by copying settings.example.js and renaming it to settings.js. For more information the settings file please see Settings","title":"Create settings.js"},{"location":"v2/contributing/local-debug-configuration/#minimal-configuration","text":"You can control which tests are run by including or omitting sp and graph sections. If sp is present and graph is not, only sp tests are run. Include both and all tests are run, respecting the enableWebTests flag. The following configuration file allows you to run all the tests that do not contact services. var sets = { testing: { enableWebTests: false, } } module.exports = sets;","title":"Minimal Configuration"},{"location":"v2/contributing/local-debug-configuration/#test-your-setup","text":"If you hit F5 in VSCode now you should be able to see the full response from getting the web's title in the internal console window. If not, ensure that you have properly updated the settings file and registered the add-in perms correctly.","title":"Test your setup"},{"location":"v2/contributing/pull-requests/","text":"Submitting Pull Requests \u00b6 Pull requests may be large or small - adding whole new features or fixing some misspellings. Regardless, they are all appreciated and help improve the library for everyone! By following the below guidelines we'll have an easier time merging your work and getting it into the next release. Target your pull requests to the version-2 branch Add/Update any relevant docs articles in the relevant package's docs folder related to your changes Include a test for any new functionality and ensure all existing tests are passing by running npm test Ensure linting checks pass by typing npm run lint Ensure everything works for a build by running npm run package Keep your PRs as simple as possible and describe the changes to help the reviewer understand your work If you have an idea for a larger change to the library please open an issue and let's discuss before you invest many hours - these are very welcome but want to ensure it is something we can merge before you spend the time :) If you need to target a PR for version 1, please target the \"version-1\" branch Sharing is Caring - Pull Request Guidance \u00b6 The PnP \"Sharing Is Caring\" initiative teaches the basics around making changes in GitHub, submitting pull requests to the PnP & Microsoft 365 open-source repositories such as PnPjs. Every month, we provide multiple live hands-on sessions that walk attendees through the process of using and contributing to PnP initiatives. To learn more and register for an upcoming session, please visit the Sharing is Caring website. Next Steps \u00b6 Now that you've submitted your PR please keep an eye on it as we might have questions. Once an initial review is complete we'll tag it with the expected version number for which it is targeted. Thank you for helping PnPjs grow and improve!!","title":"Submitting Pull Requests"},{"location":"v2/contributing/pull-requests/#submitting-pull-requests","text":"Pull requests may be large or small - adding whole new features or fixing some misspellings. Regardless, they are all appreciated and help improve the library for everyone! By following the below guidelines we'll have an easier time merging your work and getting it into the next release. Target your pull requests to the version-2 branch Add/Update any relevant docs articles in the relevant package's docs folder related to your changes Include a test for any new functionality and ensure all existing tests are passing by running npm test Ensure linting checks pass by typing npm run lint Ensure everything works for a build by running npm run package Keep your PRs as simple as possible and describe the changes to help the reviewer understand your work If you have an idea for a larger change to the library please open an issue and let's discuss before you invest many hours - these are very welcome but want to ensure it is something we can merge before you spend the time :) If you need to target a PR for version 1, please target the \"version-1\" branch","title":"Submitting Pull Requests"},{"location":"v2/contributing/pull-requests/#sharing-is-caring-pull-request-guidance","text":"The PnP \"Sharing Is Caring\" initiative teaches the basics around making changes in GitHub, submitting pull requests to the PnP & Microsoft 365 open-source repositories such as PnPjs. Every month, we provide multiple live hands-on sessions that walk attendees through the process of using and contributing to PnP initiatives. To learn more and register for an upcoming session, please visit the Sharing is Caring website.","title":"Sharing is Caring - Pull Request Guidance"},{"location":"v2/contributing/pull-requests/#next-steps","text":"Now that you've submitted your PR please keep an eye on it as we might have questions. Once an initial review is complete we'll tag it with the expected version number for which it is targeted. Thank you for helping PnPjs grow and improve!!","title":"Next Steps"},{"location":"v2/contributing/setup-dev-machine/","text":"Setting up your Developer Machine \u00b6 If you are a longtime client side developer you likely have your machine already configured and can skip to forking the repo and debugging . Setup your development environment \u00b6 These steps will help you get your environment setup for contributing to the core library. Install Visual Studio Code - this is the development environment we use so the contribution sections expect you are as well. If you prefer you can use Visual Studio or any editor you like. Install Node JS - this provides two key capabilities; the first is the nodejs server which will act as our development server (think iisexpress), the second is npm a package manager (think nuget). This library requires node >= 10.18.0 On Windows: Install Python [Optional] Install the tslint extension in VS Code: Press Shift + Ctrl + \"p\" to open the command panel Begin typing \"install extension\" and select the command when it appears in view Begin typing \"tslint\" and select the package when it appears in view Restart Code after installation Fork The Repo \u00b6 All of our contributions come via pull requests and you'll need to fork the repository Now we need to fork and clone the git repository. This can be done using your console or using your preferred Git GUI tool. Once you have the code locally, navigate to the root of the project in your console. Type the following command: npm install Follow the guidance to complete the one-time local configuration required to debug and run tests. Then you can follow the guidance in the debugging article.","title":"Setting up your Developer Machine"},{"location":"v2/contributing/setup-dev-machine/#setting-up-your-developer-machine","text":"If you are a longtime client side developer you likely have your machine already configured and can skip to forking the repo and debugging .","title":"Setting up your Developer Machine"},{"location":"v2/contributing/setup-dev-machine/#setup-your-development-environment","text":"These steps will help you get your environment setup for contributing to the core library. Install Visual Studio Code - this is the development environment we use so the contribution sections expect you are as well. If you prefer you can use Visual Studio or any editor you like. Install Node JS - this provides two key capabilities; the first is the nodejs server which will act as our development server (think iisexpress), the second is npm a package manager (think nuget). This library requires node >= 10.18.0 On Windows: Install Python [Optional] Install the tslint extension in VS Code: Press Shift + Ctrl + \"p\" to open the command panel Begin typing \"install extension\" and select the command when it appears in view Begin typing \"tslint\" and select the package when it appears in view Restart Code after installation","title":"Setup your development environment"},{"location":"v2/contributing/setup-dev-machine/#fork-the-repo","text":"All of our contributions come via pull requests and you'll need to fork the repository Now we need to fork and clone the git repository. This can be done using your console or using your preferred Git GUI tool. Once you have the code locally, navigate to the root of the project in your console. Type the following command: npm install Follow the guidance to complete the one-time local configuration required to debug and run tests. Then you can follow the guidance in the debugging article.","title":"Fork The Repo"},{"location":"v2/graph/","text":"@pnp/graph \u00b6 This package contains the fluent api used to call the graph rest services. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save Import the library into your application and access the root sp object import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; (function main() { // here we will load the current web's properties graph.groups().then(g => { console.log(`Groups: ${JSON.stringify(g, null, 4)}`); }); })() Getting Started with SharePoint Framework \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save Import the library into your application, update OnInit, and access the root sp object in render import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present graph.setup({ spfxContext: this.context }); }); } // ... public render(): void { // A simple loading message this.domElement.innerHTML = `Loading...`; // here we will load the current web's properties graph.groups().then(groups => { this.domElement.innerHTML = `Groups:
        ${groups.map(g => `
      • ${g.displayName}
      • `).join(\"\")}
      `; }); } Getting Started on Nodejs \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph @pnp/nodejs --save Import the library into your application, setup the node client, make a request import { graph } from \"@pnp/graph\"; import { AdalFetchClient } from \"@pnp/nodejs\"; import \"@pnp/graph/groups\"; // do this once per page load graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{tenant}.onmicrosoft.com\", \"AAD Application Id\", \"AAD Application Secret\"); }, }, }); // here we will load the groups information graph.groups().then(g => { console.log(`Groups: ${JSON.stringify(g, null, 4)}`); });","title":"@pnp/graph"},{"location":"v2/graph/#pnpgraph","text":"This package contains the fluent api used to call the graph rest services.","title":"@pnp/graph"},{"location":"v2/graph/#getting-started","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save Import the library into your application and access the root sp object import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; (function main() { // here we will load the current web's properties graph.groups().then(g => { console.log(`Groups: ${JSON.stringify(g, null, 4)}`); }); })()","title":"Getting Started"},{"location":"v2/graph/#getting-started-with-sharepoint-framework","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph --save Import the library into your application, update OnInit, and access the root sp object in render import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present graph.setup({ spfxContext: this.context }); }); } // ... public render(): void { // A simple loading message this.domElement.innerHTML = `Loading...`; // here we will load the current web's properties graph.groups().then(groups => { this.domElement.innerHTML = `Groups:
        ${groups.map(g => `
      • ${g.displayName}
      • `).join(\"\")}
      `; }); }","title":"Getting Started with SharePoint Framework"},{"location":"v2/graph/#getting-started-on-nodejs","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable @pnp/graph @pnp/nodejs --save Import the library into your application, setup the node client, make a request import { graph } from \"@pnp/graph\"; import { AdalFetchClient } from \"@pnp/nodejs\"; import \"@pnp/graph/groups\"; // do this once per page load graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{tenant}.onmicrosoft.com\", \"AAD Application Id\", \"AAD Application Secret\"); }, }, }); // here we will load the groups information graph.groups().then(g => { console.log(`Groups: ${JSON.stringify(g, null, 4)}`); });","title":"Getting Started on Nodejs"},{"location":"v2/graph/calendars/","text":"@pnp/graph/calendars \u00b6 Calendars exist in Outlook and can belong to either a user or group. With @pnp/graph@<=2.0.6 , only events for a user and group's default calendar could be fetched/created/updated. In versions 2.0.7 and up, all calendars and their events can be fetched. More information can be found in the official Graph documentation: Calendar Resource Type Event Resource Type ICalendar, ICalendars \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/calendars\"; Preset: All import { graph } from \"@pnp/graph/presets/all\"; Get All Calendars For a User \u00b6 import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const calendars = await graph.users.getById('user@tenant.onmicrosoft.com').calendars(); const myCalendars = await graph.me.calendars(); Get a Specific Calendar For a User \u00b6 import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const CALENDAR_ID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA-xUBMMopY1NkrWA0qGcXHsHAG4I-wMXjoRMkgRnRetM5oIAAAIBBgAAAG4I-wMXjoRMkgRnRetM5oIAAAIsYgAAAA=='; const calendar = await graph.users.getById('user@tenant.onmicrosoft.com').calendars.getById(CALENDAR_ID)(); const myCalendar = await graph.me.calendars.getById(CALENDAR_ID)(); Get a User's Default Calendar \u00b6 import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const calendar = await graph.users.getById('user@tenant.onmicrosoft.com').calendar(); const myCalendar = await graph.me.calendar(); Get Events For a User's Default Calendar \u00b6 import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; // You can get the default calendar events const events = await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events(); // or get all events for the user const events = await graph.users.getById('user@tenant.onmicrosoft.com').events(); // You can get my default calendar events const events = await graph.me.calendar.events(); // or get all events for me const events = await graph.me.events(); Get Events By ID \u00b6 You can use .events.getByID to search through all the events in all calendars or narrow the request to a specific calendar. import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const CalendarID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA=='; const EventID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA-xUBMMopY1NkrWA0qGcXHsHAG4I-wMXjoRMkgRnRetM5oIAAAIBBgAAAG4I-wMXjoRMkgRnRetM5oIAAAIsYgAAAA=='; // Get events by ID const event = await graph.users.getById('user@tenant.onmicrosoft.com').events.getByID(EventID); const events = await graph.me.events.getByID(EventID); // Get an event by ID from a specific calendar const event = await graph.users.getById('user@tenant.onmicrosoft.com').calendars.getByID(CalendarID).events.getByID(EventID); const events = await graph.me.calendars.getByID(CalendarID).events.getByID(EventID); Create Events \u00b6 This will work on any IEvents objects (e.g. anything accessed using an events key). import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events.add( { \"subject\": \"Let's go for lunch\", \"body\": { \"contentType\": \"HTML\", \"content\": \"Does late morning work for you?\" }, \"start\": { \"dateTime\": \"2017-04-15T12:00:00\", \"timeZone\": \"Pacific Standard Time\" }, \"end\": { \"dateTime\": \"2017-04-15T14:00:00\", \"timeZone\": \"Pacific Standard Time\" }, \"location\":{ \"displayName\":\"Harry's Bar\" }, \"attendees\": [ { \"emailAddress\": { \"address\":\"samanthab@contoso.onmicrosoft.com\", \"name\": \"Samantha Booth\" }, \"type\": \"required\" } ] }); Update Events \u00b6 This will work on any IEvents objects (e.g. anything accessed using an events key). import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const EVENT_ID = 'BBMkAGZjNmY6MDM3LWI3YTYtNERhZC05Y2FkLTgyZjcwZjE4OTI5ZQBGAAAAAAD8VQTDKKWNTY61gNKhnFzLBwBuCP8DF46ETJIEZ0XrTOaCAAAAAAENAABuCP8DF46ETJFEZ0EnTOaCAAFvdoJvAAA='; await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events.getById(EVENT_ID).update({ reminderMinutesBeforeStart: 99, }); Delete Event \u00b6 This will work on any IEvents objects (e.g. anything accessed using an events key). import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const EVENT_ID = 'BBMkAGZjNmY6MDM3LWI3YTYtNERhZC05Y2FkLTgyZjcwZjE4OTI5ZQBGAAAAAAD8VQTDKKWNTY61gNKhnFzLBwBuCP8DF46ETJIEZ0XrTOaCAAAAAAENAABuCP8DF46ETJFEZ0EnTOaCAAFvdoJvAAA='; await graph.users.getById('user@tenant.onmicrosoft.com').events.getById(EVENT_ID).delete(); await graph.me.events.getById(EVENT_ID).delete(); Get Calendar for a Group \u00b6 import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/groups'; const calendar = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').calendar(); Get Events for a Group \u00b6 import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/groups'; // You can do one of const events = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').calendar.events(); // or const events = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').events(); Get Calendar View \u00b6 Added in 2.0.7 Gets the events in a calendar during a specified date range. import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; // basic request, note need to invoke the returned queryable const view = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView(\"2020-01-01\", \"2020-03-01\")(); // you can use select, top, etc to filter your returned results const view2 = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView(\"2020-01-01\", \"2020-03-01\").select(\"subject\").top(3)(); // you can specify times along with the dates const view3 = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView(\"2020-01-01T19:00:00-08:00\", \"2020-03-01T19:00:00-08:00\")(); const view4 = await graph.me.calendarView(\"2020-01-01\", \"2020-03-01\")();","title":"@pnp/graph/calendars"},{"location":"v2/graph/calendars/#pnpgraphcalendars","text":"Calendars exist in Outlook and can belong to either a user or group. With @pnp/graph@<=2.0.6 , only events for a user and group's default calendar could be fetched/created/updated. In versions 2.0.7 and up, all calendars and their events can be fetched. More information can be found in the official Graph documentation: Calendar Resource Type Event Resource Type","title":"@pnp/graph/calendars"},{"location":"v2/graph/calendars/#icalendar-icalendars","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/calendars\"; Preset: All import { graph } from \"@pnp/graph/presets/all\";","title":"ICalendar, ICalendars"},{"location":"v2/graph/calendars/#get-all-calendars-for-a-user","text":"import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const calendars = await graph.users.getById('user@tenant.onmicrosoft.com').calendars(); const myCalendars = await graph.me.calendars();","title":"Get All Calendars For a User"},{"location":"v2/graph/calendars/#get-a-specific-calendar-for-a-user","text":"import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const CALENDAR_ID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA-xUBMMopY1NkrWA0qGcXHsHAG4I-wMXjoRMkgRnRetM5oIAAAIBBgAAAG4I-wMXjoRMkgRnRetM5oIAAAIsYgAAAA=='; const calendar = await graph.users.getById('user@tenant.onmicrosoft.com').calendars.getById(CALENDAR_ID)(); const myCalendar = await graph.me.calendars.getById(CALENDAR_ID)();","title":"Get a Specific Calendar For a User"},{"location":"v2/graph/calendars/#get-a-users-default-calendar","text":"import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const calendar = await graph.users.getById('user@tenant.onmicrosoft.com').calendar(); const myCalendar = await graph.me.calendar();","title":"Get a User's Default Calendar"},{"location":"v2/graph/calendars/#get-events-for-a-users-default-calendar","text":"import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; // You can get the default calendar events const events = await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events(); // or get all events for the user const events = await graph.users.getById('user@tenant.onmicrosoft.com').events(); // You can get my default calendar events const events = await graph.me.calendar.events(); // or get all events for me const events = await graph.me.events();","title":"Get Events For a User's Default Calendar"},{"location":"v2/graph/calendars/#get-events-by-id","text":"You can use .events.getByID to search through all the events in all calendars or narrow the request to a specific calendar. import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const CalendarID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA=='; const EventID = 'AQMkAGZjNmY0MDN3LRI3YTYtNDQAFWQtOWNhZC04MmY3MGYxODkeOWUARgAAA-xUBMMopY1NkrWA0qGcXHsHAG4I-wMXjoRMkgRnRetM5oIAAAIBBgAAAG4I-wMXjoRMkgRnRetM5oIAAAIsYgAAAA=='; // Get events by ID const event = await graph.users.getById('user@tenant.onmicrosoft.com').events.getByID(EventID); const events = await graph.me.events.getByID(EventID); // Get an event by ID from a specific calendar const event = await graph.users.getById('user@tenant.onmicrosoft.com').calendars.getByID(CalendarID).events.getByID(EventID); const events = await graph.me.calendars.getByID(CalendarID).events.getByID(EventID);","title":"Get Events By ID"},{"location":"v2/graph/calendars/#create-events","text":"This will work on any IEvents objects (e.g. anything accessed using an events key). import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events.add( { \"subject\": \"Let's go for lunch\", \"body\": { \"contentType\": \"HTML\", \"content\": \"Does late morning work for you?\" }, \"start\": { \"dateTime\": \"2017-04-15T12:00:00\", \"timeZone\": \"Pacific Standard Time\" }, \"end\": { \"dateTime\": \"2017-04-15T14:00:00\", \"timeZone\": \"Pacific Standard Time\" }, \"location\":{ \"displayName\":\"Harry's Bar\" }, \"attendees\": [ { \"emailAddress\": { \"address\":\"samanthab@contoso.onmicrosoft.com\", \"name\": \"Samantha Booth\" }, \"type\": \"required\" } ] });","title":"Create Events"},{"location":"v2/graph/calendars/#update-events","text":"This will work on any IEvents objects (e.g. anything accessed using an events key). import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const EVENT_ID = 'BBMkAGZjNmY6MDM3LWI3YTYtNERhZC05Y2FkLTgyZjcwZjE4OTI5ZQBGAAAAAAD8VQTDKKWNTY61gNKhnFzLBwBuCP8DF46ETJIEZ0XrTOaCAAAAAAENAABuCP8DF46ETJFEZ0EnTOaCAAFvdoJvAAA='; await graph.users.getById('user@tenant.onmicrosoft.com').calendar.events.getById(EVENT_ID).update({ reminderMinutesBeforeStart: 99, });","title":"Update Events"},{"location":"v2/graph/calendars/#delete-event","text":"This will work on any IEvents objects (e.g. anything accessed using an events key). import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; const EVENT_ID = 'BBMkAGZjNmY6MDM3LWI3YTYtNERhZC05Y2FkLTgyZjcwZjE4OTI5ZQBGAAAAAAD8VQTDKKWNTY61gNKhnFzLBwBuCP8DF46ETJIEZ0XrTOaCAAAAAAENAABuCP8DF46ETJFEZ0EnTOaCAAFvdoJvAAA='; await graph.users.getById('user@tenant.onmicrosoft.com').events.getById(EVENT_ID).delete(); await graph.me.events.getById(EVENT_ID).delete();","title":"Delete Event"},{"location":"v2/graph/calendars/#get-calendar-for-a-group","text":"import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/groups'; const calendar = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').calendar();","title":"Get Calendar for a Group"},{"location":"v2/graph/calendars/#get-events-for-a-group","text":"import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/groups'; // You can do one of const events = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').calendar.events(); // or const events = await graph.groups.getById('21aaf779-f6d8-40bd-88c2-4a03f456ee82').events();","title":"Get Events for a Group"},{"location":"v2/graph/calendars/#get-calendar-view","text":"Added in 2.0.7 Gets the events in a calendar during a specified date range. import { graph } from '@pnp/graph'; import '@pnp/graph/calendars'; import '@pnp/graph/users'; // basic request, note need to invoke the returned queryable const view = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView(\"2020-01-01\", \"2020-03-01\")(); // you can use select, top, etc to filter your returned results const view2 = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView(\"2020-01-01\", \"2020-03-01\").select(\"subject\").top(3)(); // you can specify times along with the dates const view3 = await graph.users.getById('user@tenant.onmicrosoft.com').calendarView(\"2020-01-01T19:00:00-08:00\", \"2020-03-01T19:00:00-08:00\")(); const view4 = await graph.me.calendarView(\"2020-01-01\", \"2020-03-01\")();","title":"Get Calendar View"},{"location":"v2/graph/contacts/","text":"@pnp/graph/contacts \u00b6 The ability to manage contacts and folders in Outlook is a capability introduced in version 1.2.2 of @pnp/graph. Through the methods described you can add and edit both contacts and folders in a users Outlook. More information can be found in the official Graph documentation: Contact Resource Type IContact, IContacts, IContactFolder, IContactFolders \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/contacts\"; Preset: All import { graph } from \"@pnp/graph/presets/all\"; Set up notes \u00b6 To make user calls you can use getById where the id is the users email address. Contact ID, Folder ID, and Parent Folder ID use the following format \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\" Get all of the Contacts \u00b6 Gets a list of all the contacts for the user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contacts = await graph.users.getById('user@tenant.onmicrosoft.com').contacts(); const contacts2 = await graph.me.contacts(); Get Contact by Id \u00b6 Gets a specific contact by ID for the user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"; const contact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID)(); const contact2 = await graph.me.contacts.getById(contactID)(); Add a new Contact \u00b6 Adds a new contact for the user. import { graph } from \"@pnp/graph\"; import { EmailAddress } from \"@microsoft/microsoft-graph-types\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const addedContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']); const addedContact2 = await graph.me.contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']); Update a Contact \u00b6 Updates a specific contact by ID for teh designated user import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"; const updContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID).update({birthday: \"1986-05-30\" }); const updContact2 = await graph.me.contacts.getById(contactID).update({birthday: \"1986-05-30\" }); Delete a Contact \u00b6 Delete a contact from the list of contacts for a user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"; const delContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID).delete(); const delContact2 = await graph.me.contacts.getById(contactID).delete(); Get all of the Contact Folders \u00b6 Get all the folders for the designated user's contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactFolders = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders(); const contactFolders2 = await graph.me.contactFolders(); Get Contact Folder by Id \u00b6 Get a contact folder by ID for the specified user import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const contactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID)(); const contactFolder2 = await graph.me.contactFolders.getById(folderID)(); Add a new Contact Folder \u00b6 Add a new folder in the users contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const parentFolderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAAAAAEOAAA=\"; const addedContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.add(\"New Folder\", parentFolderID); const addedContactFolder2 = await graph.me.contactFolders.add(\"New Folder\", parentFolderID); Update a Contact Folder \u00b6 Update an existing folder in the users contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const updContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).update({displayName: \"Updated Folder\" }); const updContactFolder2 = await graph.me.contactFolders.getById(folderID).update({displayName: \"Updated Folder\" }); Delete a Contact Folder \u00b6 Delete a folder from the users contacts list. Deleting a folder deletes the contacts in that folder. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const delContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).delete(); const delContactFolder2 = await graph.me.contactFolders.getById(folderID).delete(); Get all of the Contacts from the Contact Folder \u00b6 Get all the contacts in a folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const contactsInContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).contacts(); const contactsInContactFolder2 = await graph.me.contactFolders.getById(folderID).contacts(); Get Child Folders of the Contact Folder \u00b6 Get child folders from contact folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const childFolders = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders(); const childFolders2 = await graph.me.contactFolders.getById(folderID).childFolders(); Add a new Child Folder \u00b6 Add a new child folder to a contact folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const addedChildFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.add(\"Sub Folder\", folderID); const addedChildFolder2 = await graph.me.contactFolders.getById(folderID).childFolders.add(\"Sub Folder\", folderID); Get Child Folder by Id \u00b6 Get child folder by ID from user contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const subFolderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqIZAAA=\"; const childFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.getById(subFolderID)(); const childFolder2 = await graph.me.contactFolders.getById(folderID).childFolders.getById(subFolderID)(); Add Contact in Child Folder of Contact Folder \u00b6 Add a new contact to a child folder import { graph } from \"@pnp/graph\"; import { EmailAddress } from \"./@microsoft/microsoft-graph-types\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const subFolderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqIZAAA=\"; const addedContact = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.getById(subFolderID).contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']); const addedContact2 = await graph.me.contactFolders.getById(folderID).childFolders.getById(subFolderID).contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);","title":"@pnp/graph/contacts"},{"location":"v2/graph/contacts/#pnpgraphcontacts","text":"The ability to manage contacts and folders in Outlook is a capability introduced in version 1.2.2 of @pnp/graph. Through the methods described you can add and edit both contacts and folders in a users Outlook. More information can be found in the official Graph documentation: Contact Resource Type","title":"@pnp/graph/contacts"},{"location":"v2/graph/contacts/#icontact-icontacts-icontactfolder-icontactfolders","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/contacts\"; Preset: All import { graph } from \"@pnp/graph/presets/all\";","title":"IContact, IContacts, IContactFolder, IContactFolders"},{"location":"v2/graph/contacts/#set-up-notes","text":"To make user calls you can use getById where the id is the users email address. Contact ID, Folder ID, and Parent Folder ID use the following format \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"","title":"Set up notes"},{"location":"v2/graph/contacts/#get-all-of-the-contacts","text":"Gets a list of all the contacts for the user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contacts = await graph.users.getById('user@tenant.onmicrosoft.com').contacts(); const contacts2 = await graph.me.contacts();","title":"Get all of the Contacts"},{"location":"v2/graph/contacts/#get-contact-by-id","text":"Gets a specific contact by ID for the user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"; const contact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID)(); const contact2 = await graph.me.contacts.getById(contactID)();","title":"Get Contact by Id"},{"location":"v2/graph/contacts/#add-a-new-contact","text":"Adds a new contact for the user. import { graph } from \"@pnp/graph\"; import { EmailAddress } from \"@microsoft/microsoft-graph-types\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const addedContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']); const addedContact2 = await graph.me.contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);","title":"Add a new Contact"},{"location":"v2/graph/contacts/#update-a-contact","text":"Updates a specific contact by ID for teh designated user import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"; const updContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID).update({birthday: \"1986-05-30\" }); const updContact2 = await graph.me.contacts.getById(contactID).update({birthday: \"1986-05-30\" });","title":"Update a Contact"},{"location":"v2/graph/contacts/#delete-a-contact","text":"Delete a contact from the list of contacts for a user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwBGAAAAAAC75QV12PBiRIjb8MNVIrJrBwBgs0NT6NreR57m1u_D8SpPAAAAAAEOAABgs0NT6NreR57m1u_D8SpPAAFCCnApAAA=\"; const delContact = await graph.users.getById('user@tenant.onmicrosoft.com').contacts.getById(contactID).delete(); const delContact2 = await graph.me.contacts.getById(contactID).delete();","title":"Delete a Contact"},{"location":"v2/graph/contacts/#get-all-of-the-contact-folders","text":"Get all the folders for the designated user's contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const contactFolders = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders(); const contactFolders2 = await graph.me.contactFolders();","title":"Get all of the Contact Folders"},{"location":"v2/graph/contacts/#get-contact-folder-by-id","text":"Get a contact folder by ID for the specified user import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const contactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID)(); const contactFolder2 = await graph.me.contactFolders.getById(folderID)();","title":"Get Contact Folder by Id"},{"location":"v2/graph/contacts/#add-a-new-contact-folder","text":"Add a new folder in the users contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const parentFolderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAAAAAEOAAA=\"; const addedContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.add(\"New Folder\", parentFolderID); const addedContactFolder2 = await graph.me.contactFolders.add(\"New Folder\", parentFolderID);","title":"Add a new Contact Folder"},{"location":"v2/graph/contacts/#update-a-contact-folder","text":"Update an existing folder in the users contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const updContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).update({displayName: \"Updated Folder\" }); const updContactFolder2 = await graph.me.contactFolders.getById(folderID).update({displayName: \"Updated Folder\" });","title":"Update a Contact Folder"},{"location":"v2/graph/contacts/#delete-a-contact-folder","text":"Delete a folder from the users contacts list. Deleting a folder deletes the contacts in that folder. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const delContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).delete(); const delContactFolder2 = await graph.me.contactFolders.getById(folderID).delete();","title":"Delete a Contact Folder"},{"location":"v2/graph/contacts/#get-all-of-the-contacts-from-the-contact-folder","text":"Get all the contacts in a folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const contactsInContactFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).contacts(); const contactsInContactFolder2 = await graph.me.contactFolders.getById(folderID).contacts();","title":"Get all of the Contacts from the Contact Folder"},{"location":"v2/graph/contacts/#get-child-folders-of-the-contact-folder","text":"Get child folders from contact folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const childFolders = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders(); const childFolders2 = await graph.me.contactFolders.getById(folderID).childFolders();","title":"Get Child Folders of the Contact Folder"},{"location":"v2/graph/contacts/#add-a-new-child-folder","text":"Add a new child folder to a contact folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const addedChildFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.add(\"Sub Folder\", folderID); const addedChildFolder2 = await graph.me.contactFolders.getById(folderID).childFolders.add(\"Sub Folder\", folderID);","title":"Add a new Child Folder"},{"location":"v2/graph/contacts/#get-child-folder-by-id","text":"Get child folder by ID from user contacts import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const subFolderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqIZAAA=\"; const childFolder = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.getById(subFolderID)(); const childFolder2 = await graph.me.contactFolders.getById(folderID).childFolders.getById(subFolderID)();","title":"Get Child Folder by Id"},{"location":"v2/graph/contacts/#add-contact-in-child-folder-of-contact-folder","text":"Add a new contact to a child folder import { graph } from \"@pnp/graph\"; import { EmailAddress } from \"./@microsoft/microsoft-graph-types\"; import \"@pnp/graph/users\" import \"@pnp/graph/contacts\" const folderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqH9AAA=\"; const subFolderID = \"AAMkADY1OTQ5MTM0LTU2OTktNDI0Yy1iODFjLWNiY2RmMzNjODUxYwAuAAAAAAC75QV12PBiRIjb8MNVIrJrAQBgs0NT6NreR57m1u_D8SpPAAFCCqIZAAA=\"; const addedContact = await graph.users.getById('user@tenant.onmicrosoft.com').contactFolders.getById(folderID).childFolders.getById(subFolderID).contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']); const addedContact2 = await graph.me.contactFolders.getById(folderID).childFolders.getById(subFolderID).contacts.add('Pavel', 'Bansky', [{address: 'pavelb@fabrikam.onmicrosoft.com', name: 'Pavel Bansky' }], ['+1 732 555 0102']);","title":"Add Contact in Child Folder of Contact Folder"},{"location":"v2/graph/directoryobjects/","text":"@pnp/graph/directoryObjects \u00b6 Represents an Azure Active Directory object. The directoryObject type is the base type for many other directory entity types. More information can be found in the official Graph documentation: DirectoryObject Resource Type IDirectoryObject, IDirectoryObjects \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/directory-objects\"; Preset: All import { graph } from \"@pnp/sp/presets/all\"; The groups and directory roles for the user \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" const memberOf = await graph.users.getById('user@tenant.onmicrosoft.com').memberOf(); const memberOf2 = await graph.me.memberOf(); Return all the groups the user, group or directoryObject is a member of. Add true parameter to return only security enabled groups \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/groups\" const memberGroups = await graph.users.getById('user@tenant.onmicrosoft.com').getMemberGroups(); const memberGroups2 = await graph.me.getMemberGroups(); // Returns only security enabled groups const memberGroups3 = await graph.me.getMemberGroups(true); const memberGroups4 = await graph.groups.getById('user@tenant.onmicrosoft.com').getMemberGroups(); Returns all the groups, administrative units and directory roles that a user, group, or directory object is a member of. Add true parameter to return only security enabled groups \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/groups\"; const memberObjects = await graph.users.getById('user@tenant.onmicrosoft.com').getMemberObjects(); const memberObjects2 = await graph.me.getMemberObjects(); // Returns only security enabled groups const memberObjects3 = await graph.me.getMemberObjects(true); const memberObjects4 = await graph.groups.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').getMemberObjects(); Check for membership in a specified list of groups \u00b6 And returns from that list those groups of which the specified user, group, or directory object is a member import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/groups\"; const checkedMembers = await graph.users.getById('user@tenant.onmicrosoft.com').checkMemberGroups([\"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\",\"2001bb09-1d46-40a6-8176-7bb867fb75aa\"]); const checkedMembers2 = await graph.me.checkMemberGroups([\"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\",\"2001bb09-1d46-40a6-8176-7bb867fb75aa\"]); const checkedMembers3 = await graph.groups.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').checkMemberGroups([\"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\",\"2001bb09-1d46-40a6-8176-7bb867fb75aa\"]); Get directoryObject by Id \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/directory-objects\"; const dirObject = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26'); Delete directoryObject \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/directory-objects\"; const deleted = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').delete()","title":"@pnp/graph/directoryObjects"},{"location":"v2/graph/directoryobjects/#pnpgraphdirectoryobjects","text":"Represents an Azure Active Directory object. The directoryObject type is the base type for many other directory entity types. More information can be found in the official Graph documentation: DirectoryObject Resource Type","title":"@pnp/graph/directoryObjects"},{"location":"v2/graph/directoryobjects/#idirectoryobject-idirectoryobjects","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/directory-objects\"; Preset: All import { graph } from \"@pnp/sp/presets/all\";","title":"IDirectoryObject, IDirectoryObjects"},{"location":"v2/graph/directoryobjects/#the-groups-and-directory-roles-for-the-user","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" const memberOf = await graph.users.getById('user@tenant.onmicrosoft.com').memberOf(); const memberOf2 = await graph.me.memberOf();","title":"The groups and directory roles for the user"},{"location":"v2/graph/directoryobjects/#return-all-the-groups-the-user-group-or-directoryobject-is-a-member-of-add-true-parameter-to-return-only-security-enabled-groups","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/groups\" const memberGroups = await graph.users.getById('user@tenant.onmicrosoft.com').getMemberGroups(); const memberGroups2 = await graph.me.getMemberGroups(); // Returns only security enabled groups const memberGroups3 = await graph.me.getMemberGroups(true); const memberGroups4 = await graph.groups.getById('user@tenant.onmicrosoft.com').getMemberGroups();","title":"Return all the groups the user, group or directoryObject is a member of. Add true parameter to return only security enabled groups"},{"location":"v2/graph/directoryobjects/#returns-all-the-groups-administrative-units-and-directory-roles-that-a-user-group-or-directory-object-is-a-member-of-add-true-parameter-to-return-only-security-enabled-groups","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/groups\"; const memberObjects = await graph.users.getById('user@tenant.onmicrosoft.com').getMemberObjects(); const memberObjects2 = await graph.me.getMemberObjects(); // Returns only security enabled groups const memberObjects3 = await graph.me.getMemberObjects(true); const memberObjects4 = await graph.groups.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').getMemberObjects();","title":"Returns all the groups, administrative units and directory roles that a user, group, or directory object is a member of. Add true parameter to return only security enabled groups"},{"location":"v2/graph/directoryobjects/#check-for-membership-in-a-specified-list-of-groups","text":"And returns from that list those groups of which the specified user, group, or directory object is a member import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/groups\"; const checkedMembers = await graph.users.getById('user@tenant.onmicrosoft.com').checkMemberGroups([\"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\",\"2001bb09-1d46-40a6-8176-7bb867fb75aa\"]); const checkedMembers2 = await graph.me.checkMemberGroups([\"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\",\"2001bb09-1d46-40a6-8176-7bb867fb75aa\"]); const checkedMembers3 = await graph.groups.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').checkMemberGroups([\"c2fb52d1-5c60-42b1-8c7e-26ce8dc1e741\",\"2001bb09-1d46-40a6-8176-7bb867fb75aa\"]);","title":"Check for membership in a specified list of groups"},{"location":"v2/graph/directoryobjects/#get-directoryobject-by-id","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/directory-objects\"; const dirObject = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26');","title":"Get directoryObject by Id"},{"location":"v2/graph/directoryobjects/#delete-directoryobject","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/directory-objects\"; const deleted = await graph.directoryObjects.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').delete()","title":"Delete directoryObject"},{"location":"v2/graph/groups/","text":"@pnp/graph/groups \u00b6 Groups are collections of users and other principals who share access to resources in Microsoft services or in your app. All group-related operations in Microsoft Graph require administrator consent. Note: Groups can only be created through work or school accounts. Personal Microsoft accounts don't support groups. You can learn more about Microsoft Graph Groups by reading the Official Microsoft Graph Documentation . IGroup, IGroups \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {Group, GroupType, Groups, IGroup, IGroupAddResult, IGroups} from \"@pnp/graph/groups\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; Preset: All import { graph, Group, GroupType, Groups, IGroup, IGroupAddResult, IGroups } from \"@pnp/graph/presets/all\"; Add a Group \u00b6 Add a new group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; import { GroupType } from '@pnp/graph/groups'; const groupAddResult = await graph.groups.add(\"GroupName\", \"Mail_NickName\", GroupType.Office365); const group = await groupAddResult.group(); Delete a Group \u00b6 Deletes an existing group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").delete(); Update Group Properties \u00b6 Updates an existing group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").update({ displayName: newName, propertyName: updatedValue}); Add favorite \u00b6 Add the group to the list of the current user's favorite groups. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").addFavorite(); Remove favorite \u00b6 Remove the group from the list of the current user's favorite groups. Supported for Office 365 Groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").removeFavorite(); Reset Unseen Count \u00b6 Reset the unseenCount of all the posts that the current user has not seen since their last visit. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").resetUnseenCount(); Subscribe By Mail \u00b6 Calling this method will enable the current user to receive email notifications for this group, about new posts, events, and files in that group. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").subscribeByMail(); Unsubscribe By Mail \u00b6 Calling this method will prevent the current user from receiving email notifications for this group about new posts, events, and files in that group. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").unsubscribeByMail(); Get Calendar View \u00b6 Get the occurrences, exceptions, and single instances of events in a calendar view defined by a time range, from the default calendar of a group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; const startDate = new Date(\"2020-04-01\"); const endDate = new Date(\"2020-03-01\"); const events = graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").getCalendarView(startDate, endDate); Group Photo Operations \u00b6 See Photos Get the Team Site for a Group \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; import \"@pnp/graph/sites/group\"; const teamSite = await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").sites.root(); const url = teamSite.webUrl","title":"@pnp/graph/groups"},{"location":"v2/graph/groups/#pnpgraphgroups","text":"Groups are collections of users and other principals who share access to resources in Microsoft services or in your app. All group-related operations in Microsoft Graph require administrator consent. Note: Groups can only be created through work or school accounts. Personal Microsoft accounts don't support groups. You can learn more about Microsoft Graph Groups by reading the Official Microsoft Graph Documentation .","title":"@pnp/graph/groups"},{"location":"v2/graph/groups/#igroup-igroups","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {Group, GroupType, Groups, IGroup, IGroupAddResult, IGroups} from \"@pnp/graph/groups\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; Preset: All import { graph, Group, GroupType, Groups, IGroup, IGroupAddResult, IGroups } from \"@pnp/graph/presets/all\";","title":"IGroup, IGroups"},{"location":"v2/graph/groups/#add-a-group","text":"Add a new group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; import { GroupType } from '@pnp/graph/groups'; const groupAddResult = await graph.groups.add(\"GroupName\", \"Mail_NickName\", GroupType.Office365); const group = await groupAddResult.group();","title":"Add a Group"},{"location":"v2/graph/groups/#delete-a-group","text":"Deletes an existing group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").delete();","title":"Delete a Group"},{"location":"v2/graph/groups/#update-group-properties","text":"Updates an existing group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").update({ displayName: newName, propertyName: updatedValue});","title":"Update Group Properties"},{"location":"v2/graph/groups/#add-favorite","text":"Add the group to the list of the current user's favorite groups. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").addFavorite();","title":"Add favorite"},{"location":"v2/graph/groups/#remove-favorite","text":"Remove the group from the list of the current user's favorite groups. Supported for Office 365 Groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").removeFavorite();","title":"Remove favorite"},{"location":"v2/graph/groups/#reset-unseen-count","text":"Reset the unseenCount of all the posts that the current user has not seen since their last visit. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").resetUnseenCount();","title":"Reset Unseen Count"},{"location":"v2/graph/groups/#subscribe-by-mail","text":"Calling this method will enable the current user to receive email notifications for this group, about new posts, events, and files in that group. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").subscribeByMail();","title":"Subscribe By Mail"},{"location":"v2/graph/groups/#unsubscribe-by-mail","text":"Calling this method will prevent the current user from receiving email notifications for this group about new posts, events, and files in that group. Supported for Office 365 groups only. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").unsubscribeByMail();","title":"Unsubscribe By Mail"},{"location":"v2/graph/groups/#get-calendar-view","text":"Get the occurrences, exceptions, and single instances of events in a calendar view defined by a time range, from the default calendar of a group. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; const startDate = new Date(\"2020-04-01\"); const endDate = new Date(\"2020-03-01\"); const events = graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").getCalendarView(startDate, endDate);","title":"Get Calendar View"},{"location":"v2/graph/groups/#group-photo-operations","text":"See Photos","title":"Group Photo Operations"},{"location":"v2/graph/groups/#get-the-team-site-for-a-group","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; import \"@pnp/graph/sites/group\"; const teamSite = await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").sites.root(); const url = teamSite.webUrl","title":"Get the Team Site for a Group"},{"location":"v2/graph/insights/","text":"@pnp/graph/insights \u00b6 This module helps you get Insights in form of Trending , Used and Shared . The results are based on relationships calculated using advanced analytics and machine learning techniques. IInsights \u00b6 Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; Preset: All import \"@pnp/graph/presets/all\"; Get all Trending documents \u00b6 Returns documents from OneDrive and SharePoint sites trending around a user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const trending = await graph.me.insights.trending() const trending = await graph.users.getById(\"userId\").insights.trending() Get a Trending document by Id \u00b6 Using the getById method to get a trending document by Id. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const trendingDoc = await graph.me.insights.trending.getById('Id')() const trendingDoc = await graph.users.getById(\"userId\").insights.trending.getById('Id')() Get the resource from Trending document \u00b6 Using the resources method to get the resource from a trending document. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const resource = await graph.me.insights.trending.getById('Id').resource() const resource = await graph.users.getById(\"userId\").insights.trending.getById('Id').resource() Get all Used documents \u00b6 Returns documents viewed and modified by a user. Includes documents the user used in OneDrive for Business, SharePoint, opened as email attachments, and as link attachments from sources like Box, DropBox and Google Drive. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const used = await graph.me.insights.used() const used = await graph.users.getById(\"userId\").insights.used() Get a Used document by Id \u00b6 Using the getById method to get a used document by Id. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const usedDoc = await graph.me.insights.used.getById('Id')() const usedDoc = await graph.users.getById(\"userId\").insights.used.getById('Id')() Get the resource from Used document \u00b6 Using the resources method to get the resource from a used document. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const resource = await graph.me.insights.used.getById('Id').resource() const resource = await graph.users.getById(\"userId\").insights.used.getById('Id').resource() Get all Shared documents \u00b6 Returns documents shared with a user. Documents can be shared as email attachments or as OneDrive for Business links sent in emails. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const shared = await graph.me.insights.shared() const shared = await graph.users.getById(\"userId\").insights.shared() Get a Shared document by Id \u00b6 Using the getById method to get a shared document by Id. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const sharedDoc = await graph.me.insights.shared.getById('Id')() const sharedDoc = await graph.users.getById(\"userId\").insights.shared.getById('Id')() Get the resource from a Shared document \u00b6 Using the resources method to get the resource from a shared document. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const resource = await graph.me.insights.shared.getById('Id').resource() const resource = await graph.users.getById(\"userId\").insights.shared.getById('Id').resource()","title":"@pnp/graph/insights"},{"location":"v2/graph/insights/#pnpgraphinsights","text":"This module helps you get Insights in form of Trending , Used and Shared . The results are based on relationships calculated using advanced analytics and machine learning techniques.","title":"@pnp/graph/insights"},{"location":"v2/graph/insights/#iinsights","text":"Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; Preset: All import \"@pnp/graph/presets/all\";","title":"IInsights"},{"location":"v2/graph/insights/#get-all-trending-documents","text":"Returns documents from OneDrive and SharePoint sites trending around a user. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const trending = await graph.me.insights.trending() const trending = await graph.users.getById(\"userId\").insights.trending()","title":"Get all Trending documents"},{"location":"v2/graph/insights/#get-a-trending-document-by-id","text":"Using the getById method to get a trending document by Id. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const trendingDoc = await graph.me.insights.trending.getById('Id')() const trendingDoc = await graph.users.getById(\"userId\").insights.trending.getById('Id')()","title":"Get a Trending document by Id"},{"location":"v2/graph/insights/#get-the-resource-from-trending-document","text":"Using the resources method to get the resource from a trending document. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const resource = await graph.me.insights.trending.getById('Id').resource() const resource = await graph.users.getById(\"userId\").insights.trending.getById('Id').resource()","title":"Get the resource from Trending document"},{"location":"v2/graph/insights/#get-all-used-documents","text":"Returns documents viewed and modified by a user. Includes documents the user used in OneDrive for Business, SharePoint, opened as email attachments, and as link attachments from sources like Box, DropBox and Google Drive. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const used = await graph.me.insights.used() const used = await graph.users.getById(\"userId\").insights.used()","title":"Get all Used documents"},{"location":"v2/graph/insights/#get-a-used-document-by-id","text":"Using the getById method to get a used document by Id. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const usedDoc = await graph.me.insights.used.getById('Id')() const usedDoc = await graph.users.getById(\"userId\").insights.used.getById('Id')()","title":"Get a Used document by Id"},{"location":"v2/graph/insights/#get-the-resource-from-used-document","text":"Using the resources method to get the resource from a used document. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const resource = await graph.me.insights.used.getById('Id').resource() const resource = await graph.users.getById(\"userId\").insights.used.getById('Id').resource()","title":"Get the resource from Used document"},{"location":"v2/graph/insights/#get-all-shared-documents","text":"Returns documents shared with a user. Documents can be shared as email attachments or as OneDrive for Business links sent in emails. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const shared = await graph.me.insights.shared() const shared = await graph.users.getById(\"userId\").insights.shared()","title":"Get all Shared documents"},{"location":"v2/graph/insights/#get-a-shared-document-by-id","text":"Using the getById method to get a shared document by Id. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const sharedDoc = await graph.me.insights.shared.getById('Id')() const sharedDoc = await graph.users.getById(\"userId\").insights.shared.getById('Id')()","title":"Get a Shared document by Id"},{"location":"v2/graph/insights/#get-the-resource-from-a-shared-document","text":"Using the resources method to get the resource from a shared document. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/insights\"; import \"@pnp/graph/users\"; const resource = await graph.me.insights.shared.getById('Id').resource() const resource = await graph.users.getById(\"userId\").insights.shared.getById('Id').resource()","title":"Get the resource from a Shared document"},{"location":"v2/graph/invitations/","text":"@pnp/graph/invitations \u00b6 The ability invite an external user via the invitation manager IInvitations \u00b6 Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/invitations\"; Preset: All import \"@pnp/graph/presets/all\"; Create Invitation \u00b6 Using the invitations.create() you can create an Invitation. We need the email address of the user being invited and the URL user should be redirected to once the invitation is redeemed (redirect URL). import { graph } from \"@pnp/graph\"; import \"@pnp/graph/invitations\" const invitationResult = await graph.invitations.create('external.user@email-address.com', 'https://tenant.sharepoint.com/sites/redirecturi');","title":"@pnp/graph/invitations"},{"location":"v2/graph/invitations/#pnpgraphinvitations","text":"The ability invite an external user via the invitation manager","title":"@pnp/graph/invitations"},{"location":"v2/graph/invitations/#iinvitations","text":"Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/invitations\"; Preset: All import \"@pnp/graph/presets/all\";","title":"IInvitations"},{"location":"v2/graph/invitations/#create-invitation","text":"Using the invitations.create() you can create an Invitation. We need the email address of the user being invited and the URL user should be redirected to once the invitation is redeemed (redirect URL). import { graph } from \"@pnp/graph\"; import \"@pnp/graph/invitations\" const invitationResult = await graph.invitations.create('external.user@email-address.com', 'https://tenant.sharepoint.com/sites/redirecturi');","title":"Create Invitation"},{"location":"v2/graph/onedrive/","text":"@pnp/graph/onedrive \u00b6 The ability to manage drives and drive items in Onedrive is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described you can manage drives and drive items in Onedrive. IInvitations \u00b6 Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; Preset: All import \"@pnp/graph/presets/all\"; Get the default drive \u00b6 Using the drive() you can get the default drive from Onedrive import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const drives = await graph.users.getById('user@tenant.onmicrosoft.com').drives(); const drives = await graph.me.drives(); Get all of the drives \u00b6 Using the drives() you can get the users available drives from Onedrive import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const drives = await graph.users.getById('user@tenant.onmicrosoft.com').drives(); const drives = await graph.me.drives(); Get drive by Id \u00b6 Using the drives.getById() you can get one of the available drives in Outlook import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const drive = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId'); const drive = await graph.me.drives.getById('driveId'); Get the associated list of a drive \u00b6 Using the list() you get the associated list import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const list = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').list(); const list = await graph.me.drives.getById('driveId').list(); Get the recent files \u00b6 Using the recent() you get the recent files import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const files = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').recent(); const files = await graph.me.drives.getById('driveId').recent(); Get the files shared with me \u00b6 Using the sharedWithMe() you get the files shared with the user import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const shared = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').sharedWithMe(); const shared = await graph.me.drives.getById('driveId').sharedWithMe(); Get the Root folder \u00b6 Using the root() you get the root folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const root = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root(); const root = await graph.me.drives.getById('driveId').root(); Get the Children \u00b6 Using the children() you get the children import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const rootChildren = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root.children(); const rootChildren = await graph.me.drives.getById('driveId').root.children(); const itemChildren = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').children(); const itemChildren = await graph.me.drives.getById('driveId').root.items.getById('itemId').children(); Add folder or item \u00b6 Using the add you can add a folder or an item import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; import { DriveItem as IDriveItem } from \"@microsoft/microsoft-graph-types\"; const addFolder = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root.children.add('New Folder', {folder: {}}); const addFolder = await graph.me.drives.getById('driveId').root.children.add('New Folder', {folder: {}}); Search items \u00b6 Using the search() you can search for items, and optionally select properties import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const search = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId')root.search('queryText')(); const search = await graph.me.drives.getById('driveId')root.search('queryText')(); Get specific item in drive \u00b6 Using the items.getById() you can get a specific item from the current drive import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const item = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId'); const item = await graph.me.drives.getById('driveId').items.getById('itemId'); Get thumbnails \u00b6 Using the thumbnails() you get the thumbnails import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const thumbs = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').thumbnails(); const thumbs = await graph.me.drives.getById('driveId').items.getById('itemId').thumbnails(); Delete drive item \u00b6 Using the delete() you delete the current item import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const thumbs = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').delete(); const thumbs = await graph.me.drives.getById('driveId').items.getById('itemId').delete(); Update drive item \u00b6 Using the update() you update the current item import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const update = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').update({name: \"New Name\"}); const update = await graph.me.drives.getById('driveId').items.getById('itemId').update({name: \"New Name\"}); Move drive item \u00b6 Using the move() you move the current item, and optionally update it import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; // Requires a parentReference to the new folder location const move = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').move({ parentReference: { id: 'itemId'}}, {name: \"New Name\"}); const move = await graph.me.drives.getById('driveId').items.getById('itemId').move({ parentReference: { id: 'itemId'}}, {name: \"New Name\"});","title":"@pnp/graph/onedrive"},{"location":"v2/graph/onedrive/#pnpgraphonedrive","text":"The ability to manage drives and drive items in Onedrive is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described you can manage drives and drive items in Onedrive.","title":"@pnp/graph/onedrive"},{"location":"v2/graph/onedrive/#iinvitations","text":"Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; Preset: All import \"@pnp/graph/presets/all\";","title":"IInvitations"},{"location":"v2/graph/onedrive/#get-the-default-drive","text":"Using the drive() you can get the default drive from Onedrive import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const drives = await graph.users.getById('user@tenant.onmicrosoft.com').drives(); const drives = await graph.me.drives();","title":"Get the default drive"},{"location":"v2/graph/onedrive/#get-all-of-the-drives","text":"Using the drives() you can get the users available drives from Onedrive import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const drives = await graph.users.getById('user@tenant.onmicrosoft.com').drives(); const drives = await graph.me.drives();","title":"Get all of the drives"},{"location":"v2/graph/onedrive/#get-drive-by-id","text":"Using the drives.getById() you can get one of the available drives in Outlook import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const drive = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId'); const drive = await graph.me.drives.getById('driveId');","title":"Get drive by Id"},{"location":"v2/graph/onedrive/#get-the-associated-list-of-a-drive","text":"Using the list() you get the associated list import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const list = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').list(); const list = await graph.me.drives.getById('driveId').list();","title":"Get the associated list of a drive"},{"location":"v2/graph/onedrive/#get-the-recent-files","text":"Using the recent() you get the recent files import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const files = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').recent(); const files = await graph.me.drives.getById('driveId').recent();","title":"Get the recent files"},{"location":"v2/graph/onedrive/#get-the-files-shared-with-me","text":"Using the sharedWithMe() you get the files shared with the user import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const shared = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').sharedWithMe(); const shared = await graph.me.drives.getById('driveId').sharedWithMe();","title":"Get the files shared with me"},{"location":"v2/graph/onedrive/#get-the-root-folder","text":"Using the root() you get the root folder import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const root = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root(); const root = await graph.me.drives.getById('driveId').root();","title":"Get the Root folder"},{"location":"v2/graph/onedrive/#get-the-children","text":"Using the children() you get the children import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const rootChildren = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root.children(); const rootChildren = await graph.me.drives.getById('driveId').root.children(); const itemChildren = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').children(); const itemChildren = await graph.me.drives.getById('driveId').root.items.getById('itemId').children();","title":"Get the Children"},{"location":"v2/graph/onedrive/#add-folder-or-item","text":"Using the add you can add a folder or an item import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; import { DriveItem as IDriveItem } from \"@microsoft/microsoft-graph-types\"; const addFolder = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').root.children.add('New Folder', {folder: {}}); const addFolder = await graph.me.drives.getById('driveId').root.children.add('New Folder', {folder: {}});","title":"Add folder or item"},{"location":"v2/graph/onedrive/#search-items","text":"Using the search() you can search for items, and optionally select properties import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const search = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId')root.search('queryText')(); const search = await graph.me.drives.getById('driveId')root.search('queryText')();","title":"Search items"},{"location":"v2/graph/onedrive/#get-specific-item-in-drive","text":"Using the items.getById() you can get a specific item from the current drive import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const item = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId'); const item = await graph.me.drives.getById('driveId').items.getById('itemId');","title":"Get specific item in drive"},{"location":"v2/graph/onedrive/#get-thumbnails","text":"Using the thumbnails() you get the thumbnails import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const thumbs = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').thumbnails(); const thumbs = await graph.me.drives.getById('driveId').items.getById('itemId').thumbnails();","title":"Get thumbnails"},{"location":"v2/graph/onedrive/#delete-drive-item","text":"Using the delete() you delete the current item import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const thumbs = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').delete(); const thumbs = await graph.me.drives.getById('driveId').items.getById('itemId').delete();","title":"Delete drive item"},{"location":"v2/graph/onedrive/#update-drive-item","text":"Using the update() you update the current item import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; const update = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').update({name: \"New Name\"}); const update = await graph.me.drives.getById('driveId').items.getById('itemId').update({name: \"New Name\"});","title":"Update drive item"},{"location":"v2/graph/onedrive/#move-drive-item","text":"Using the move() you move the current item, and optionally update it import { graph } from \"@pnp/graph\"; import \"@pnp/graph/onedrive\"; // Requires a parentReference to the new folder location const move = await graph.users.getById('user@tenant.onmicrosoft.com').drives.getById('driveId').items.getById('itemId').move({ parentReference: { id: 'itemId'}}, {name: \"New Name\"}); const move = await graph.me.drives.getById('driveId').items.getById('itemId').move({ parentReference: { id: 'itemId'}}, {name: \"New Name\"});","title":"Move drive item"},{"location":"v2/graph/outlook/","text":"@pnp/graph/outlook \u00b6 Represents the Outlook services available to a user. Currently, only interacting with categories is supported. You can learn more by reading the Official Microsoft Graph Documentation . IUsers, IUser, IPeople \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {Outlook, IOutlook, MasterCategories, IMasterCategories, OutlookCategory, IOutlookCategory} from \"@pnp/graph/outlook\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/outlook\"; Preset: All import { graph, Outlook, IOutlook, MasterCategories, IMasterCategories } from \"@pnp/graph/presets/all\"; Get All Categories User \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; // Delegated permissions const categories = await graph.me.outlook.masterCategories(); // Application permissions const categories = await graph.users.getById('{user id}').outlook.masterCategories(); Add Category User \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; // Delegated permissions await graph.me.outlook.masterCategories.add({ displayName: 'Newsletters', color: 'preset2' }); // Application permissions await graph.users.getById('{user id}').outlook.masterCategories.add({ displayName: 'Newsletters', color: 'preset2' }); Update Category \u00b6 Testing has shown that displayName cannot be updated. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; import { OutlookCategory } from \"@microsoft/microsoft-graph-types\"; const categoryUpdate: OutlookCategory = { color: \"preset5\" } // Delegated permissions const categories = await graph.me.outlook.masterCategories.getById('{category id}').update(categoryUpdate); // Application permissions const categories = await graph.users.getById('{user id}').outlook.masterCategories.getById('{category id}').update(categoryUpdate); Delete Category \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; // Delegated permissions const categories = await graph.me.outlook.masterCategories.getById('{category id}').delete(); // Application permissions const categories = await graph.users.getById('{user id}').outlook.masterCategories.getById('{category id}').delete();","title":"@pnp/graph/outlook"},{"location":"v2/graph/outlook/#pnpgraphoutlook","text":"Represents the Outlook services available to a user. Currently, only interacting with categories is supported. You can learn more by reading the Official Microsoft Graph Documentation .","title":"@pnp/graph/outlook"},{"location":"v2/graph/outlook/#iusers-iuser-ipeople","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {Outlook, IOutlook, MasterCategories, IMasterCategories, OutlookCategory, IOutlookCategory} from \"@pnp/graph/outlook\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/outlook\"; Preset: All import { graph, Outlook, IOutlook, MasterCategories, IMasterCategories } from \"@pnp/graph/presets/all\";","title":"IUsers, IUser, IPeople"},{"location":"v2/graph/outlook/#get-all-categories-user","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; // Delegated permissions const categories = await graph.me.outlook.masterCategories(); // Application permissions const categories = await graph.users.getById('{user id}').outlook.masterCategories();","title":"Get All Categories User"},{"location":"v2/graph/outlook/#add-category-user","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; // Delegated permissions await graph.me.outlook.masterCategories.add({ displayName: 'Newsletters', color: 'preset2' }); // Application permissions await graph.users.getById('{user id}').outlook.masterCategories.add({ displayName: 'Newsletters', color: 'preset2' });","title":"Add Category User"},{"location":"v2/graph/outlook/#update-category","text":"Testing has shown that displayName cannot be updated. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; import { OutlookCategory } from \"@microsoft/microsoft-graph-types\"; const categoryUpdate: OutlookCategory = { color: \"preset5\" } // Delegated permissions const categories = await graph.me.outlook.masterCategories.getById('{category id}').update(categoryUpdate); // Application permissions const categories = await graph.users.getById('{user id}').outlook.masterCategories.getById('{category id}').update(categoryUpdate);","title":"Update Category"},{"location":"v2/graph/outlook/#delete-category","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/outlook\"; // Delegated permissions const categories = await graph.me.outlook.masterCategories.getById('{category id}').delete(); // Application permissions const categories = await graph.users.getById('{user id}').outlook.masterCategories.getById('{category id}').delete();","title":"Delete Category"},{"location":"v2/graph/photos/","text":"@pnp/graph/photos \u00b6 A profile photo of a user, group or an Outlook contact accessed from Exchange Online or Azure Active Directory (AAD). It's binary data not encoded in base-64. You can learn more about Microsoft Graph users by reading the Official Microsoft Graph Documentation . IPhoto \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {IPhoto, Photo} from \"@pnp/graph/photos\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/photos\"; Preset: All import { graph, IPhoto, Photo } from \"@pnp/sp/presets/all\"; Current User Photo \u00b6 This example shows the getBlob() endpoint, there is also a getBuffer() endpoint to support node.js import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const photoValue = await graph.me.photo.getBlob(); const url = window.URL || window.webkitURL; const blobUrl = url.createObjectURL(photoValue); document.getElementById(\"photoElement\").setAttribute(\"src\", blobUrl); Current Group Photo \u00b6 This example shows the getBlob() endpoint, there is also a getBuffer() endpoint to support node.js import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; import \"@pnp/graph/photos\"; const photoValue = await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").photo.getBlob(); const url = window.URL || window.webkitURL; const blobUrl = url.createObjectURL(photoValue); document.getElementById(\"photoElement\").setAttribute(\"src\", blobUrl); Set User Photo \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const input = document.getElementById(\"thefileinput\"); const file = input.files[0]; await graph.me.photo.setContent(file); Set Group Photo \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const input = document.getElementById(\"thefileinput\"); const file = input.files[0]; await graph.me.photo.setContent(file);","title":"@pnp/graph/photos"},{"location":"v2/graph/photos/#pnpgraphphotos","text":"A profile photo of a user, group or an Outlook contact accessed from Exchange Online or Azure Active Directory (AAD). It's binary data not encoded in base-64. You can learn more about Microsoft Graph users by reading the Official Microsoft Graph Documentation .","title":"@pnp/graph/photos"},{"location":"v2/graph/photos/#iphoto","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {IPhoto, Photo} from \"@pnp/graph/photos\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/photos\"; Preset: All import { graph, IPhoto, Photo } from \"@pnp/sp/presets/all\";","title":"IPhoto"},{"location":"v2/graph/photos/#current-user-photo","text":"This example shows the getBlob() endpoint, there is also a getBuffer() endpoint to support node.js import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const photoValue = await graph.me.photo.getBlob(); const url = window.URL || window.webkitURL; const blobUrl = url.createObjectURL(photoValue); document.getElementById(\"photoElement\").setAttribute(\"src\", blobUrl);","title":"Current User Photo"},{"location":"v2/graph/photos/#current-group-photo","text":"This example shows the getBlob() endpoint, there is also a getBuffer() endpoint to support node.js import { graph } from \"@pnp/graph\"; import \"@pnp/graph/groups\"; import \"@pnp/graph/photos\"; const photoValue = await graph.groups.getById(\"7d2b9355-0891-47d3-84c8-bf2cd9c62177\").photo.getBlob(); const url = window.URL || window.webkitURL; const blobUrl = url.createObjectURL(photoValue); document.getElementById(\"photoElement\").setAttribute(\"src\", blobUrl);","title":"Current Group Photo"},{"location":"v2/graph/photos/#set-user-photo","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const input = document.getElementById(\"thefileinput\"); const file = input.files[0]; await graph.me.photo.setContent(file);","title":"Set User Photo"},{"location":"v2/graph/photos/#set-group-photo","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const input = document.getElementById(\"thefileinput\"); const file = input.files[0]; await graph.me.photo.setContent(file);","title":"Set Group Photo"},{"location":"v2/graph/planner/","text":"@pnp/graph/planner \u00b6 The ability to manage plans and tasks in Planner is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described you can add, update and delete items in Planner. IInvitations \u00b6 Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\"; Preset: All import \"@pnp/graph/presets/all\"; Get Plans by Id \u00b6 Using the planner.plans.getById() you can get a specific Plan. Planner.plans is not an available endpoint, you need to get a specific Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const plan = await graph.planner.plans.getById('planId')(); Add new Plan \u00b6 Using the planner.plans.add() you can create a new Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const newPlan = await graph.planner.plans.add('groupObjectId', 'title'); Get Tasks in Plan \u00b6 Using the tasks() you can get the Tasks in a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const planTasks = await graph.planner.plans.getById('planId').tasks(); Get Buckets in Plan \u00b6 Using the buckets() you can get the Buckets in a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const planBuckets = await graph.planner.plans.getById('planId').buckets(); Get Details in Plan \u00b6 Using the details() you can get the details in a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const planDetails = await graph.planner.plans.getById('planId').details(); Delete Plan \u00b6 Using the delete() you can get delete a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const delPlan = await graph.planner.plans.getById('planId').delete('planEtag'); Update Plan \u00b6 Using the update() you can get update a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const updPlan = await graph.planner.plans.getById('planId').update({title: 'New Title', eTag: 'planEtag'}); Get Task by Id \u00b6 Using the planner.tasks.getById() you can get a specific Task. Planner.tasks is not an available endpoint, you need to get a specific Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const task = await graph.planner.tasks.getById('taskId')(); Add new Task \u00b6 Using the planner.tasks.add() you can create a new Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const newTask = await graph.planner.tasks.add('planId', 'title'); Get Details in Task \u00b6 Using the details() you can get the details in a Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const taskDetails = await graph.planner.tasks.getById('taskId').details(); Delete Task \u00b6 Using the delete() you can get delete a Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const delTask = await graph.planner.tasks.getById('taskId').delete('taskEtag'); Update Task \u00b6 Using the update() you can get update a Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const updTask = await graph.planner.tasks.getById('taskId').update({properties, eTag:'taskEtag'}); Get Buckets by Id \u00b6 Using the planner.buckets.getById() you can get a specific Bucket. planner.buckets is not an available endpoint, you need to get a specific Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const bucket = await graph.planner.buckets.getById('bucketId')(); Add new Bucket \u00b6 Using the planner.buckets.add() you can create a new Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const newBucket = await graph.planner.buckets.add('name', 'planId'); Update Bucket \u00b6 Using the update() you can get update a Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const updBucket = await graph.planner.buckets.getById('bucketId').update({name: \"Name\", eTag:'bucketEtag'}); Delete Bucket \u00b6 Using the delete() you can get delete a Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const delBucket = await graph.planner.buckets.getById('bucketId').delete(eTag:'bucketEtag'); Get Bucket Tasks \u00b6 Using the tasks() you can get Tasks in a Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const bucketTasks = await graph.planner.buckets.getById('bucketId').tasks();","title":"@pnp/graph/planner"},{"location":"v2/graph/planner/#pnpgraphplanner","text":"The ability to manage plans and tasks in Planner is a capability introduced in version 1.2.4 of @pnp/graph. Through the methods described you can add, update and delete items in Planner.","title":"@pnp/graph/planner"},{"location":"v2/graph/planner/#iinvitations","text":"Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\"; Preset: All import \"@pnp/graph/presets/all\";","title":"IInvitations"},{"location":"v2/graph/planner/#get-plans-by-id","text":"Using the planner.plans.getById() you can get a specific Plan. Planner.plans is not an available endpoint, you need to get a specific Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const plan = await graph.planner.plans.getById('planId')();","title":"Get Plans by Id"},{"location":"v2/graph/planner/#add-new-plan","text":"Using the planner.plans.add() you can create a new Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const newPlan = await graph.planner.plans.add('groupObjectId', 'title');","title":"Add new Plan"},{"location":"v2/graph/planner/#get-tasks-in-plan","text":"Using the tasks() you can get the Tasks in a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const planTasks = await graph.planner.plans.getById('planId').tasks();","title":"Get Tasks in Plan"},{"location":"v2/graph/planner/#get-buckets-in-plan","text":"Using the buckets() you can get the Buckets in a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const planBuckets = await graph.planner.plans.getById('planId').buckets();","title":"Get Buckets in Plan"},{"location":"v2/graph/planner/#get-details-in-plan","text":"Using the details() you can get the details in a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const planDetails = await graph.planner.plans.getById('planId').details();","title":"Get Details in Plan"},{"location":"v2/graph/planner/#delete-plan","text":"Using the delete() you can get delete a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const delPlan = await graph.planner.plans.getById('planId').delete('planEtag');","title":"Delete Plan"},{"location":"v2/graph/planner/#update-plan","text":"Using the update() you can get update a Plan. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const updPlan = await graph.planner.plans.getById('planId').update({title: 'New Title', eTag: 'planEtag'});","title":"Update Plan"},{"location":"v2/graph/planner/#get-task-by-id","text":"Using the planner.tasks.getById() you can get a specific Task. Planner.tasks is not an available endpoint, you need to get a specific Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const task = await graph.planner.tasks.getById('taskId')();","title":"Get Task by Id"},{"location":"v2/graph/planner/#add-new-task","text":"Using the planner.tasks.add() you can create a new Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const newTask = await graph.planner.tasks.add('planId', 'title');","title":"Add new Task"},{"location":"v2/graph/planner/#get-details-in-task","text":"Using the details() you can get the details in a Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const taskDetails = await graph.planner.tasks.getById('taskId').details();","title":"Get Details in Task"},{"location":"v2/graph/planner/#delete-task","text":"Using the delete() you can get delete a Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const delTask = await graph.planner.tasks.getById('taskId').delete('taskEtag');","title":"Delete Task"},{"location":"v2/graph/planner/#update-task","text":"Using the update() you can get update a Task. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const updTask = await graph.planner.tasks.getById('taskId').update({properties, eTag:'taskEtag'});","title":"Update Task"},{"location":"v2/graph/planner/#get-buckets-by-id","text":"Using the planner.buckets.getById() you can get a specific Bucket. planner.buckets is not an available endpoint, you need to get a specific Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const bucket = await graph.planner.buckets.getById('bucketId')();","title":"Get Buckets by Id"},{"location":"v2/graph/planner/#add-new-bucket","text":"Using the planner.buckets.add() you can create a new Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const newBucket = await graph.planner.buckets.add('name', 'planId');","title":"Add new Bucket"},{"location":"v2/graph/planner/#update-bucket","text":"Using the update() you can get update a Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const updBucket = await graph.planner.buckets.getById('bucketId').update({name: \"Name\", eTag:'bucketEtag'});","title":"Update Bucket"},{"location":"v2/graph/planner/#delete-bucket","text":"Using the delete() you can get delete a Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const delBucket = await graph.planner.buckets.getById('bucketId').delete(eTag:'bucketEtag');","title":"Delete Bucket"},{"location":"v2/graph/planner/#get-bucket-tasks","text":"Using the tasks() you can get Tasks in a Bucket. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/planner\" const bucketTasks = await graph.planner.buckets.getById('bucketId').tasks();","title":"Get Bucket Tasks"},{"location":"v2/graph/search/","text":"@pnp/graph/search \u00b6 The search module allows you to access the Microsoft Graph Search API. You can read full details of using the API, for library examples please see below. Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/search\"; Preset: All import \"@pnp/graph/presets/all\"; Call graph.query \u00b6 This example shows calling the search API via the query method of the root graph object. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/search\"; const results = await graph.query({ entityTypes: [\"site\"], query: { queryString: \"test\" }, }); Note: This library allows you to pass multiple search requests to the query method as the value consumed by the server is an array, but it only a single requests works at this time. Eventually this may change and no updates will be required.","title":"@pnp/graph/search"},{"location":"v2/graph/search/#pnpgraphsearch","text":"The search module allows you to access the Microsoft Graph Search API. You can read full details of using the API, for library examples please see below. Scenario Import Statement Selective import { graph } from \"@pnp/graph\"; import \"@pnp/graph/search\"; Preset: All import \"@pnp/graph/presets/all\";","title":"@pnp/graph/search"},{"location":"v2/graph/search/#call-graphquery","text":"This example shows calling the search API via the query method of the root graph object. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/search\"; const results = await graph.query({ entityTypes: [\"site\"], query: { queryString: \"test\" }, }); Note: This library allows you to pass multiple search requests to the query method as the value consumed by the server is an array, but it only a single requests works at this time. Eventually this may change and no updates will be required.","title":"Call graph.query"},{"location":"v2/graph/subscriptions/","text":"@pnp/graph/subscriptions \u00b6 The ability to manage subscriptions is a capability introduced in version 1.2.9 of @pnp/graph. A subscription allows a client app to receive notifications about changes to data in Microsoft Graph. Currently, subscriptions are enabled for the following resources: Mail, events, and contacts from Outlook. Conversations from Office Groups. Drive root items from OneDrive. Users and Groups from Azure Active Directory. Alerts from the Microsoft Graph Security API. Get all of the Subscriptions \u00b6 Using the subscriptions(). If successful this method returns a 200 OK response code and a list of subscription objects in the response body. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const subscriptions = await graph.subscriptions(); Create a new Subscription \u00b6 Using the subscriptions.add(). Creating a subscription requires read scope to the resource. For example, to get notifications messages, your app needs the Mail.Read permission. To learn more about the scopes visit this url. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const addedSubscription = await graph.subscriptions.add(\"created,updated\", \"https://webhook.azurewebsites.net/api/send/myNotifyClient\", \"me/mailFolders('Inbox')/messages\", \"2019-11-20T18:23:45.9356913Z\"); Get Subscription by Id \u00b6 Using the subscriptions.getById() you can get one of the subscriptions import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const subscription = await graph.subscriptions.getById('subscriptionId')(); Delete a Subscription \u00b6 Using the subscriptions.getById().delete() you can remove one of the Subscriptions import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const delSubscription = await graph.subscriptions.getById('subscriptionId').delete(); Update a Subscription \u00b6 Using the subscriptions.getById().update() you can update one of the Subscriptions import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const updSubscription = await graph.subscriptions.getById('subscriptionId').update({changeType: \"created,updated,deleted\" });","title":"@pnp/graph/subscriptions"},{"location":"v2/graph/subscriptions/#pnpgraphsubscriptions","text":"The ability to manage subscriptions is a capability introduced in version 1.2.9 of @pnp/graph. A subscription allows a client app to receive notifications about changes to data in Microsoft Graph. Currently, subscriptions are enabled for the following resources: Mail, events, and contacts from Outlook. Conversations from Office Groups. Drive root items from OneDrive. Users and Groups from Azure Active Directory. Alerts from the Microsoft Graph Security API.","title":"@pnp/graph/subscriptions"},{"location":"v2/graph/subscriptions/#get-all-of-the-subscriptions","text":"Using the subscriptions(). If successful this method returns a 200 OK response code and a list of subscription objects in the response body. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const subscriptions = await graph.subscriptions();","title":"Get all of the Subscriptions"},{"location":"v2/graph/subscriptions/#create-a-new-subscription","text":"Using the subscriptions.add(). Creating a subscription requires read scope to the resource. For example, to get notifications messages, your app needs the Mail.Read permission. To learn more about the scopes visit this url. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const addedSubscription = await graph.subscriptions.add(\"created,updated\", \"https://webhook.azurewebsites.net/api/send/myNotifyClient\", \"me/mailFolders('Inbox')/messages\", \"2019-11-20T18:23:45.9356913Z\");","title":"Create a new Subscription"},{"location":"v2/graph/subscriptions/#get-subscription-by-id","text":"Using the subscriptions.getById() you can get one of the subscriptions import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const subscription = await graph.subscriptions.getById('subscriptionId')();","title":"Get Subscription by Id"},{"location":"v2/graph/subscriptions/#delete-a-subscription","text":"Using the subscriptions.getById().delete() you can remove one of the Subscriptions import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const delSubscription = await graph.subscriptions.getById('subscriptionId').delete();","title":"Delete a Subscription"},{"location":"v2/graph/subscriptions/#update-a-subscription","text":"Using the subscriptions.getById().update() you can update one of the Subscriptions import { graph } from \"@pnp/graph\"; import \"@pnp/graph/subscriptions\" const updSubscription = await graph.subscriptions.getById('subscriptionId').update({changeType: \"created,updated,deleted\" });","title":"Update a Subscription"},{"location":"v2/graph/teams/","text":"@pnp/graph/teams \u00b6 The ability to manage Team is a capability introduced in the 1.2.7 of @pnp/graph. Through the methods described you can add, update and delete items in Teams. Teams the user is a member of \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/teams\" const joinedTeams = await graph.users.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').joinedTeams(); const myJoinedTeams = await graph.me.joinedTeams(); Get Teams by Id \u00b6 Using the teams.getById() you can get a specific Team. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const team = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528')(); Create new Team/Group - Method #1 \u00b6 The first way to create a new Team and corresponding Group is to first create the group and then create the team. Follow the example in Groups to create the group and get the GroupID. Then make a call to create the team from the group. Create a Team via a specific group \u00b6 Here we get the group via id and use createTeam import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" import \"@pnp/graph/groups\" const createdTeam = await graph.groups.getById('679c8ff4-f07d-40de-b02b-60ec332472dd').createTeam({ \"memberSettings\": { \"allowCreateUpdateChannels\": true }, \"messagingSettings\": { \"allowUserEditMessages\": true, \"allowUserDeleteMessages\": true }, \"funSettings\": { \"allowGiphy\": true, \"giphyContentRating\": \"strict\" }}); Create new Team/Group - Method #2 \u00b6 The second way to create a new Team and corresponding Group is to do so in one call. This can be done by using the createTeam method. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const team = { \"template@odata.bind\": \"https://graph.microsoft.com/v1.0/teamsTemplates('standard')\", \"displayName\": \"PnPJS Test Team\", \"description\": \"PnPJS Test Team\u2019s Description\", \"members\": [ { \"@odata.type\": \"#microsoft.graph.aadUserConversationMember\", \"roles\": [\"owner\"], \"user@odata.bind\": \"https://graph.microsoft.com/v1.0/users('{owners user id}')\", }, ], }; const createdTeam: ITeamCreateResultAsync = await graph.teams.create(team); //To check the status of the team creation, call getOperationById for the newly created team. const createdTeamStatus = await graph.teams.getById(createdTeam.teamId).getOperationById(createdTeam.operationId); Clone a Team \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const clonedTeam = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').cloneTeam( 'Cloned','description','apps,tabs,settings,channels,members','public'); Get Teams Async Operation \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const clonedTeam = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').cloneTeam( 'Cloned','description','apps,tabs,settings,channels,members','public'); const clonedTeamStatus = await graph.teams.getById(clonedTeam.teamId).getOperationById(clonedTeam.operationId); Archive a Team \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const archived = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').archive(); Unarchive a Team \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const archived = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').unarchive(); Get all channels of a Team \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const channels = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels(); Get channel by Id \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const channel = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype')(); Create a new Channel \u00b6 import { graph } from \"@pnp/graph\"; const newChannel = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.create('New Channel', 'Description'); Get installed Apps \u00b6 import { graph } from \"@pnp/graph\"; const installedApps = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps(); Add an App \u00b6 import { graph } from \"@pnp/graph\"; const addedApp = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps.add('https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a'); Remove an App \u00b6 import { graph } from \"@pnp/graph\"; const removedApp = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps.remove(); Get Tabs from a Channel \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const tabs = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528'). channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs(); Get Tab by Id \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const tab = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528'). channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs.getById('Id')(); Add a new Tab \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const newTab = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528'). channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs.add('Tab','https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a',{});","title":"@pnp/graph/teams"},{"location":"v2/graph/teams/#pnpgraphteams","text":"The ability to manage Team is a capability introduced in the 1.2.7 of @pnp/graph. Through the methods described you can add, update and delete items in Teams.","title":"@pnp/graph/teams"},{"location":"v2/graph/teams/#teams-the-user-is-a-member-of","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\" import \"@pnp/graph/teams\" const joinedTeams = await graph.users.getById('99dc1039-eb80-43b1-a09e-250d50a80b26').joinedTeams(); const myJoinedTeams = await graph.me.joinedTeams();","title":"Teams the user is a member of"},{"location":"v2/graph/teams/#get-teams-by-id","text":"Using the teams.getById() you can get a specific Team. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const team = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528')();","title":"Get Teams by Id"},{"location":"v2/graph/teams/#create-new-teamgroup-method-1","text":"The first way to create a new Team and corresponding Group is to first create the group and then create the team. Follow the example in Groups to create the group and get the GroupID. Then make a call to create the team from the group.","title":"Create new Team/Group - Method #1"},{"location":"v2/graph/teams/#create-a-team-via-a-specific-group","text":"Here we get the group via id and use createTeam import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" import \"@pnp/graph/groups\" const createdTeam = await graph.groups.getById('679c8ff4-f07d-40de-b02b-60ec332472dd').createTeam({ \"memberSettings\": { \"allowCreateUpdateChannels\": true }, \"messagingSettings\": { \"allowUserEditMessages\": true, \"allowUserDeleteMessages\": true }, \"funSettings\": { \"allowGiphy\": true, \"giphyContentRating\": \"strict\" }});","title":"Create a Team via a specific group"},{"location":"v2/graph/teams/#create-new-teamgroup-method-2","text":"The second way to create a new Team and corresponding Group is to do so in one call. This can be done by using the createTeam method. import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const team = { \"template@odata.bind\": \"https://graph.microsoft.com/v1.0/teamsTemplates('standard')\", \"displayName\": \"PnPJS Test Team\", \"description\": \"PnPJS Test Team\u2019s Description\", \"members\": [ { \"@odata.type\": \"#microsoft.graph.aadUserConversationMember\", \"roles\": [\"owner\"], \"user@odata.bind\": \"https://graph.microsoft.com/v1.0/users('{owners user id}')\", }, ], }; const createdTeam: ITeamCreateResultAsync = await graph.teams.create(team); //To check the status of the team creation, call getOperationById for the newly created team. const createdTeamStatus = await graph.teams.getById(createdTeam.teamId).getOperationById(createdTeam.operationId);","title":"Create new Team/Group - Method #2"},{"location":"v2/graph/teams/#clone-a-team","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const clonedTeam = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').cloneTeam( 'Cloned','description','apps,tabs,settings,channels,members','public');","title":"Clone a Team"},{"location":"v2/graph/teams/#get-teams-async-operation","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const clonedTeam = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').cloneTeam( 'Cloned','description','apps,tabs,settings,channels,members','public'); const clonedTeamStatus = await graph.teams.getById(clonedTeam.teamId).getOperationById(clonedTeam.operationId);","title":"Get Teams Async Operation"},{"location":"v2/graph/teams/#archive-a-team","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const archived = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').archive();","title":"Archive a Team"},{"location":"v2/graph/teams/#unarchive-a-team","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const archived = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').unarchive();","title":"Unarchive a Team"},{"location":"v2/graph/teams/#get-all-channels-of-a-team","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const channels = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels();","title":"Get all channels of a Team"},{"location":"v2/graph/teams/#get-channel-by-id","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const channel = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype')();","title":"Get channel by Id"},{"location":"v2/graph/teams/#create-a-new-channel","text":"import { graph } from \"@pnp/graph\"; const newChannel = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').channels.create('New Channel', 'Description');","title":"Create a new Channel"},{"location":"v2/graph/teams/#get-installed-apps","text":"import { graph } from \"@pnp/graph\"; const installedApps = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps();","title":"Get installed Apps"},{"location":"v2/graph/teams/#add-an-app","text":"import { graph } from \"@pnp/graph\"; const addedApp = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps.add('https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a');","title":"Add an App"},{"location":"v2/graph/teams/#remove-an-app","text":"import { graph } from \"@pnp/graph\"; const removedApp = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528').installedApps.remove();","title":"Remove an App"},{"location":"v2/graph/teams/#get-tabs-from-a-channel","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const tabs = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528'). channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs();","title":"Get Tabs from a Channel"},{"location":"v2/graph/teams/#get-tab-by-id","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const tab = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528'). channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs.getById('Id')();","title":"Get Tab by Id"},{"location":"v2/graph/teams/#add-a-new-tab","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/teams\" const newTab = await graph.teams.getById('3531f3fb-f9ee-4f43-982a-6c90d8226528'). channels.getById('19:65723d632b384ca89c81115c281428a3@thread.skype').tabs.add('Tab','https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/12345678-9abc-def0-123456789a',{});","title":"Add a new Tab"},{"location":"v2/graph/users/","text":"@pnp/graph/users \u00b6 Users are Azure Active Directory objects representing users in the organizations. They represent the single identity for a person across Microsoft 365 services. You can learn more about Microsoft Graph users by reading the Official Microsoft Graph Documentation . IUsers, IUser, IPeople \u00b6 Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {IUser, IUsers, User, Users, IPeople, People} from \"@pnp/graph/users\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; Preset: All import { graph,IUser, IUsers, User, Users, IPeople, People } from \"@pnp/graph/presets/all\"; Current User \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const currentUser = await graph.me(); Get All Users in the Organization \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const allUsers = await graph.users(); Get a User by email address (or user id) \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const matchingUser = await graph.users.getById('jane@contoso.com')(); Update Current User \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; await graph.me.update({ displayName: 'John Doe' }); People \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const people = await graph.me.people(); // get the top 3 people const people = await graph.me.people.top(3)(); People \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const people = await graph.me.people(); // get the top 3 people const people = await graph.me.people.top(3)(); Manager \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const manager = await graph.me.manager(); Direct Reports \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const reports = await graph.me.directReports(); Photo \u00b6 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const currentUser = await graph.me.photo(); const specificUser = await graph.users.getById('jane@contoso.com').photo(); User Photo Operations \u00b6 See Photos","title":"@pnp/graph/users"},{"location":"v2/graph/users/#pnpgraphusers","text":"Users are Azure Active Directory objects representing users in the organizations. They represent the single identity for a person across Microsoft 365 services. You can learn more about Microsoft Graph users by reading the Official Microsoft Graph Documentation .","title":"@pnp/graph/users"},{"location":"v2/graph/users/#iusers-iuser-ipeople","text":"Scenario Import Statement Selective 1 import { graph } from \"@pnp/graph\"; import {IUser, IUsers, User, Users, IPeople, People} from \"@pnp/graph/users\"; Selective 2 import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; Preset: All import { graph,IUser, IUsers, User, Users, IPeople, People } from \"@pnp/graph/presets/all\";","title":"IUsers, IUser, IPeople"},{"location":"v2/graph/users/#current-user","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const currentUser = await graph.me();","title":"Current User"},{"location":"v2/graph/users/#get-all-users-in-the-organization","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const allUsers = await graph.users();","title":"Get All Users in the Organization"},{"location":"v2/graph/users/#get-a-user-by-email-address-or-user-id","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const matchingUser = await graph.users.getById('jane@contoso.com')();","title":"Get a User by email address (or user id)"},{"location":"v2/graph/users/#update-current-user","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; await graph.me.update({ displayName: 'John Doe' });","title":"Update Current User"},{"location":"v2/graph/users/#people","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const people = await graph.me.people(); // get the top 3 people const people = await graph.me.people.top(3)();","title":"People"},{"location":"v2/graph/users/#people_1","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const people = await graph.me.people(); // get the top 3 people const people = await graph.me.people.top(3)();","title":"People"},{"location":"v2/graph/users/#manager","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const manager = await graph.me.manager();","title":"Manager"},{"location":"v2/graph/users/#direct-reports","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; const reports = await graph.me.directReports();","title":"Direct Reports"},{"location":"v2/graph/users/#photo","text":"import { graph } from \"@pnp/graph\"; import \"@pnp/graph/users\"; import \"@pnp/graph/photos\"; const currentUser = await graph.me.photo(); const specificUser = await graph.users.getById('jane@contoso.com').photo();","title":"Photo"},{"location":"v2/graph/users/#user-photo-operations","text":"See Photos","title":"User Photo Operations"},{"location":"v2/logging/","text":"@pnp/logging \u00b6 The logging module provides light weight subscribable and extensible logging framework which is used internally and available for use in your projects. This article outlines how to setup logging and use the various loggers. Getting Started \u00b6 Install the logging module, it has no other dependencies npm install @pnp/logging --save Understanding the Logging Framework \u00b6 The logging framework is centered on the Logger class to which any number of listeners can be subscribed. Each of these listeners will receive each of the messages logged. Each listener must implement the ILogListener interface, shown below. There is only one method to implement and it takes an instance of the LogEntry interface as a parameter. /** * Interface that defines a log listener * */ export interface ILogListener { /** * Any associated data that a given logging listener may choose to log or ignore * * @param entry The information to be logged */ log(entry: ILogEntry): void; } /** * Interface that defines a log entry * */ export interface ILogEntry { /** * The main message to be logged */ message: string; /** * The level of information this message represents */ level: LogLevel; /** * Any associated data that a given logging listener may choose to log or ignore */ data?: any; } Log Levels \u00b6 export const enum LogLevel { Verbose = 0, Info = 1, Warning = 2, Error = 3, Off = 99, } Writing to the Logger \u00b6 To write information to a logger you can use either write, writeJSON, or log. import { Logger, LogLevel } from \"@pnp/logging\"; // write logs a simple string as the message value of the LogEntry Logger.write(\"This is logging a simple string\"); // optionally passing a level, default level is Verbose Logger.write(\"This is logging a simple string\", LogLevel.Error); // this will convert the object to a string using JSON.stringify and set the message with the result Logger.writeJSON({ name: \"value\", name2: \"value2\"}); // optionally passing a level, default level is Verbose Logger.writeJSON({ name: \"value\", name2: \"value2\"}, LogLevel.Warning); // specify the entire LogEntry interface using log Logger.log({ data: { name: \"value\", name2: \"value2\"}, level: LogLevel.Warning, message: \"This is my message\" }); Log an error \u00b6 There exists a shortcut method to log an error to the Logger. This will log an entry to the subscribed loggers where the data property will be the Error instance passed in, the level will be 'Error', and the message will be the Error instance's message property. const e = Error(\"An Error\"); Logger.error(e); Subscribing a Listener \u00b6 By default no listeners are subscribed, so if you would like to get logging information you need to subscribe at least one listener. This is done as shown below by importing the Logger and your listener(s) of choice. Here we are using the provided ConsoleListener. We are also setting the active log level, which controls the level of logging that will be output. Be aware that Verbose produces a substantial amount of data about each request. import { Logger, ConsoleListener, LogLevel } from \"@pnp/logging\"; // subscribe a listener Logger.subscribe(new ConsoleListener()); // set the active log level Logger.activeLogLevel = LogLevel.Info; Available Listeners \u00b6 There are two listeners included in the library, ConsoleListener and FunctionListener. ConsoleListener \u00b6 This listener outputs information to the console and works in Node as well as within browsers. It can be used without settings and writes to the appropriate console method based on message level. For example a LogEntry with level Warning will be written to console.warn. Basic usage is shown in the example above. Configuration Options \u00b6 Although ConsoleListener can be used without configuration, there are some additional options available to you. ConsoleListener supports adding a prefix to every output (helpful for filtering console messages) and specifying text color for messages (including by LogLevel). Using a Prefix \u00b6 To add a prefix to all output, supply a string in the constructor: import { Logger, ConsoleListener, LogLevel } from \"@pnp/logging\"; const LOG_SOURCE: string = 'MyAwesomeWebPart'; Logger.subscribe(new ConsoleListener(LOG_SOURCE)); Logger.activeLogLevel = LogLevel.Info; With the above configuration, Logger.write(\"My special message\"); will be output to the console as: MyAwesomeWebPart - My special message Customizing Text Color \u00b6 You can also specify text color for your messages by supplying an IConsoleListenerColors object. You can simply specify color to set the default color for all logging levels or you can set one or more logging level specific text colors (if you only want to set color for a specific logging level(s), leave color out and all other log levels will use the default color). Colors can be specified the same way color values are specified in CSS (named colors, hex values, rgb, rgba, hsl, hsla, etc.): import { Logger, ConsoleListener, LogLevel } from \"@pnp/logging\"; const LOG_SOURCE: string = 'MyAwesomeWebPart'; Logger.subscribe(new ConsoleListener(LOG_SOURCE, {color:'#0b6a0b',warningColor:'magenta'})); Logger.activeLogLevel = LogLevel.Info; With the above configuration: Logger.write(\"My special message\"); Logger.write(\"A warning!\", LogLevel.Warning); Will result in messages that look like this: Color options: color : Default text color for all logging levels unless they're specified verboseColor : Text color to use for messages with LogLevel.Verbose infoColor : Text color to use for messages with LogLevel.Info warningColor : Text color to use for messages with LogLevel.Warning errorColor : Text color to use for messages with LogLevel.Error To set colors without a prefix, specify either undefined or an empty string for the first parameter: Logger.subscribe(new ConsoleListener(undefined, {color:'purple'})); FunctionListener \u00b6 The FunctionListener allows you to wrap any functionality by creating a function that takes a LogEntry as its single argument. This produces the same result as implementing the LogListener interface, but is useful if you already have a logging method or framework to which you want to pass the messages. import { Logger, FunctionListener, ILogEntry } from \"@pnp/logging\"; let listener = new FunctionListener((entry: ILogEntry) => { // pass all logging data to an existing framework MyExistingCompanyLoggingFramework.log(entry.message); }); Logger.subscribe(listener); Create a Custom Listener \u00b6 If desirable for your project you can create a custom listener to perform any logging action you would like. This is done by implementing the ILogListener interface. import { Logger, ILogListener, ILogEntry } from \"@pnp/logging\"; class MyListener implements ILogListener { log(entry: ILogEntry): void { // here you would do something with the entry } } Logger.subscribe(new MyListener());","title":"@pnp/logging"},{"location":"v2/logging/#pnplogging","text":"The logging module provides light weight subscribable and extensible logging framework which is used internally and available for use in your projects. This article outlines how to setup logging and use the various loggers.","title":"@pnp/logging"},{"location":"v2/logging/#getting-started","text":"Install the logging module, it has no other dependencies npm install @pnp/logging --save","title":"Getting Started"},{"location":"v2/logging/#understanding-the-logging-framework","text":"The logging framework is centered on the Logger class to which any number of listeners can be subscribed. Each of these listeners will receive each of the messages logged. Each listener must implement the ILogListener interface, shown below. There is only one method to implement and it takes an instance of the LogEntry interface as a parameter. /** * Interface that defines a log listener * */ export interface ILogListener { /** * Any associated data that a given logging listener may choose to log or ignore * * @param entry The information to be logged */ log(entry: ILogEntry): void; } /** * Interface that defines a log entry * */ export interface ILogEntry { /** * The main message to be logged */ message: string; /** * The level of information this message represents */ level: LogLevel; /** * Any associated data that a given logging listener may choose to log or ignore */ data?: any; }","title":"Understanding the Logging Framework"},{"location":"v2/logging/#log-levels","text":"export const enum LogLevel { Verbose = 0, Info = 1, Warning = 2, Error = 3, Off = 99, }","title":"Log Levels"},{"location":"v2/logging/#writing-to-the-logger","text":"To write information to a logger you can use either write, writeJSON, or log. import { Logger, LogLevel } from \"@pnp/logging\"; // write logs a simple string as the message value of the LogEntry Logger.write(\"This is logging a simple string\"); // optionally passing a level, default level is Verbose Logger.write(\"This is logging a simple string\", LogLevel.Error); // this will convert the object to a string using JSON.stringify and set the message with the result Logger.writeJSON({ name: \"value\", name2: \"value2\"}); // optionally passing a level, default level is Verbose Logger.writeJSON({ name: \"value\", name2: \"value2\"}, LogLevel.Warning); // specify the entire LogEntry interface using log Logger.log({ data: { name: \"value\", name2: \"value2\"}, level: LogLevel.Warning, message: \"This is my message\" });","title":"Writing to the Logger"},{"location":"v2/logging/#log-an-error","text":"There exists a shortcut method to log an error to the Logger. This will log an entry to the subscribed loggers where the data property will be the Error instance passed in, the level will be 'Error', and the message will be the Error instance's message property. const e = Error(\"An Error\"); Logger.error(e);","title":"Log an error"},{"location":"v2/logging/#subscribing-a-listener","text":"By default no listeners are subscribed, so if you would like to get logging information you need to subscribe at least one listener. This is done as shown below by importing the Logger and your listener(s) of choice. Here we are using the provided ConsoleListener. We are also setting the active log level, which controls the level of logging that will be output. Be aware that Verbose produces a substantial amount of data about each request. import { Logger, ConsoleListener, LogLevel } from \"@pnp/logging\"; // subscribe a listener Logger.subscribe(new ConsoleListener()); // set the active log level Logger.activeLogLevel = LogLevel.Info;","title":"Subscribing a Listener"},{"location":"v2/logging/#available-listeners","text":"There are two listeners included in the library, ConsoleListener and FunctionListener.","title":"Available Listeners"},{"location":"v2/logging/#consolelistener","text":"This listener outputs information to the console and works in Node as well as within browsers. It can be used without settings and writes to the appropriate console method based on message level. For example a LogEntry with level Warning will be written to console.warn. Basic usage is shown in the example above.","title":"ConsoleListener"},{"location":"v2/logging/#configuration-options","text":"Although ConsoleListener can be used without configuration, there are some additional options available to you. ConsoleListener supports adding a prefix to every output (helpful for filtering console messages) and specifying text color for messages (including by LogLevel).","title":"Configuration Options"},{"location":"v2/logging/#using-a-prefix","text":"To add a prefix to all output, supply a string in the constructor: import { Logger, ConsoleListener, LogLevel } from \"@pnp/logging\"; const LOG_SOURCE: string = 'MyAwesomeWebPart'; Logger.subscribe(new ConsoleListener(LOG_SOURCE)); Logger.activeLogLevel = LogLevel.Info; With the above configuration, Logger.write(\"My special message\"); will be output to the console as: MyAwesomeWebPart - My special message","title":"Using a Prefix"},{"location":"v2/logging/#customizing-text-color","text":"You can also specify text color for your messages by supplying an IConsoleListenerColors object. You can simply specify color to set the default color for all logging levels or you can set one or more logging level specific text colors (if you only want to set color for a specific logging level(s), leave color out and all other log levels will use the default color). Colors can be specified the same way color values are specified in CSS (named colors, hex values, rgb, rgba, hsl, hsla, etc.): import { Logger, ConsoleListener, LogLevel } from \"@pnp/logging\"; const LOG_SOURCE: string = 'MyAwesomeWebPart'; Logger.subscribe(new ConsoleListener(LOG_SOURCE, {color:'#0b6a0b',warningColor:'magenta'})); Logger.activeLogLevel = LogLevel.Info; With the above configuration: Logger.write(\"My special message\"); Logger.write(\"A warning!\", LogLevel.Warning); Will result in messages that look like this: Color options: color : Default text color for all logging levels unless they're specified verboseColor : Text color to use for messages with LogLevel.Verbose infoColor : Text color to use for messages with LogLevel.Info warningColor : Text color to use for messages with LogLevel.Warning errorColor : Text color to use for messages with LogLevel.Error To set colors without a prefix, specify either undefined or an empty string for the first parameter: Logger.subscribe(new ConsoleListener(undefined, {color:'purple'}));","title":"Customizing Text Color"},{"location":"v2/logging/#functionlistener","text":"The FunctionListener allows you to wrap any functionality by creating a function that takes a LogEntry as its single argument. This produces the same result as implementing the LogListener interface, but is useful if you already have a logging method or framework to which you want to pass the messages. import { Logger, FunctionListener, ILogEntry } from \"@pnp/logging\"; let listener = new FunctionListener((entry: ILogEntry) => { // pass all logging data to an existing framework MyExistingCompanyLoggingFramework.log(entry.message); }); Logger.subscribe(listener);","title":"FunctionListener"},{"location":"v2/logging/#create-a-custom-listener","text":"If desirable for your project you can create a custom listener to perform any logging action you would like. This is done by implementing the ILogListener interface. import { Logger, ILogListener, ILogEntry } from \"@pnp/logging\"; class MyListener implements ILogListener { log(entry: ILogEntry): void { // here you would do something with the entry } } Logger.subscribe(new MyListener());","title":"Create a Custom Listener"},{"location":"v2/news/2020-year-in-review/","text":"2020 Year End Report \u00b6 Welcome to our first year in review report for PnPjs. This year has marked usage milestones, seen more contributors than ever, and expanded the core maintainers team. But none of this would be possible without everyones support and participation - so we start by saying Thank You! We deeply appreciate everyone that has used, helped us grow, and improved the library over the last year. This year we introduced MSAL clients for node and browser, improved our testing/local development plumbing, and updated the libraries to work with the node 15 module resolution rules. We fixed 43 reported bugs, answered 131 questions, and made 55 suggested enhancements to the library - all driven by feedback from users and the community. Planned for release in January 2021 we also undertook the work to enable isolated runtimes, a long requested feature. This allows you to operate on multiple independently configured \"roots\" such as \"sp\" or \"graph\" from the same application. Previously the library was configured globally, so this opens new possibilities for both client and server side scenarios. Finally we made many tooling and project improvements such as moving to GitHub actions, updating the tests to use MSAL, and exploring ways to enhance the developer experience. Usage \u00b6 In 2020 we tracked steady month/month growth in raw usage measured by requests as well as in the number of tenants deploying the library. Starting the year we were used in 14605 tenants and by December that number grew to 21,227. These tenants generated 6.1 billion requests to the service in January growing to 9.2 billion by December, peaking at 10.1 billion requests in November. 1) There was a data glitch in October so the numbers do not fully represent usage. 2) These numbers only include public cloud SPO usage, true usage is higher than we can track due to on-premesis and gov/sovereign clouds Releases \u00b6 We continued our monthly release cadence as it represents a good pace for addressing issues while not expecting folks to update too often and keeping each update to a reasonable size. All changes can be tracked in our change log , updated with each release. You can check our scheduled releases through project milestones , understanding there are occasionally delays. Monthly releases allows us to ensure bugs do not linger and we continually improve and expand the capabilities of the libraries. NPM Package download statistics (@pnp/sp): \u00b6 Month Count * Month Count January 100,686 * July 36,805 February 34,437 * August 38,897 March 34,574 * September 45,968 April 32,436 * October 46,655 May 34,482 * November 45,511 June 34,408 * December 58,977 Grand Total 543,836 With 2020 our total all time downloads of @pnp/sp is now at: 949,638 Stats from https://npm-stat.com/ Future Plans \u00b6 Looking to the future we will continue to actively grow and improve v2 of the library, guided by feedback and reported issues. Additionally, we are beginning to discuss v3 and doing initial planning and prototyping. The v3 work will continue through 2021 with no currently set release date, though we will keep everyone up to date. Additionally in 2021 there will be a general focus on improving not just the code but our tooling, build pipeline, and library contributor experience. We will also look at automatic canary releases with each merge, and other improvements. New Lead Maintainer \u00b6 With the close of 2020 we are very excited to announce a new lead maintainer for PnPjs, Julie Turner ! Julie brings deep expertise with SharePoint Framework, TypeScript, and SharePoint development to the team, coupled with dedication and care in the work. Over the last year she has gotten more involved with handling releases, responding to issues, and helping to keep the code updated and clean. We are very lucky to have her working on the project and look forward to seeing her lead the growth and direction for years to come. Contributors \u00b6 As always we have abundant thanks and appreciation for your contributors. Taking your time to help improve PnPjs for the community is massive and valuable to ensure our sustainability. Thank you for all your help in 2020! If you are interested in becoming a contributor check out our guide on ways to get started. Sponsors \u00b6 We want to thank our sponsors for their support in 2020! This year we put the money towards helping offset the cost and shipping of hoodies to contributors and sponsors. Your continued generosity makes a big difference in our ability to recognize and reward the folks building PnPjs. Thank You Closing \u00b6 In closing we want say Thank You to everyone who uses, contributes to, and participates in PnPjs and the SharePoint Patterns and Practices program. Wishing you the very best for 2021, The PnPjs Team","title":"2020 Year End Report"},{"location":"v2/news/2020-year-in-review/#2020-year-end-report","text":"Welcome to our first year in review report for PnPjs. This year has marked usage milestones, seen more contributors than ever, and expanded the core maintainers team. But none of this would be possible without everyones support and participation - so we start by saying Thank You! We deeply appreciate everyone that has used, helped us grow, and improved the library over the last year. This year we introduced MSAL clients for node and browser, improved our testing/local development plumbing, and updated the libraries to work with the node 15 module resolution rules. We fixed 43 reported bugs, answered 131 questions, and made 55 suggested enhancements to the library - all driven by feedback from users and the community. Planned for release in January 2021 we also undertook the work to enable isolated runtimes, a long requested feature. This allows you to operate on multiple independently configured \"roots\" such as \"sp\" or \"graph\" from the same application. Previously the library was configured globally, so this opens new possibilities for both client and server side scenarios. Finally we made many tooling and project improvements such as moving to GitHub actions, updating the tests to use MSAL, and exploring ways to enhance the developer experience.","title":"2020 Year End Report"},{"location":"v2/news/2020-year-in-review/#usage","text":"In 2020 we tracked steady month/month growth in raw usage measured by requests as well as in the number of tenants deploying the library. Starting the year we were used in 14605 tenants and by December that number grew to 21,227. These tenants generated 6.1 billion requests to the service in January growing to 9.2 billion by December, peaking at 10.1 billion requests in November. 1) There was a data glitch in October so the numbers do not fully represent usage. 2) These numbers only include public cloud SPO usage, true usage is higher than we can track due to on-premesis and gov/sovereign clouds","title":"Usage"},{"location":"v2/news/2020-year-in-review/#releases","text":"We continued our monthly release cadence as it represents a good pace for addressing issues while not expecting folks to update too often and keeping each update to a reasonable size. All changes can be tracked in our change log , updated with each release. You can check our scheduled releases through project milestones , understanding there are occasionally delays. Monthly releases allows us to ensure bugs do not linger and we continually improve and expand the capabilities of the libraries.","title":"Releases"},{"location":"v2/news/2020-year-in-review/#npm-package-download-statistics-pnpsp","text":"Month Count * Month Count January 100,686 * July 36,805 February 34,437 * August 38,897 March 34,574 * September 45,968 April 32,436 * October 46,655 May 34,482 * November 45,511 June 34,408 * December 58,977 Grand Total 543,836 With 2020 our total all time downloads of @pnp/sp is now at: 949,638 Stats from https://npm-stat.com/","title":"NPM Package download statistics (@pnp/sp):"},{"location":"v2/news/2020-year-in-review/#future-plans","text":"Looking to the future we will continue to actively grow and improve v2 of the library, guided by feedback and reported issues. Additionally, we are beginning to discuss v3 and doing initial planning and prototyping. The v3 work will continue through 2021 with no currently set release date, though we will keep everyone up to date. Additionally in 2021 there will be a general focus on improving not just the code but our tooling, build pipeline, and library contributor experience. We will also look at automatic canary releases with each merge, and other improvements.","title":"Future Plans"},{"location":"v2/news/2020-year-in-review/#new-lead-maintainer","text":"With the close of 2020 we are very excited to announce a new lead maintainer for PnPjs, Julie Turner ! Julie brings deep expertise with SharePoint Framework, TypeScript, and SharePoint development to the team, coupled with dedication and care in the work. Over the last year she has gotten more involved with handling releases, responding to issues, and helping to keep the code updated and clean. We are very lucky to have her working on the project and look forward to seeing her lead the growth and direction for years to come.","title":"New Lead Maintainer"},{"location":"v2/news/2020-year-in-review/#contributors","text":"As always we have abundant thanks and appreciation for your contributors. Taking your time to help improve PnPjs for the community is massive and valuable to ensure our sustainability. Thank you for all your help in 2020! If you are interested in becoming a contributor check out our guide on ways to get started.","title":"Contributors"},{"location":"v2/news/2020-year-in-review/#sponsors","text":"We want to thank our sponsors for their support in 2020! This year we put the money towards helping offset the cost and shipping of hoodies to contributors and sponsors. Your continued generosity makes a big difference in our ability to recognize and reward the folks building PnPjs. Thank You","title":"Sponsors"},{"location":"v2/news/2020-year-in-review/#closing","text":"In closing we want say Thank You to everyone who uses, contributes to, and participates in PnPjs and the SharePoint Patterns and Practices program. Wishing you the very best for 2021, The PnPjs Team","title":"Closing"},{"location":"v2/nodejs/","text":"@pnp/nodejs \u00b6 This package supplies helper code when using the @pnp libraries within the context of nodejs. Primarily these consist of clients to enable use of the libraries in nodejs. Getting Started \u00b6 Install the library and required dependencies. You will also need to install other libraries such as @pnp/sp or @pnp/graph to use the exported functionality. npm install @pnp/sp @pnp/nodejs --save AdalFetchClient SPFetchClient BearerTokenFetchClient Proxy SP Extensions \u00b6 Added in 2.0.9 A set of nodejs specific extensions for the @pnp/sp library. SP Extensions","title":"@pnp/nodejs"},{"location":"v2/nodejs/#pnpnodejs","text":"This package supplies helper code when using the @pnp libraries within the context of nodejs. Primarily these consist of clients to enable use of the libraries in nodejs.","title":"@pnp/nodejs"},{"location":"v2/nodejs/#getting-started","text":"Install the library and required dependencies. You will also need to install other libraries such as @pnp/sp or @pnp/graph to use the exported functionality. npm install @pnp/sp @pnp/nodejs --save AdalFetchClient SPFetchClient BearerTokenFetchClient Proxy","title":"Getting Started"},{"location":"v2/nodejs/#sp-extensions","text":"Added in 2.0.9 A set of nodejs specific extensions for the @pnp/sp library. SP Extensions","title":"SP Extensions"},{"location":"v2/nodejs/adal-fetch-client/","text":"@pnp/nodejs/adalfetchclient \u00b6 The AdalFetchClient class depends on the adal-node package to authenticate against Azure AD. The example below outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected. import { AdalFetchClient } from \"@pnp/nodejs\"; import { graph } from \"@pnp/graph/presets/all\"; // setup the client using graph setup function graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{tenant}\", \"{app id}\", \"{app secret}\"); }, }, }); // execute a library request as normal const g = await graph.groups(); console.log(JSON.stringify(g, null, 4));","title":"@pnp/nodejs/adalfetchclient"},{"location":"v2/nodejs/adal-fetch-client/#pnpnodejsadalfetchclient","text":"The AdalFetchClient class depends on the adal-node package to authenticate against Azure AD. The example below outlines usage with the @pnp/graph library, though it would work in any case where an Azure AD Bearer token is expected. import { AdalFetchClient } from \"@pnp/nodejs\"; import { graph } from \"@pnp/graph/presets/all\"; // setup the client using graph setup function graph.setup({ graph: { fetchClientFactory: () => { return new AdalFetchClient(\"{tenant}\", \"{app id}\", \"{app secret}\"); }, }, }); // execute a library request as normal const g = await graph.groups(); console.log(JSON.stringify(g, null, 4));","title":"@pnp/nodejs/adalfetchclient"},{"location":"v2/nodejs/bearer-token-fetch-client/","text":"@pnp/nodejs/BearerTokenFetchClient \u00b6 The BearerTokenFetchClient class allows you to easily specify your own Bearer tokens to be used in the requests. How you derive the token is up to you. import { BearerTokenFetchClient } from \"@pnp/nodejs\"; import { graph } from \"@pnp/graph/presets/all\"; // setup the client using graph setup function graph.setup({ graph: { fetchClientFactory: () => { return new BearerTokenFetchClient(\"{Bearer Token}\"); }, }, }); // execute a library request as normal const g = await graph.groups(); console.log(JSON.stringify(g, null, 4));","title":"@pnp/nodejs/BearerTokenFetchClient"},{"location":"v2/nodejs/bearer-token-fetch-client/#pnpnodejsbearertokenfetchclient","text":"The BearerTokenFetchClient class allows you to easily specify your own Bearer tokens to be used in the requests. How you derive the token is up to you. import { BearerTokenFetchClient } from \"@pnp/nodejs\"; import { graph } from \"@pnp/graph/presets/all\"; // setup the client using graph setup function graph.setup({ graph: { fetchClientFactory: () => { return new BearerTokenFetchClient(\"{Bearer Token}\"); }, }, }); // execute a library request as normal const g = await graph.groups(); console.log(JSON.stringify(g, null, 4));","title":"@pnp/nodejs/BearerTokenFetchClient"},{"location":"v2/nodejs/provider-hosted-app/","text":"@pnp/nodejs/providerhostedrequestcontext \u00b6 The ProviderHostedRequestContext enables the creation of provider-hosted add-ins built in node.js to use pnpjs to interact with SharePoint. The context is associated to a SharePoint user, allowing requests to be made by the add-in on the behalf of the user. The usage of this class assumes the provider-hosted add-in is called from SharePoint with a valid SPAppToken. This is typically done by means of accessing /_layouts/15/AppRedirect.aspx with the app's client ID and app's redirect URI. Note : To support concurrent requests by different users and/or add-ins on different tenants, do not use the SPFetchClient class. Instead, use the more generic NodeFetchClient class. The downside is that you have to manually configure each request to use the desired user/app context. import { sp, SPRest } from \"@pnp/sp/presets/all\"; import { NodeFetchClient, ProviderHostedRequestContext } from \"@pnp/nodejs\"; // configure your node options sp.setup({ sp: { fetchClientFactory: () => { return new NodeFetchClient(); }, }, }); // get request data generated by /_layouts/15/AppRedirect.aspx const spAppToken = request.body.SPAppToken; const spSiteUrl = request.body.SPSiteUrl; // create a context based on the add-in details and SPAppToken const ctx = await ProviderHostedRequestContext.create(spSiteUrl, \"{client id}\", \"{client secret}\", spAppToken); // create an SPRest object configured to use our context // this is used in place of the global sp object const userSP = new SPRest().configure(await ctx.getUserConfig(), spSiteUrl); const addinSP = new SPRest().configure(await ctx.getAddInOnlyConfig(), spSiteUrl); // make a request on behalf of the user const user = await userSP.web.currentUser(); console.log(`Hello ${user.Title}`); // make an add-in only request const app = await addinSP.web.currentUser(); console.log(`Add-in principal: ${app.Title}`);","title":"@pnp/nodejs/providerhostedrequestcontext"},{"location":"v2/nodejs/provider-hosted-app/#pnpnodejsproviderhostedrequestcontext","text":"The ProviderHostedRequestContext enables the creation of provider-hosted add-ins built in node.js to use pnpjs to interact with SharePoint. The context is associated to a SharePoint user, allowing requests to be made by the add-in on the behalf of the user. The usage of this class assumes the provider-hosted add-in is called from SharePoint with a valid SPAppToken. This is typically done by means of accessing /_layouts/15/AppRedirect.aspx with the app's client ID and app's redirect URI. Note : To support concurrent requests by different users and/or add-ins on different tenants, do not use the SPFetchClient class. Instead, use the more generic NodeFetchClient class. The downside is that you have to manually configure each request to use the desired user/app context. import { sp, SPRest } from \"@pnp/sp/presets/all\"; import { NodeFetchClient, ProviderHostedRequestContext } from \"@pnp/nodejs\"; // configure your node options sp.setup({ sp: { fetchClientFactory: () => { return new NodeFetchClient(); }, }, }); // get request data generated by /_layouts/15/AppRedirect.aspx const spAppToken = request.body.SPAppToken; const spSiteUrl = request.body.SPSiteUrl; // create a context based on the add-in details and SPAppToken const ctx = await ProviderHostedRequestContext.create(spSiteUrl, \"{client id}\", \"{client secret}\", spAppToken); // create an SPRest object configured to use our context // this is used in place of the global sp object const userSP = new SPRest().configure(await ctx.getUserConfig(), spSiteUrl); const addinSP = new SPRest().configure(await ctx.getAddInOnlyConfig(), spSiteUrl); // make a request on behalf of the user const user = await userSP.web.currentUser(); console.log(`Hello ${user.Title}`); // make an add-in only request const app = await addinSP.web.currentUser(); console.log(`Add-in principal: ${app.Title}`);","title":"@pnp/nodejs/providerhostedrequestcontext"},{"location":"v2/nodejs/proxy/","text":"@pnp/nodejs/proxy \u00b6 In some cases when deploying on node you may need to use a proxy as governed by corporate policy, or perhaps you want to examine the traffic using a tool such as Fiddler. setProxyUrl \u00b6 Basic Usage \u00b6 You need to import the setProxyUrl function from @pnp/nodejs library and call it with your proxy url. Once done an https-proxy-agent will be used with each request. This works across all clients within the @pnp/nodejs library. import { SPFetchClient, SPOAuthEnv, setProxyUrl } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { // call the set proxy url function and it will be used for all requests regardless of client setProxyUrl(\"{your proxy url}\"); return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO); }, }, }); Use with Fiddler \u00b6 To get Fiddler to work you may need to set an environment variable. This should only be done for testing! import { SPFetchClient, SPOAuthEnv, setProxyUrl } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { // ignore certificate errors: ONLY FOR TESTING!! process.env.NODE_TLS_REJECT_UNAUTHORIZED = \"0\"; // this is my fiddler url locally setProxyUrl(\"http://127.0.0.1:8888\"); return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO); }, }, }); setProxyAgent \u00b6 Added in 2.0.11 You need to import the setProxyAgent function from @pnp/nodejs library and call it with your proxy url. You can supply any valid proxy and it will be used. import { SPFetchClient, SPOAuthEnv, setProxyAgent } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { const myAgent = new MyAgentOfSomeType({}); // call the set proxy agent function and it will be used for all requests regardless of client setProxyAgent(myAgent); return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO); }, }, });","title":"@pnp/nodejs/proxy"},{"location":"v2/nodejs/proxy/#pnpnodejsproxy","text":"In some cases when deploying on node you may need to use a proxy as governed by corporate policy, or perhaps you want to examine the traffic using a tool such as Fiddler.","title":"@pnp/nodejs/proxy"},{"location":"v2/nodejs/proxy/#setproxyurl","text":"","title":"setProxyUrl"},{"location":"v2/nodejs/proxy/#basic-usage","text":"You need to import the setProxyUrl function from @pnp/nodejs library and call it with your proxy url. Once done an https-proxy-agent will be used with each request. This works across all clients within the @pnp/nodejs library. import { SPFetchClient, SPOAuthEnv, setProxyUrl } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { // call the set proxy url function and it will be used for all requests regardless of client setProxyUrl(\"{your proxy url}\"); return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO); }, }, });","title":"Basic Usage"},{"location":"v2/nodejs/proxy/#use-with-fiddler","text":"To get Fiddler to work you may need to set an environment variable. This should only be done for testing! import { SPFetchClient, SPOAuthEnv, setProxyUrl } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { // ignore certificate errors: ONLY FOR TESTING!! process.env.NODE_TLS_REJECT_UNAUTHORIZED = \"0\"; // this is my fiddler url locally setProxyUrl(\"http://127.0.0.1:8888\"); return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO); }, }, });","title":"Use with Fiddler"},{"location":"v2/nodejs/proxy/#setproxyagent","text":"Added in 2.0.11 You need to import the setProxyAgent function from @pnp/nodejs library and call it with your proxy url. You can supply any valid proxy and it will be used. import { SPFetchClient, SPOAuthEnv, setProxyAgent } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { const myAgent = new MyAgentOfSomeType({}); // call the set proxy agent function and it will be used for all requests regardless of client setProxyAgent(myAgent); return new SPFetchClient(settings.testing.sp.url, settings.testing.sp.id, settings.testing.sp.secret, SPOAuthEnv.SPO); }, }, });","title":"setProxyAgent"},{"location":"v2/nodejs/sp-extensions/","text":"@pnp/nodejs - sp extensions \u00b6 By importing anything from the @pnp/nodejs library you automatically get nodejs specific extension methods added into the sp fluent api. This article describes them. These examples use the *-commonjs version of the libraries as they target node, you can read more about the differences . IFile.getStream \u00b6 Allows you to read a response body as a nodejs PassThrough stream. // by importing the the library the node specific extensions are automatically applied import { SPFetchClient, SPNS } from \"@pnp/nodejs-commonjs\"; import { sp } from \"@pnp/sp-commonjs\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{url}\", \"{id}\", \"{secret}\"); }, }, }); // get the stream const streamResult: SPNS.IResponseBodyStream = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/file.txt\").getStream(); // see if we have a known length console.log(streamResult.knownLength); // read the stream // this is a very basic example - you can do tons more with streams in node const txt = await new Promise((resolve) => { let data = \"\"; stream.body.on(\"data\", (chunk) => data += chunk); stream.body.on(\"end\", () => resolve(data)); }); IFiles.addChunked \u00b6 Added in 2.1.0 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/files/web\"; import \"@pnp/sp/files/folder\"; import * as fs from \"fs\"; const stream = fs.createReadStream(\"{file path}\"); const files = sp.web.defaultDocumentLibrary.rootFolder.files; await files.addChunked(name, stream, null, true, 10); IFile.setStreamContentChunked \u00b6 Added in 2.1.0 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/files/web\"; import \"@pnp/sp/files/folder\"; import * as fs from \"fs\"; const stream = fs.createReadStream(\"{file path}\"); const file = sp.web.defaultDocumentLibrary.rootFolder.files..getByName(\"file-name.txt\"); await file.setStreamContentChunked(stream); Explicit import \u00b6 If you don't need to import anything from the library, but would like to include the extensions just import the library as shown. // ES Modules: import \"@pnp/nodejs\"; import \"@pnp/nodejs-commonjs\"; // get the stream const streamResult = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/file.txt\").getStream(); Accessing SP Extension Namespace \u00b6 There are classes and interfaces included in extension modules, which you can access through a namespace, \"SPNS\". import { SPNS } from \"@pnp/nodejs-commonjs\"; const parser = new SPNS.StreamParser();","title":"@pnp/nodejs - sp extensions"},{"location":"v2/nodejs/sp-extensions/#pnpnodejs-sp-extensions","text":"By importing anything from the @pnp/nodejs library you automatically get nodejs specific extension methods added into the sp fluent api. This article describes them. These examples use the *-commonjs version of the libraries as they target node, you can read more about the differences .","title":"@pnp/nodejs - sp extensions"},{"location":"v2/nodejs/sp-extensions/#ifilegetstream","text":"Allows you to read a response body as a nodejs PassThrough stream. // by importing the the library the node specific extensions are automatically applied import { SPFetchClient, SPNS } from \"@pnp/nodejs-commonjs\"; import { sp } from \"@pnp/sp-commonjs\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{url}\", \"{id}\", \"{secret}\"); }, }, }); // get the stream const streamResult: SPNS.IResponseBodyStream = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/file.txt\").getStream(); // see if we have a known length console.log(streamResult.knownLength); // read the stream // this is a very basic example - you can do tons more with streams in node const txt = await new Promise((resolve) => { let data = \"\"; stream.body.on(\"data\", (chunk) => data += chunk); stream.body.on(\"end\", () => resolve(data)); });","title":"IFile.getStream"},{"location":"v2/nodejs/sp-extensions/#ifilesaddchunked","text":"Added in 2.1.0 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/files/web\"; import \"@pnp/sp/files/folder\"; import * as fs from \"fs\"; const stream = fs.createReadStream(\"{file path}\"); const files = sp.web.defaultDocumentLibrary.rootFolder.files; await files.addChunked(name, stream, null, true, 10);","title":"IFiles.addChunked"},{"location":"v2/nodejs/sp-extensions/#ifilesetstreamcontentchunked","text":"Added in 2.1.0 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/files/web\"; import \"@pnp/sp/files/folder\"; import * as fs from \"fs\"; const stream = fs.createReadStream(\"{file path}\"); const file = sp.web.defaultDocumentLibrary.rootFolder.files..getByName(\"file-name.txt\"); await file.setStreamContentChunked(stream);","title":"IFile.setStreamContentChunked"},{"location":"v2/nodejs/sp-extensions/#explicit-import","text":"If you don't need to import anything from the library, but would like to include the extensions just import the library as shown. // ES Modules: import \"@pnp/nodejs\"; import \"@pnp/nodejs-commonjs\"; // get the stream const streamResult = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/file.txt\").getStream();","title":"Explicit import"},{"location":"v2/nodejs/sp-extensions/#accessing-sp-extension-namespace","text":"There are classes and interfaces included in extension modules, which you can access through a namespace, \"SPNS\". import { SPNS } from \"@pnp/nodejs-commonjs\"; const parser = new SPNS.StreamParser();","title":"Accessing SP Extension Namespace"},{"location":"v2/nodejs/sp-fetch-client/","text":"@pnp/nodejs/spfetchclient \u00b6 The SPFetchClient is used to authentication to SharePoint as a provider hosted add-in using a client and secret in nodejs. Remember it is not a good practice to expose client ids and secrets on the client and use of this class is intended for nodejs exclusively. See: How to register a legacy SharePoint application import { SPFetchClient } from \"@pnp/nodejs\"; import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\"); }, }, }); // execute a library request as normal const w = await sp.web(); console.log(JSON.stringify(w, null, 4)); Set Authentication Environment \u00b6 For some areas such as Germany, China, and US Gov clouds you need to specify a different authentication url to the service. This is done by specifying the correct SPOAuthEnv enumeration to the SPFetchClient constructor. The options are listed below. If you are not sure which option to specify the default is likely OK. SPO : (default) for all *.sharepoint.com urls China: for China hosted cloud Germany: for Germany local cloud USDef: USA Defense cloud USGov: USA Government cloud import { sp } from \"@pnp/sp/presets/all\"; import { SPFetchClient, SPOAuthEnv } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\", SPOAuthEnv.China); }, }, }); Set Realm \u00b6 In some cases automatically resolving the realm may not work. In this case you can set the realm parameter in the SPFetchClient constructor. You can determine the correct value for the realm by navigating to https://{site name}-admin.sharepoint.com/_layouts/15/TA_AllAppPrincipals.aspx and copying the GUID value that appears after the \"@\" - this is the realm id. import { sp } from \"@pnp/sp/presets/all\"; import { SPFetchClient, SPOAuthEnv } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\", SPOAuthEnv.SPO, \"{realm}\"); }, }, });","title":"@pnp/nodejs/spfetchclient"},{"location":"v2/nodejs/sp-fetch-client/#pnpnodejsspfetchclient","text":"The SPFetchClient is used to authentication to SharePoint as a provider hosted add-in using a client and secret in nodejs. Remember it is not a good practice to expose client ids and secrets on the client and use of this class is intended for nodejs exclusively. See: How to register a legacy SharePoint application import { SPFetchClient } from \"@pnp/nodejs\"; import { sp } from \"@pnp/sp/presets/all\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\"); }, }, }); // execute a library request as normal const w = await sp.web(); console.log(JSON.stringify(w, null, 4));","title":"@pnp/nodejs/spfetchclient"},{"location":"v2/nodejs/sp-fetch-client/#set-authentication-environment","text":"For some areas such as Germany, China, and US Gov clouds you need to specify a different authentication url to the service. This is done by specifying the correct SPOAuthEnv enumeration to the SPFetchClient constructor. The options are listed below. If you are not sure which option to specify the default is likely OK. SPO : (default) for all *.sharepoint.com urls China: for China hosted cloud Germany: for Germany local cloud USDef: USA Defense cloud USGov: USA Government cloud import { sp } from \"@pnp/sp/presets/all\"; import { SPFetchClient, SPOAuthEnv } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\", SPOAuthEnv.China); }, }, });","title":"Set Authentication Environment"},{"location":"v2/nodejs/sp-fetch-client/#set-realm","text":"In some cases automatically resolving the realm may not work. In this case you can set the realm parameter in the SPFetchClient constructor. You can determine the correct value for the realm by navigating to https://{site name}-admin.sharepoint.com/_layouts/15/TA_AllAppPrincipals.aspx and copying the GUID value that appears after the \"@\" - this is the realm id. import { sp } from \"@pnp/sp/presets/all\"; import { SPFetchClient, SPOAuthEnv } from \"@pnp/nodejs\"; sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{site url}\", \"{client id}\", \"{client secret}\", SPOAuthEnv.SPO, \"{realm}\"); }, }, });","title":"Set Realm"},{"location":"v2/odata/","text":"@pnp/queryable \u00b6 This modules contains the abstract core classes used to process odata requests. They can also be used to build your own odata library should you wish to. By sharing the core functionality across libraries we can provide a consistent API as well as ensure the core code is solid and well tested, with any updates benefitting all inheriting libraries. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable --save Library Topics \u00b6 caching core OData Batching Parsers Pipeline Queryable","title":"@pnp/queryable"},{"location":"v2/odata/#pnpqueryable","text":"This modules contains the abstract core classes used to process odata requests. They can also be used to build your own odata library should you wish to. By sharing the core functionality across libraries we can provide a consistent API as well as ensure the core code is solid and well tested, with any updates benefitting all inheriting libraries.","title":"@pnp/queryable"},{"location":"v2/odata/#getting-started","text":"Install the library and required dependencies npm install @pnp/logging @pnp/core @pnp/queryable --save","title":"Getting Started"},{"location":"v2/odata/#library-topics","text":"caching core OData Batching Parsers Pipeline Queryable","title":"Library Topics"},{"location":"v2/odata/caching/","text":"@pnp/queryable/caching \u00b6 Often times data doesn't change that quickly, especially in the case of rolling up corporate news or upcoming events. These types of things can be cached for minutes if not hours. To help make caching easy you just need to insert the usingCaching method in your chain. This only applies to get requests. The usingCaching method can be used with the inBatch method as well to cache the results of batched requests. The below examples uses the @pnp/sp library as the example - but this works equally well for any library making use of the @pnp/queryable base classes, such as @pnp/graph. Basic example \u00b6 You can use the method without any additional configuration. We have made some default choices for you and will discuss ways to override them later. The code below will get items from a list, first checking the cache for the value. You can also use it with OData operators such as top and orderBy. The usingCaching() method should always be the last method in the chain before the get() (OR if you are using batching these methods can be transposed, more details below). import { sp } from \"@pnp/sp\"; const r = await sp.web.lists.getByTitle(\"Tasks\").items.usingCaching()(); console.log(r); const r2 = await sp.web.lists.getByTitle(\"Tasks\").items.top(5).orderBy(\"Modified\").usingCaching()(); console.log(r2); Globally Configure Cache Settings \u00b6 If you would not like to use the default values, but don't want to clutter your code by setting the caching values on each request you can configure custom options globally. These will be applied to all calls to usingCaching() throughout your application. import { sp } from \"@pnp/sp\"; sp.setup({ defaultCachingStore: \"session\", // or \"local\" defaultCachingTimeoutSeconds: 30, globalCacheDisable: false // or true to disable caching in case of debugging/testing }); const r = await sp.web.lists.getByTitle(\"Tasks\").items.top(5).orderBy(\"Modified\").usingCaching()(); console.log(r); Per Call Configuration \u00b6 If you prefer more verbose code or have a need to manage the cache settings on a per request basis you can include individual caching settings for each request. These settings are passed to the usingCaching method call and are defined in the following interface. If you want to use the per-request options you must include the key. export interface ICachingOptions { expiration?: Date; storeName?: \"session\" | \"local\"; key: string; } import { sp } from \"@pnp/sp\"; import { dateAdd } from \"@pnp/core\"; const r = await sp.web.lists.getByTitle(\"Tasks\").items.top(5).orderBy(\"Modified\").usingCaching({ expiration: dateAdd(new Date(), \"minute\", 20), key: \"My Key\", storeName: \"local\" })(); console.log(r); Using Batching with Caching \u00b6 You can use batching and caching together, but remember caching is only applied to get requests. When you use them together the methods can be transposed, the below example is valid. import { sp } from \"@pnp/sp\"; let batch = sp.createBatch(); sp.web.lists.inBatch(batch).usingCaching()().then(r => { console.log(r) }); sp.web.lists.getByTitle(\"Tasks\").items.usingCaching().inBatch(batch)().then(r => { console.log(r) }); batch.execute().then(() => console.log(\"All done!\")); Implement Custom Caching \u00b6 You may desire to use a different caching strategy than the one we implemented within the library. The easiest way to achieve this is to wrap the request in your custom caching functionality using the unresolved promise as needed. Here we show how to implement the Stale While Revalidate pattern as discussed here . Implement caching helper method \u00b6 We create a map to act as our cache storage and a function to wrap the request caching logic const map = new Map(); async function staleWhileRevalidate(key: string, p: Promise): Promise { if (map.has(key)) { // In Cache p.then(u => { // Update Cache once we have a result map.set(key, u); }); // Return from Cache return map.get(key); } // Not In Cache so we need to wait for the value const r = await p; // Set Cache map.set(key, r); // Return from Promise return r; } Usage \u00b6 Don't call usingCaching just apply the helper method // this one will wait for the request to finish const r1 = await staleWhileRevalidate(\"test1\", sp.web.select(\"Title\", \"Description\")()); console.log(JSON.stringify(r1, null, 2)); // this one will return the result from cache and then update the cache in the background const r2 = await staleWhileRevalidate(\"test1\", sp.web.select(\"Title\", \"Description\")()); console.log(JSON.stringify(r2, null, 2)); Wrapper Function \u00b6 You can wrap this call into a single function you can reuse within your application each time you need the web data for example. You can update the select and interface to match your needs as well. interface WebData { Title: string; Description: string; } function getWebData(): Promise { return staleWhileRevalidate(\"test1\", sp.web.select(\"Title\", \"Description\")()); } // this one will wait for the request to finish const r1 = await getWebData(); console.log(JSON.stringify(r1, null, 2)); // this one will return the result from cache and then update the cache in the background const r2 = await getWebData(); console.log(JSON.stringify(r2, null, 2));","title":"@pnp/queryable/caching"},{"location":"v2/odata/caching/#pnpqueryablecaching","text":"Often times data doesn't change that quickly, especially in the case of rolling up corporate news or upcoming events. These types of things can be cached for minutes if not hours. To help make caching easy you just need to insert the usingCaching method in your chain. This only applies to get requests. The usingCaching method can be used with the inBatch method as well to cache the results of batched requests. The below examples uses the @pnp/sp library as the example - but this works equally well for any library making use of the @pnp/queryable base classes, such as @pnp/graph.","title":"@pnp/queryable/caching"},{"location":"v2/odata/caching/#basic-example","text":"You can use the method without any additional configuration. We have made some default choices for you and will discuss ways to override them later. The code below will get items from a list, first checking the cache for the value. You can also use it with OData operators such as top and orderBy. The usingCaching() method should always be the last method in the chain before the get() (OR if you are using batching these methods can be transposed, more details below). import { sp } from \"@pnp/sp\"; const r = await sp.web.lists.getByTitle(\"Tasks\").items.usingCaching()(); console.log(r); const r2 = await sp.web.lists.getByTitle(\"Tasks\").items.top(5).orderBy(\"Modified\").usingCaching()(); console.log(r2);","title":"Basic example"},{"location":"v2/odata/caching/#globally-configure-cache-settings","text":"If you would not like to use the default values, but don't want to clutter your code by setting the caching values on each request you can configure custom options globally. These will be applied to all calls to usingCaching() throughout your application. import { sp } from \"@pnp/sp\"; sp.setup({ defaultCachingStore: \"session\", // or \"local\" defaultCachingTimeoutSeconds: 30, globalCacheDisable: false // or true to disable caching in case of debugging/testing }); const r = await sp.web.lists.getByTitle(\"Tasks\").items.top(5).orderBy(\"Modified\").usingCaching()(); console.log(r);","title":"Globally Configure Cache Settings"},{"location":"v2/odata/caching/#per-call-configuration","text":"If you prefer more verbose code or have a need to manage the cache settings on a per request basis you can include individual caching settings for each request. These settings are passed to the usingCaching method call and are defined in the following interface. If you want to use the per-request options you must include the key. export interface ICachingOptions { expiration?: Date; storeName?: \"session\" | \"local\"; key: string; } import { sp } from \"@pnp/sp\"; import { dateAdd } from \"@pnp/core\"; const r = await sp.web.lists.getByTitle(\"Tasks\").items.top(5).orderBy(\"Modified\").usingCaching({ expiration: dateAdd(new Date(), \"minute\", 20), key: \"My Key\", storeName: \"local\" })(); console.log(r);","title":"Per Call Configuration"},{"location":"v2/odata/caching/#using-batching-with-caching","text":"You can use batching and caching together, but remember caching is only applied to get requests. When you use them together the methods can be transposed, the below example is valid. import { sp } from \"@pnp/sp\"; let batch = sp.createBatch(); sp.web.lists.inBatch(batch).usingCaching()().then(r => { console.log(r) }); sp.web.lists.getByTitle(\"Tasks\").items.usingCaching().inBatch(batch)().then(r => { console.log(r) }); batch.execute().then(() => console.log(\"All done!\"));","title":"Using Batching with Caching"},{"location":"v2/odata/caching/#implement-custom-caching","text":"You may desire to use a different caching strategy than the one we implemented within the library. The easiest way to achieve this is to wrap the request in your custom caching functionality using the unresolved promise as needed. Here we show how to implement the Stale While Revalidate pattern as discussed here .","title":"Implement Custom Caching"},{"location":"v2/odata/caching/#implement-caching-helper-method","text":"We create a map to act as our cache storage and a function to wrap the request caching logic const map = new Map(); async function staleWhileRevalidate(key: string, p: Promise): Promise { if (map.has(key)) { // In Cache p.then(u => { // Update Cache once we have a result map.set(key, u); }); // Return from Cache return map.get(key); } // Not In Cache so we need to wait for the value const r = await p; // Set Cache map.set(key, r); // Return from Promise return r; }","title":"Implement caching helper method"},{"location":"v2/odata/caching/#usage","text":"Don't call usingCaching just apply the helper method // this one will wait for the request to finish const r1 = await staleWhileRevalidate(\"test1\", sp.web.select(\"Title\", \"Description\")()); console.log(JSON.stringify(r1, null, 2)); // this one will return the result from cache and then update the cache in the background const r2 = await staleWhileRevalidate(\"test1\", sp.web.select(\"Title\", \"Description\")()); console.log(JSON.stringify(r2, null, 2));","title":"Usage"},{"location":"v2/odata/caching/#wrapper-function","text":"You can wrap this call into a single function you can reuse within your application each time you need the web data for example. You can update the select and interface to match your needs as well. interface WebData { Title: string; Description: string; } function getWebData(): Promise { return staleWhileRevalidate(\"test1\", sp.web.select(\"Title\", \"Description\")()); } // this one will wait for the request to finish const r1 = await getWebData(); console.log(JSON.stringify(r1, null, 2)); // this one will return the result from cache and then update the cache in the background const r2 = await getWebData(); console.log(JSON.stringify(r2, null, 2));","title":"Wrapper Function"},{"location":"v2/odata/core/","text":"@pnp/queryable/core \u00b6 This module contains shared interfaces and abstract classes used within the @pnp/queryable package and those items that inherit from it. ProcessHttpClientResponseException \u00b6 The exception thrown when a response is returned and cannot be processed. interface ODataParser \u00b6 Base interface used to describe a class that that will parse incoming responses. It takes a single type parameter representing the type of the value to be returned. It has two methods, one is optional: parse(r: Response): Promise - main method use to parse a response and return a Promise resolving to an object of type T hydrate?: (d: any) => T - optional method used when getting an object from the cache if it requires calling a constructor ODataParserBase \u00b6 The base class used by all parsers in the @pnp libraries. It is optional to use when creating your own custom parsers, but does contain several helper methods. Create a custom parser from ODataParserBase \u00b6 You can always create custom parsers for your projects, however it is likely you will not require this step as the default parsers should work for most cases. class MyParser extends ODataParserBase { // we need to override the parse method to do our custom stuff public parse(r: Response): Promise { // we wrap everything in a promise return new Promise((resolve, reject) => { // lets use the default error handling which returns true for no error // and will call reject with an error if one exists if (this.handleError(r, reject)) { // now we add our custom parsing here r.text().then(txt => { // here we call a made up function to parse the result // this is where we would do our parsing as required myCustomerUnencode(txt).then(v => { resolve(v); }); }); } }); } }","title":"@pnp/queryable/core"},{"location":"v2/odata/core/#pnpqueryablecore","text":"This module contains shared interfaces and abstract classes used within the @pnp/queryable package and those items that inherit from it.","title":"@pnp/queryable/core"},{"location":"v2/odata/core/#processhttpclientresponseexception","text":"The exception thrown when a response is returned and cannot be processed.","title":"ProcessHttpClientResponseException"},{"location":"v2/odata/core/#interface-odataparser","text":"Base interface used to describe a class that that will parse incoming responses. It takes a single type parameter representing the type of the value to be returned. It has two methods, one is optional: parse(r: Response): Promise - main method use to parse a response and return a Promise resolving to an object of type T hydrate?: (d: any) => T - optional method used when getting an object from the cache if it requires calling a constructor","title":"interface ODataParser"},{"location":"v2/odata/core/#odataparserbase","text":"The base class used by all parsers in the @pnp libraries. It is optional to use when creating your own custom parsers, but does contain several helper methods.","title":"ODataParserBase"},{"location":"v2/odata/core/#create-a-custom-parser-from-odataparserbase","text":"You can always create custom parsers for your projects, however it is likely you will not require this step as the default parsers should work for most cases. class MyParser extends ODataParserBase { // we need to override the parse method to do our custom stuff public parse(r: Response): Promise { // we wrap everything in a promise return new Promise((resolve, reject) => { // lets use the default error handling which returns true for no error // and will call reject with an error if one exists if (this.handleError(r, reject)) { // now we add our custom parsing here r.text().then(txt => { // here we call a made up function to parse the result // this is where we would do our parsing as required myCustomerUnencode(txt).then(v => { resolve(v); }); }); } }); } }","title":"Create a custom parser from ODataParserBase"},{"location":"v2/odata/debug/","text":"Debugging Proxy Objects \u00b6 Because all queryables are now represented as Proxy objects you can't immediately see the properties/method of the object or the data stored about the request. In certain debugging scenarios it can help to get visibility into the object that is wrapped by the proxy. To enable this we provide a set of extensions to help. The debug extensions are added by including the import \"@pnp/queryable/debug\"; statement in your project. It should be removed for production. This module provides several methods to help with debugging Queryable Proxy objects. Unwrap \u00b6 The __unwrap() method returns the concrete Queryable instance wrapped by the Proxy. You can then examine this object in various ways or dump it to the console for debugging. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; // unwrap the underlying concrete queryable instance const unwrapped = sp.web.__unwrap(); console.log(JSON.stringify(unwrapped, null, 2)); Note: It is not supported to unwrap objects and then use them. It may work in some cases, but this behavior may change as what is contained with the Proxy is an implementation detail and should not be relied upon. Without the Proxy wrapper we make no guarantees. Data \u00b6 All of the information related to a queryable's request is contained within the \"data\" property. If you need to grab that information you can use the __data property. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; // get the underlying queryable's data const data = sp.web.__data; console.log(JSON.stringify(data, null, 2)); JSON \u00b6 You can also get a representation of the wrapped instance in JSON format consisting of all its own properties and values. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; // get the underlying queryable's as JSON const data = sp.web.__json(); console.log(JSON.stringify(data, null, 2)); Deep Trace \u00b6 Deep tracing is the ability to write every property and method access to the log. This produces VERY verbose output but can be helpful in situations where you need to trace how things are called and when within the Proxy. You enable deep tracing using the __enableDeepTrace method and disable using __disableDeepTrace . import { Logger, ConsoleListener } from \"@pnp/logging\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; Logger.subscribe(new ConsoleListener()); // grab an instance to enable deep trace const web = sp.web; // enable deep trace on the instance web.__enableDeepTrace(); const y = await web.lists(); // disable deep trace web.__disableDeepTrace(); The example above produces the following output: Message: get ::> lists Message: get ::> lists Message: get ::> toUrl Message: get ::> toUrl Message: get ::> data Message: get ::> data Message: get ::> _data Message: get ::> query Message: get ::> query Message: get ::> data Message: get ::> data Message: get ::> _data Message: get ::> _data Message: get ::> data Message: get ::> data Message: get ::> _data Message: get ::> _data Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122352) Beginning GET request (_api/web/lists) Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122352) Beginning GET request (_api/web/lists) Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122354) Sending request. Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122354) Sending request. Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124099) Completing GET request. Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124099) Completing GET request. Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124102) Returning result from pipeline. Set logging to verbose to see data. Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124102) Returning result from pipeline. Set logging to verbose to see data. Data: {} Message: get ::> __disableDeepTrace Message: get ::> __disableDeepTrace","title":"Debugging Proxy Objects"},{"location":"v2/odata/debug/#debugging-proxy-objects","text":"Because all queryables are now represented as Proxy objects you can't immediately see the properties/method of the object or the data stored about the request. In certain debugging scenarios it can help to get visibility into the object that is wrapped by the proxy. To enable this we provide a set of extensions to help. The debug extensions are added by including the import \"@pnp/queryable/debug\"; statement in your project. It should be removed for production. This module provides several methods to help with debugging Queryable Proxy objects.","title":"Debugging Proxy Objects"},{"location":"v2/odata/debug/#unwrap","text":"The __unwrap() method returns the concrete Queryable instance wrapped by the Proxy. You can then examine this object in various ways or dump it to the console for debugging. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; // unwrap the underlying concrete queryable instance const unwrapped = sp.web.__unwrap(); console.log(JSON.stringify(unwrapped, null, 2)); Note: It is not supported to unwrap objects and then use them. It may work in some cases, but this behavior may change as what is contained with the Proxy is an implementation detail and should not be relied upon. Without the Proxy wrapper we make no guarantees.","title":"Unwrap"},{"location":"v2/odata/debug/#data","text":"All of the information related to a queryable's request is contained within the \"data\" property. If you need to grab that information you can use the __data property. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; // get the underlying queryable's data const data = sp.web.__data; console.log(JSON.stringify(data, null, 2));","title":"Data"},{"location":"v2/odata/debug/#json","text":"You can also get a representation of the wrapped instance in JSON format consisting of all its own properties and values. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; // get the underlying queryable's as JSON const data = sp.web.__json(); console.log(JSON.stringify(data, null, 2));","title":"JSON"},{"location":"v2/odata/debug/#deep-trace","text":"Deep tracing is the ability to write every property and method access to the log. This produces VERY verbose output but can be helpful in situations where you need to trace how things are called and when within the Proxy. You enable deep tracing using the __enableDeepTrace method and disable using __disableDeepTrace . import { Logger, ConsoleListener } from \"@pnp/logging\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/queryable/debug\"; Logger.subscribe(new ConsoleListener()); // grab an instance to enable deep trace const web = sp.web; // enable deep trace on the instance web.__enableDeepTrace(); const y = await web.lists(); // disable deep trace web.__disableDeepTrace(); The example above produces the following output: Message: get ::> lists Message: get ::> lists Message: get ::> toUrl Message: get ::> toUrl Message: get ::> data Message: get ::> data Message: get ::> _data Message: get ::> query Message: get ::> query Message: get ::> data Message: get ::> data Message: get ::> _data Message: get ::> _data Message: get ::> data Message: get ::> data Message: get ::> _data Message: get ::> _data Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122352) Beginning GET request (_api/web/lists) Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122352) Beginning GET request (_api/web/lists) Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122354) Sending request. Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232122354) Sending request. Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124099) Completing GET request. Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124099) Completing GET request. Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124102) Returning result from pipeline. Set logging to verbose to see data. Data: {} Message: [5912fe3e-6c2a-4538-84ee-eec28a29cfef] (1580232124102) Returning result from pipeline. Set logging to verbose to see data. Data: {} Message: get ::> __disableDeepTrace Message: get ::> __disableDeepTrace","title":"Deep Trace"},{"location":"v2/odata/extensions/","text":"Extensions \u00b6 introduced in 2.0.0 Extending is the concept of overriding or adding functionality into an object or environment without altering the underlying class instances. This can be useful for debugging, testing, or injecting custom functionality. Extensions work with any invocable . You can control any behavior of the library with extensions. Extensions do not work in ie11 compatibility mode. This is by design. Types of Extensions \u00b6 There are three types of Extensions available as well as three methods for registration. You can register any type of extension with any of the registration options. Function Extensions \u00b6 The first type is a simple function with a signature: (op: \"apply\" | \"get\" | \"has\" | \"set\", target: T, ...rest: any[]): void This function is passed the current operation as the first argument, currently one of \"apply\", \"get\", \"has\", or \"set\". The second argument is the target instance upon which the operation is being invoked. The remaining parameters vary by the operation being performed, but will match their respective ProxyHandler method signatures. Named Extensions \u00b6 Named extensions are designed to add or replace a single property or method, though you can register multiple using the same object. These extensions are defined by using an object which has the property/methods you want to override described. Registering named extensions globally will override that operation to all invokables. import { extendFactory } from \"@pnp/queryable\"; import { sp, List, Lists, IWeb, ILists, List, IList, Web } from \"@pnp/sp/presets/all\"; import { escapeQueryStrValue } from \"@pnp/sp/utils/escapeQueryStrValue\"; // create a plain object with the props and methods we want to add/change const myExtensions = { // override the lists property get lists(this: IWeb): ILists { // we will always order our lists by title and select just the Title for ALL calls (just as an example) return Lists(this).orderBy(\"Title\").select(\"Title\"); }, // override the getByTitle method getByTitle: function (this: ILists, title: string): IList { // in our example our list has moved, so we rewrite the request on the fly if (title === \"List1\") { return List(this, `getByTitle('List2')`); } else { // you can't at this point call the \"base\" method as you will end up in loop within the proxy // so you need to ensure you patch/include any original functionality you need return List(this, `getByTitle('${escapeQueryStrValue(title)}')`); } }, }; // register all the named Extensions extendFactory(Web, myExtensions); // this will use our extension to ensure the lists are ordered const lists = await sp.web.lists(); console.log(JSON.stringify(lists, null, 2)); // we will get the items from List1 but within the extension it is rewritten as List2 const items = await sp.web.lists.getByTitle(\"List1\").items(); console.log(JSON.stringify(items.length, null, 2)); ProxyHandler Extensions \u00b6 You can also register a partial ProxyHandler implementation as an extension. You can implement one or more of the ProxyHandler methods as needed. Here we implement the same override of getByTitle globally. This is the most complicated method of creating an extension and assumes an understanding of how ProxyHandlers work. import { extendFactory } from \"@pnp/queryable\"; import { sp, Lists, IWeb, ILists, Web } from \"@pnp/sp/presets/all\"; import { escapeQueryStrValue } from \"@pnp/sp/utils/escapeSingleQuote\"; const myExtensions = { get: (target, p: string | number | symbol, _receiver: any) => { switch (p) { case \"getByTitle\": return (title: string) => { // in our example our list has moved, so we rewrite the request on the fly if (title === \"LookupList\") { return List(target, `getByTitle('OrderByList')`); } else { // you can't at this point call the \"base\" method as you will end up in loop within the proxy // so you need to ensure you patch/include any original functionality you need return List(target, `getByTitle('${escapeQueryStrValue(title)}')`); } }; } }, }; extendFactory(Web, myExtensions); const lists = sp.web.lists; const items = await lists.getByTitle(\"LookupList\").items(); console.log(JSON.stringify(items.length, null, 2)); Registering Extensions \u00b6 You can register Extensions either globally, on an invocable factory, or on a per-object basis, and you can register a single extension or an array of Extensions. Global Registration \u00b6 Globally registering an extension allows you to inject functionality into every invocable that is instantiated within your application. It is important to remember that processing extensions happens on ALL property access and method invocation operations - so global extensions should be used sparingly. import { extendGlobal } from \"@pnp/queryable\"; // we can add a logging method to very verbosely track what things are called in our application extendGlobal((op: string, _target: any, ...rest: any[]): void => { switch (op) { case \"apply\": Logger.write(`${op} ::> ()`, LogLevel.Info); break; case \"has\": case \"get\": case \"set\": Logger.write(`${op} ::> ${rest[0]}`, LogLevel.Info); break; default: Logger.write(`unknown ${op}`, LogLevel.Info); } }); Factory Registration \u00b6 The pattern you will likely find most useful is the ability to extend an invocable factory. This will apply your extensions to all instances created with that factory, meaning all IWebs or ILists will have the extension methods. The example below shows how to add a property to IWeb as well as a method to IList. import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import { IWeb, Web } from \"@pnp/sp/webs\"; import { ILists, Lists } from \"@pnp/sp/lists\"; import { extendFactory } from \"@pnp/queryable\"; import { sp } from \"@pnp/sp\"; // sets up the types correctly when importing across your application declare module \"@pnp/sp/webs/types\" { // we need to extend the interface interface IWeb { orderedLists: ILists; } } // sets up the types correctly when importing across your application declare module \"@pnp/sp/lists/types\" { // we need to extend the interface interface ILists { getOrderedListsQuery: (this: ILists) => ILists; } } extendFactory(Web, { // add an ordered lists property get orderedLists(this: IWeb): ILists { return this.lists.getOrderedListsQuery(); }, }); extendFactory(Lists, { // add an ordered lists property getOrderedListsQuery(this: ILists): ILists { return this.top(10).orderBy(\"Title\").select(\"Title\"); }, }); // regardless of how we access the web and lists collections our extensions remain with all new instance based on const web = Web(\"https://tenant.sharepoint.com/sites/dev/\"); const lists1 = await web.orderedLists(); console.log(JSON.stringify(lists1, null, 2)); const lists2 = await Web(\"https://tenant.sharepoint.com/sites/dev/\").orderedLists(); console.log(JSON.stringify(lists2, null, 2)); const lists3 = await sp.web.orderedLists(); console.log(JSON.stringify(lists3, null, 2)); Instance Registration \u00b6 You can also register Extensions on a single object instance, which is often the preferred approach as it will have less of a performance impact across your whole application. This is useful for debugging, overriding methods/properties, or controlling the behavior of specific object instances. Extensions are not transferred to child objects in a fluent chain, be sure you are extending the instance you think you are. Here we show the same override operation of getByTitle on the lists collection, but safely only overriding the single instance. import { extendObj } from \"@pnp/queryable\"; import { sp, List, ILists } from \"@pnp/sp/presets/all\"; const myExtensions = { getByTitle: function (this: ILists, title: string) { // in our example our list has moved, so we rewrite the request on the fly if (title === \"List1\") { return List(this, \"getByTitle('List2')\"); } else { // you can't at this point call the \"base\" method as you will end up in loop within the proxy // so you need to ensure you patch/include any original functionality you need return List(this, `getByTitle('${escapeQueryStrValue(title)}')`); } }, }; const lists = extendObj(sp.web.lists, myExtensions); const items = await lists.getByTitle(\"LookupList\").items(); console.log(JSON.stringify(items.length, null, 2)); Enable & Disable Extensions and Clear Global Extensions \u00b6 Extensions are automatically enabled when you set an extension through any of the above outlined methods. You can disable and enable extensions on demand if needed. import { enableExtensions, disableExtensions, clearGlobalExtensions } from \"@pnp/queryable\"; // disable Extensions disableExtensions(); // enable Extensions enableExtensions(); // clear all the globally registered extensions clearGlobalExtensions(); Order of Operations \u00b6 It is important to understand the order in which extensions are executed and when a value is returned. Instance extensions* are always called first, followed by global Extensions - in both cases they are called in the order they were registered. This allows you to perhaps have some global functionality while maintaining the ability to override it again at the instance level. IF an extension returns a value other than undefined that value is returned and no other extensions are processed. *extensions applied via an extended factory are considered instance extensions","title":"Extensions"},{"location":"v2/odata/extensions/#extensions","text":"introduced in 2.0.0 Extending is the concept of overriding or adding functionality into an object or environment without altering the underlying class instances. This can be useful for debugging, testing, or injecting custom functionality. Extensions work with any invocable . You can control any behavior of the library with extensions. Extensions do not work in ie11 compatibility mode. This is by design.","title":"Extensions"},{"location":"v2/odata/extensions/#types-of-extensions","text":"There are three types of Extensions available as well as three methods for registration. You can register any type of extension with any of the registration options.","title":"Types of Extensions"},{"location":"v2/odata/extensions/#function-extensions","text":"The first type is a simple function with a signature: (op: \"apply\" | \"get\" | \"has\" | \"set\", target: T, ...rest: any[]): void This function is passed the current operation as the first argument, currently one of \"apply\", \"get\", \"has\", or \"set\". The second argument is the target instance upon which the operation is being invoked. The remaining parameters vary by the operation being performed, but will match their respective ProxyHandler method signatures.","title":"Function Extensions"},{"location":"v2/odata/extensions/#named-extensions","text":"Named extensions are designed to add or replace a single property or method, though you can register multiple using the same object. These extensions are defined by using an object which has the property/methods you want to override described. Registering named extensions globally will override that operation to all invokables. import { extendFactory } from \"@pnp/queryable\"; import { sp, List, Lists, IWeb, ILists, List, IList, Web } from \"@pnp/sp/presets/all\"; import { escapeQueryStrValue } from \"@pnp/sp/utils/escapeQueryStrValue\"; // create a plain object with the props and methods we want to add/change const myExtensions = { // override the lists property get lists(this: IWeb): ILists { // we will always order our lists by title and select just the Title for ALL calls (just as an example) return Lists(this).orderBy(\"Title\").select(\"Title\"); }, // override the getByTitle method getByTitle: function (this: ILists, title: string): IList { // in our example our list has moved, so we rewrite the request on the fly if (title === \"List1\") { return List(this, `getByTitle('List2')`); } else { // you can't at this point call the \"base\" method as you will end up in loop within the proxy // so you need to ensure you patch/include any original functionality you need return List(this, `getByTitle('${escapeQueryStrValue(title)}')`); } }, }; // register all the named Extensions extendFactory(Web, myExtensions); // this will use our extension to ensure the lists are ordered const lists = await sp.web.lists(); console.log(JSON.stringify(lists, null, 2)); // we will get the items from List1 but within the extension it is rewritten as List2 const items = await sp.web.lists.getByTitle(\"List1\").items(); console.log(JSON.stringify(items.length, null, 2));","title":"Named Extensions"},{"location":"v2/odata/extensions/#proxyhandler-extensions","text":"You can also register a partial ProxyHandler implementation as an extension. You can implement one or more of the ProxyHandler methods as needed. Here we implement the same override of getByTitle globally. This is the most complicated method of creating an extension and assumes an understanding of how ProxyHandlers work. import { extendFactory } from \"@pnp/queryable\"; import { sp, Lists, IWeb, ILists, Web } from \"@pnp/sp/presets/all\"; import { escapeQueryStrValue } from \"@pnp/sp/utils/escapeSingleQuote\"; const myExtensions = { get: (target, p: string | number | symbol, _receiver: any) => { switch (p) { case \"getByTitle\": return (title: string) => { // in our example our list has moved, so we rewrite the request on the fly if (title === \"LookupList\") { return List(target, `getByTitle('OrderByList')`); } else { // you can't at this point call the \"base\" method as you will end up in loop within the proxy // so you need to ensure you patch/include any original functionality you need return List(target, `getByTitle('${escapeQueryStrValue(title)}')`); } }; } }, }; extendFactory(Web, myExtensions); const lists = sp.web.lists; const items = await lists.getByTitle(\"LookupList\").items(); console.log(JSON.stringify(items.length, null, 2));","title":"ProxyHandler Extensions"},{"location":"v2/odata/extensions/#registering-extensions","text":"You can register Extensions either globally, on an invocable factory, or on a per-object basis, and you can register a single extension or an array of Extensions.","title":"Registering Extensions"},{"location":"v2/odata/extensions/#global-registration","text":"Globally registering an extension allows you to inject functionality into every invocable that is instantiated within your application. It is important to remember that processing extensions happens on ALL property access and method invocation operations - so global extensions should be used sparingly. import { extendGlobal } from \"@pnp/queryable\"; // we can add a logging method to very verbosely track what things are called in our application extendGlobal((op: string, _target: any, ...rest: any[]): void => { switch (op) { case \"apply\": Logger.write(`${op} ::> ()`, LogLevel.Info); break; case \"has\": case \"get\": case \"set\": Logger.write(`${op} ::> ${rest[0]}`, LogLevel.Info); break; default: Logger.write(`unknown ${op}`, LogLevel.Info); } });","title":"Global Registration"},{"location":"v2/odata/extensions/#factory-registration","text":"The pattern you will likely find most useful is the ability to extend an invocable factory. This will apply your extensions to all instances created with that factory, meaning all IWebs or ILists will have the extension methods. The example below shows how to add a property to IWeb as well as a method to IList. import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import { IWeb, Web } from \"@pnp/sp/webs\"; import { ILists, Lists } from \"@pnp/sp/lists\"; import { extendFactory } from \"@pnp/queryable\"; import { sp } from \"@pnp/sp\"; // sets up the types correctly when importing across your application declare module \"@pnp/sp/webs/types\" { // we need to extend the interface interface IWeb { orderedLists: ILists; } } // sets up the types correctly when importing across your application declare module \"@pnp/sp/lists/types\" { // we need to extend the interface interface ILists { getOrderedListsQuery: (this: ILists) => ILists; } } extendFactory(Web, { // add an ordered lists property get orderedLists(this: IWeb): ILists { return this.lists.getOrderedListsQuery(); }, }); extendFactory(Lists, { // add an ordered lists property getOrderedListsQuery(this: ILists): ILists { return this.top(10).orderBy(\"Title\").select(\"Title\"); }, }); // regardless of how we access the web and lists collections our extensions remain with all new instance based on const web = Web(\"https://tenant.sharepoint.com/sites/dev/\"); const lists1 = await web.orderedLists(); console.log(JSON.stringify(lists1, null, 2)); const lists2 = await Web(\"https://tenant.sharepoint.com/sites/dev/\").orderedLists(); console.log(JSON.stringify(lists2, null, 2)); const lists3 = await sp.web.orderedLists(); console.log(JSON.stringify(lists3, null, 2));","title":"Factory Registration"},{"location":"v2/odata/extensions/#instance-registration","text":"You can also register Extensions on a single object instance, which is often the preferred approach as it will have less of a performance impact across your whole application. This is useful for debugging, overriding methods/properties, or controlling the behavior of specific object instances. Extensions are not transferred to child objects in a fluent chain, be sure you are extending the instance you think you are. Here we show the same override operation of getByTitle on the lists collection, but safely only overriding the single instance. import { extendObj } from \"@pnp/queryable\"; import { sp, List, ILists } from \"@pnp/sp/presets/all\"; const myExtensions = { getByTitle: function (this: ILists, title: string) { // in our example our list has moved, so we rewrite the request on the fly if (title === \"List1\") { return List(this, \"getByTitle('List2')\"); } else { // you can't at this point call the \"base\" method as you will end up in loop within the proxy // so you need to ensure you patch/include any original functionality you need return List(this, `getByTitle('${escapeQueryStrValue(title)}')`); } }, }; const lists = extendObj(sp.web.lists, myExtensions); const items = await lists.getByTitle(\"LookupList\").items(); console.log(JSON.stringify(items.length, null, 2));","title":"Instance Registration"},{"location":"v2/odata/extensions/#enable-disable-extensions-and-clear-global-extensions","text":"Extensions are automatically enabled when you set an extension through any of the above outlined methods. You can disable and enable extensions on demand if needed. import { enableExtensions, disableExtensions, clearGlobalExtensions } from \"@pnp/queryable\"; // disable Extensions disableExtensions(); // enable Extensions enableExtensions(); // clear all the globally registered extensions clearGlobalExtensions();","title":"Enable & Disable Extensions and Clear Global Extensions"},{"location":"v2/odata/extensions/#order-of-operations","text":"It is important to understand the order in which extensions are executed and when a value is returned. Instance extensions* are always called first, followed by global Extensions - in both cases they are called in the order they were registered. This allows you to perhaps have some global functionality while maintaining the ability to override it again at the instance level. IF an extension returns a value other than undefined that value is returned and no other extensions are processed. *extensions applied via an extended factory are considered instance extensions","title":"Order of Operations"},{"location":"v2/odata/odata-batch/","text":"@pnp/queryable/odatabatch \u00b6 This module contains an abstract class used as a base when inheriting libraries support batching. ODataBatchRequestInfo \u00b6 This interface defines what each batch needs to know about each request. It is generic in that any library can provide the information but will be responsible for processing that info by implementing the abstract executeImpl method. ODataBatch \u00b6 Base class for building batching support for a library inheriting from @pnp/queryable. You can see implementations of this abstract class in the @pnp/sp and @pnp/graph modules.","title":"@pnp/queryable/odatabatch"},{"location":"v2/odata/odata-batch/#pnpqueryableodatabatch","text":"This module contains an abstract class used as a base when inheriting libraries support batching.","title":"@pnp/queryable/odatabatch"},{"location":"v2/odata/odata-batch/#odatabatchrequestinfo","text":"This interface defines what each batch needs to know about each request. It is generic in that any library can provide the information but will be responsible for processing that info by implementing the abstract executeImpl method.","title":"ODataBatchRequestInfo"},{"location":"v2/odata/odata-batch/#odatabatch","text":"Base class for building batching support for a library inheriting from @pnp/queryable. You can see implementations of this abstract class in the @pnp/sp and @pnp/graph modules.","title":"ODataBatch"},{"location":"v2/odata/parsers/","text":"@pnp/queryable/parsers \u00b6 This modules contains a set of generic parsers. These can be used or extended as needed, though it is likely in most cases the default parser will be all you need. ODataDefaultParser \u00b6 The simplest parser used to transform a Response into its JSON representation. The default parser will handle errors in a consistent manner throwing an HttpRequestError instance. This class extends Error and adds the response, status, and statusText properties. The response object is unread. You can use this custom error as shown below to gather more information about what went wrong in the request. import { sp } from \"@pnp/sp\"; import { JSONParser } from \"@pnp/queryable\"; try { const parser = new JSONParser(); // this always throws a 404 error await sp.web.getList(\"doesn't exist\").get(parser); } catch (e) { // we can check for the property \"isHttpRequestError\" to see if this is an instance of our class // this gets by all the many limitations of subclassing Error and type detection in JavaScript if (e.hasOwnProperty(\"isHttpRequestError\")) { console.log(\"e is HttpRequestError\"); // now we can access the various properties and make use of the response object. // at this point the body is unread console.log(`status: ${e.status}`); console.log(`statusText: ${e.statusText}`); const json = await e.response.clone().json(); console.log(JSON.stringify(json)); const text = await e.response.clone().text(); console.log(text); const headers = e.response.headers; } console.error(e); } TextParser \u00b6 Specialized parser used to parse the response using the .text() method with no other processing. Used primarily for files. BlobParser \u00b6 Specialized parser used to parse the response using the .blob() method with no other processing. Used primarily for files. JSONParser \u00b6 Specialized parser used to parse the response using the .json() method with no other processing. Used primarily for files. BufferParser \u00b6 Specialized parser used to parse the response using the .arrayBuffer() [node] for .buffer() [browser] method with no other processing. Used primarily for files. LambdaParser \u00b6 Allows you to pass in any handler function you want, called if the request does not result in an error that transforms the raw, unread request into the result type. import { LambdaParser } from \"@pnp/queryable\"; import { sp } from \"@pnp/sp\"; // here a simple parser duplicating the functionality of the JSONParser const parser = new LambdaParser((r: Response) => r.json()); const webDataJson = await sp.web.get(parser); console.log(webDataJson);","title":"@pnp/queryable/parsers"},{"location":"v2/odata/parsers/#pnpqueryableparsers","text":"This modules contains a set of generic parsers. These can be used or extended as needed, though it is likely in most cases the default parser will be all you need.","title":"@pnp/queryable/parsers"},{"location":"v2/odata/parsers/#odatadefaultparser","text":"The simplest parser used to transform a Response into its JSON representation. The default parser will handle errors in a consistent manner throwing an HttpRequestError instance. This class extends Error and adds the response, status, and statusText properties. The response object is unread. You can use this custom error as shown below to gather more information about what went wrong in the request. import { sp } from \"@pnp/sp\"; import { JSONParser } from \"@pnp/queryable\"; try { const parser = new JSONParser(); // this always throws a 404 error await sp.web.getList(\"doesn't exist\").get(parser); } catch (e) { // we can check for the property \"isHttpRequestError\" to see if this is an instance of our class // this gets by all the many limitations of subclassing Error and type detection in JavaScript if (e.hasOwnProperty(\"isHttpRequestError\")) { console.log(\"e is HttpRequestError\"); // now we can access the various properties and make use of the response object. // at this point the body is unread console.log(`status: ${e.status}`); console.log(`statusText: ${e.statusText}`); const json = await e.response.clone().json(); console.log(JSON.stringify(json)); const text = await e.response.clone().text(); console.log(text); const headers = e.response.headers; } console.error(e); }","title":"ODataDefaultParser"},{"location":"v2/odata/parsers/#textparser","text":"Specialized parser used to parse the response using the .text() method with no other processing. Used primarily for files.","title":"TextParser"},{"location":"v2/odata/parsers/#blobparser","text":"Specialized parser used to parse the response using the .blob() method with no other processing. Used primarily for files.","title":"BlobParser"},{"location":"v2/odata/parsers/#jsonparser","text":"Specialized parser used to parse the response using the .json() method with no other processing. Used primarily for files.","title":"JSONParser"},{"location":"v2/odata/parsers/#bufferparser","text":"Specialized parser used to parse the response using the .arrayBuffer() [node] for .buffer() [browser] method with no other processing. Used primarily for files.","title":"BufferParser"},{"location":"v2/odata/parsers/#lambdaparser","text":"Allows you to pass in any handler function you want, called if the request does not result in an error that transforms the raw, unread request into the result type. import { LambdaParser } from \"@pnp/queryable\"; import { sp } from \"@pnp/sp\"; // here a simple parser duplicating the functionality of the JSONParser const parser = new LambdaParser((r: Response) => r.json()); const webDataJson = await sp.web.get(parser); console.log(webDataJson);","title":"LambdaParser"},{"location":"v2/odata/pipeline/","text":"@pnp/queryable/pipeline \u00b6 All of the odata requests processed by @pnp/queryable pass through an extensible request pipeline. Each request is executed in a specific request context defined by the RequestContext interface with the type parameter representing the type ultimately returned at the end a successful processing through the pipeline. Unless you are writing a pipeline method it is unlikely you will ever interact directly with the request pipeline. interface RequestContext \u00b6 The interface that defines the context within which all requests are executed. Note that the pipeline methods to be executed are part of the context. This allows full control over the methods called during a request, and allows for the insertion of any custom methods required. interface RequestContext { batch: ODataBatch; batchDependency: () => void; cachingOptions: ICachingOptions; hasResult?: boolean; isBatched: boolean; isCached: boolean; options: FetchOptions; parser: ODataParser; pipeline: Array<(c: RequestContext) => Promise>>; requestAbsoluteUrl: string; requestId: string; result?: T; verb: string; clientFactory: () => RequestClient; } requestPipelineMethod decorator \u00b6 The requestPipelineMethod decorator is used to tag a pipeline method and add functionality to bypass processing if a result is already present in the pipeline. If you would like your method to always run regardless of the existence of a result you can pass true to ensure it will always run. Each pipeline method takes a single argument of the current RequestContext and returns a promise resolving to the RequestContext updated as needed. @requestPipelineMethod(true) public static myPipelineMethod(context: RequestContext): Promise> { return new Promise>(resolve => { // do something resolve(context); }); } Default Pipeline \u00b6 logs the start of the request checks the cache for a value based on the context's cache settings sends the request if no value from found in the cache logs the end of the request","title":"@pnp/queryable/pipeline"},{"location":"v2/odata/pipeline/#pnpqueryablepipeline","text":"All of the odata requests processed by @pnp/queryable pass through an extensible request pipeline. Each request is executed in a specific request context defined by the RequestContext interface with the type parameter representing the type ultimately returned at the end a successful processing through the pipeline. Unless you are writing a pipeline method it is unlikely you will ever interact directly with the request pipeline.","title":"@pnp/queryable/pipeline"},{"location":"v2/odata/pipeline/#interface-requestcontextt","text":"The interface that defines the context within which all requests are executed. Note that the pipeline methods to be executed are part of the context. This allows full control over the methods called during a request, and allows for the insertion of any custom methods required. interface RequestContext { batch: ODataBatch; batchDependency: () => void; cachingOptions: ICachingOptions; hasResult?: boolean; isBatched: boolean; isCached: boolean; options: FetchOptions; parser: ODataParser; pipeline: Array<(c: RequestContext) => Promise>>; requestAbsoluteUrl: string; requestId: string; result?: T; verb: string; clientFactory: () => RequestClient; }","title":"interface RequestContext<T>"},{"location":"v2/odata/pipeline/#requestpipelinemethod-decorator","text":"The requestPipelineMethod decorator is used to tag a pipeline method and add functionality to bypass processing if a result is already present in the pipeline. If you would like your method to always run regardless of the existence of a result you can pass true to ensure it will always run. Each pipeline method takes a single argument of the current RequestContext and returns a promise resolving to the RequestContext updated as needed. @requestPipelineMethod(true) public static myPipelineMethod(context: RequestContext): Promise> { return new Promise>(resolve => { // do something resolve(context); }); }","title":"requestPipelineMethod decorator"},{"location":"v2/odata/pipeline/#default-pipeline","text":"logs the start of the request checks the cache for a value based on the context's cache settings sends the request if no value from found in the cache logs the end of the request","title":"Default Pipeline"},{"location":"v2/odata/queryable/","text":"@pnp/queryable/queryable \u00b6 The Queryable class is the base class for all of the libraries building fluent request apis. abstract class ODataQueryable \u00b6 This class takes a single type parameter representing the type of the batch implementation object. If your api will not support batching you can create a dummy class here and simply not use the batching calls. Properties \u00b6 query \u00b6 Provides access to the query string builder for this url Public Methods \u00b6 concat \u00b6 Directly concatenates the supplied string to the current url, not normalizing \"/\" chars configure \u00b6 Sets custom options for current object and all derived objects accessible via chaining import { ConfigOptions } from \"@pnp/queryable\"; import { sp } from \"@pnp/sp\"; const headers: ConfigOptions = { Accept: 'application/json;odata=nometadata' }; // here we use configure to set the headers value for all child requests of the list instance const list = sp.web.lists.getByTitle(\"List1\").configure({ headers }); // this will use the values set in configure list.items().then(items => console.log(JSON.stringify(items, null, 2)); For reference the ConfigOptions interface is shown below: export interface ConfigOptions { headers?: string[][] | { [key: string]: string } | Headers; mode?: \"navigate\" | \"same-origin\" | \"no-cors\" | \"cors\"; credentials?: \"omit\" | \"same-origin\" | \"include\"; cache?: \"default\" | \"no-store\" | \"reload\" | \"no-cache\" | \"force-cache\" | \"only-if-cached\"; } configureFrom \u00b6 Sets custom options from another queryable instance's options. Identical to configure except the options are derived from the supplied instance. usingCaching \u00b6 Enables caching for this request. See caching for more details. import { sp } from \"@pnp/sp\" sp.web.usingCaching()().then(...); inBatch \u00b6 Adds this query to the supplied batch toUrl \u00b6 Gets the current url abstract toUrlAndQuery() \u00b6 When implemented by an inheriting class will build the full url with appropriate query string used to make the actual request get \u00b6 Execute the current request. Takes an optional type parameter allowing for the typing of the value or the user of parsers that will create specific object instances.","title":"@pnp/queryable/queryable"},{"location":"v2/odata/queryable/#pnpqueryablequeryable","text":"The Queryable class is the base class for all of the libraries building fluent request apis.","title":"@pnp/queryable/queryable"},{"location":"v2/odata/queryable/#abstract-class-odataqueryablebatchtype-extends-odatabatch","text":"This class takes a single type parameter representing the type of the batch implementation object. If your api will not support batching you can create a dummy class here and simply not use the batching calls.","title":"abstract class ODataQueryable<BatchType extends ODataBatch>"},{"location":"v2/odata/queryable/#properties","text":"","title":"Properties"},{"location":"v2/odata/queryable/#query","text":"Provides access to the query string builder for this url","title":"query"},{"location":"v2/odata/queryable/#public-methods","text":"","title":"Public Methods"},{"location":"v2/odata/queryable/#concat","text":"Directly concatenates the supplied string to the current url, not normalizing \"/\" chars","title":"concat"},{"location":"v2/odata/queryable/#configure","text":"Sets custom options for current object and all derived objects accessible via chaining import { ConfigOptions } from \"@pnp/queryable\"; import { sp } from \"@pnp/sp\"; const headers: ConfigOptions = { Accept: 'application/json;odata=nometadata' }; // here we use configure to set the headers value for all child requests of the list instance const list = sp.web.lists.getByTitle(\"List1\").configure({ headers }); // this will use the values set in configure list.items().then(items => console.log(JSON.stringify(items, null, 2)); For reference the ConfigOptions interface is shown below: export interface ConfigOptions { headers?: string[][] | { [key: string]: string } | Headers; mode?: \"navigate\" | \"same-origin\" | \"no-cors\" | \"cors\"; credentials?: \"omit\" | \"same-origin\" | \"include\"; cache?: \"default\" | \"no-store\" | \"reload\" | \"no-cache\" | \"force-cache\" | \"only-if-cached\"; }","title":"configure"},{"location":"v2/odata/queryable/#configurefrom","text":"Sets custom options from another queryable instance's options. Identical to configure except the options are derived from the supplied instance.","title":"configureFrom"},{"location":"v2/odata/queryable/#usingcaching","text":"Enables caching for this request. See caching for more details. import { sp } from \"@pnp/sp\" sp.web.usingCaching()().then(...);","title":"usingCaching"},{"location":"v2/odata/queryable/#inbatch","text":"Adds this query to the supplied batch","title":"inBatch"},{"location":"v2/odata/queryable/#tourl","text":"Gets the current url","title":"toUrl"},{"location":"v2/odata/queryable/#abstract-tourlandquery","text":"When implemented by an inheriting class will build the full url with appropriate query string used to make the actual request","title":"abstract toUrlAndQuery()"},{"location":"v2/odata/queryable/#get","text":"Execute the current request. Takes an optional type parameter allowing for the typing of the value or the user of parsers that will create specific object instances.","title":"get"},{"location":"v2/pnpjs/","text":"PnPjs \u00b6 This package is a rollup package of all the other libraries for scenarios where you would prefer to access all of the code from a single file. Examples would be importing a single file into a script editor webpart or using the library in other ways that benefit from a single file. You will not be able to take advantage of selective imports using this bundle. Our recommendation is to import the packages directly into your project, or to create a custom bundle . This package is mostly provided to help folks with backward-compatibility needs. Script Editor Webpart \u00b6 The below is an example of using the pnp.js bundle within a Script Editor webpart. This script editor example is provided for folks on older version of SharePoint - when possible your first choice is SharePoint Framework. You will need to grab the pnp.js bundle file from the dist folder of the pnpjs package and upload it to a location where you can reference it from without your script editor webparts. *This is included as a reference for backward compatibility. The script editor webpart is no longer available in SharePoint online. In addition, see our General Statement on Polyfills and IE11
      Access Library Features \u00b6 Within the bundle all of the classes and methods are exported at the root object, with the exports from sp and graph libraries contained with NS variables to avoid naming conflicts. So if you need to access say the \"Web\" factory you can do so: const web = pnp.SPNS.Web(\"https://something.sharepoint.com\"); const lists = await web.lists(); pnp.GraphNS.* Individual libraries can also be accessed for their exports: pnp.Logger.subscribe(new pnp.ConsoleListener()); pnp.log.write(\"hello\");","title":"PnPjs"},{"location":"v2/pnpjs/#pnpjs","text":"This package is a rollup package of all the other libraries for scenarios where you would prefer to access all of the code from a single file. Examples would be importing a single file into a script editor webpart or using the library in other ways that benefit from a single file. You will not be able to take advantage of selective imports using this bundle. Our recommendation is to import the packages directly into your project, or to create a custom bundle . This package is mostly provided to help folks with backward-compatibility needs.","title":"PnPjs"},{"location":"v2/pnpjs/#script-editor-webpart","text":"The below is an example of using the pnp.js bundle within a Script Editor webpart. This script editor example is provided for folks on older version of SharePoint - when possible your first choice is SharePoint Framework. You will need to grab the pnp.js bundle file from the dist folder of the pnpjs package and upload it to a location where you can reference it from without your script editor webparts. *This is included as a reference for backward compatibility. The script editor webpart is no longer available in SharePoint online. In addition, see our General Statement on Polyfills and IE11
      ","title":"Script Editor Webpart"},{"location":"v2/pnpjs/#access-library-features","text":"Within the bundle all of the classes and methods are exported at the root object, with the exports from sp and graph libraries contained with NS variables to avoid naming conflicts. So if you need to access say the \"Web\" factory you can do so: const web = pnp.SPNS.Web(\"https://something.sharepoint.com\"); const lists = await web.lists(); pnp.GraphNS.* Individual libraries can also be accessed for their exports: pnp.Logger.subscribe(new pnp.ConsoleListener()); pnp.log.write(\"hello\");","title":"Access Library Features"},{"location":"v2/sp/","text":"@pnp/sp \u00b6 This package contains the fluent api used to call the SharePoint rest services. Getting Started \u00b6 Install the library and required dependencies npm install @pnp/sp --save Import the library into your application and access the root sp object import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; (function main() { // here we will load the current web's title const w = await sp.web.select(\"Title\")(); console.log(`Web Title: ${w.Title}`); )() Getting Started: SharePoint Framework \u00b6 Install the library and required dependencies npm install @pnp/sp --save Import the library into your application, update OnInit, and access the root sp object in render import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present sp.setup({ spfxContext: this.context }); }); } // ... public render(): void { // A simple loading message this.domElement.innerHTML = `Loading...`; const w = await sp.web.select(\"Title\")(); this.domElement.innerHTML = `Web Title: ${w.Title}`; } Getting Started: Nodejs \u00b6 Install the library and required dependencies npm install @pnp/sp @pnp/nodejs --save Import the library into your application, setup the node client, make a request import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { SPFetchClient } from \"@pnp/nodejs\"; // do this once per page load sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{your site url}\", \"{your client id}\", \"{your client secret}\"); }, }, }); // now make any calls you need using the configured client const w = await sp.web.select(\"Title\")(); console.log(`Web Title: ${w.Title}`);","title":"@pnp/sp"},{"location":"v2/sp/#pnpsp","text":"This package contains the fluent api used to call the SharePoint rest services.","title":"@pnp/sp"},{"location":"v2/sp/#getting-started","text":"Install the library and required dependencies npm install @pnp/sp --save Import the library into your application and access the root sp object import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; (function main() { // here we will load the current web's title const w = await sp.web.select(\"Title\")(); console.log(`Web Title: ${w.Title}`); )()","title":"Getting Started"},{"location":"v2/sp/#getting-started-sharepoint-framework","text":"Install the library and required dependencies npm install @pnp/sp --save Import the library into your application, update OnInit, and access the root sp object in render import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // ... public onInit(): Promise { return super.onInit().then(_ => { // other init code may be present sp.setup({ spfxContext: this.context }); }); } // ... public render(): void { // A simple loading message this.domElement.innerHTML = `Loading...`; const w = await sp.web.select(\"Title\")(); this.domElement.innerHTML = `Web Title: ${w.Title}`; }","title":"Getting Started: SharePoint Framework"},{"location":"v2/sp/#getting-started-nodejs","text":"Install the library and required dependencies npm install @pnp/sp @pnp/nodejs --save Import the library into your application, setup the node client, make a request import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { SPFetchClient } from \"@pnp/nodejs\"; // do this once per page load sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{your site url}\", \"{your client id}\", \"{your client secret}\"); }, }, }); // now make any calls you need using the configured client const w = await sp.web.select(\"Title\")(); console.log(`Web Title: ${w.Title}`);","title":"Getting Started: Nodejs"},{"location":"v2/sp/alias-parameters/","text":"@pnp/sp - Aliased Parameters \u00b6 Within the @pnp/sp api you can alias any of the parameters so they will be written into the querystring. This is most helpful if you are hitting up against the url length limits when working with files and folders. To alias a parameter you include the label name, a separator (\"::\") and the value in the string. You also need to prepend a \"!\" to the string to trigger the replacement. You can see this below, as well as the string that will be generated. Labels must start with a \"@\" followed by a letter. It is also your responsibility to ensure that the aliases you supply do not conflict, for example if you use \"@p1\" you should use \"@p2\" for a second parameter alias in the same query. Construct a parameter alias \u00b6 Pattern: !@{label name}::{value} Example: \"!@p1::\\sites\\dev\" or \"!@p2::\\text.txt\" Example without aliasing \u00b6 import { sp } from \"@pnp/sp/presets/all\"; // still works as expected, no aliasing const query = sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/\").files.select(\"Title\").top(3); console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files?$select=Title&$top=3 const r = await query(); console.log(r);; Example with aliasing \u00b6 import { sp } from \"@pnp/sp/presets/all\"; // same query with aliasing const query = sp.web.getFolderByServerRelativeUrl(\"!@p1::/sites/dev/Shared Documents/\").files.select(\"Title\").top(3); console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3 const r = await query(); console.log(r); Example with aliasing and batching \u00b6 Aliasing is supported with batching as well: import { sp } from \"@pnp/sp/presets/all\"; // same query with aliasing and batching const batch = sp.web.createBatch(); const query = sp.web.getFolderByServerRelativeUrl(\"!@p1::/sites/dev/Shared Documents/\").files.select(\"Title\").top(3); console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3 query.inBatch(batch)().then(r => { console.log(r); }); batch.execute();","title":"@pnp/sp - Aliased Parameters"},{"location":"v2/sp/alias-parameters/#pnpsp-aliased-parameters","text":"Within the @pnp/sp api you can alias any of the parameters so they will be written into the querystring. This is most helpful if you are hitting up against the url length limits when working with files and folders. To alias a parameter you include the label name, a separator (\"::\") and the value in the string. You also need to prepend a \"!\" to the string to trigger the replacement. You can see this below, as well as the string that will be generated. Labels must start with a \"@\" followed by a letter. It is also your responsibility to ensure that the aliases you supply do not conflict, for example if you use \"@p1\" you should use \"@p2\" for a second parameter alias in the same query.","title":"@pnp/sp - Aliased Parameters"},{"location":"v2/sp/alias-parameters/#construct-a-parameter-alias","text":"Pattern: !@{label name}::{value} Example: \"!@p1::\\sites\\dev\" or \"!@p2::\\text.txt\"","title":"Construct a parameter alias"},{"location":"v2/sp/alias-parameters/#example-without-aliasing","text":"import { sp } from \"@pnp/sp/presets/all\"; // still works as expected, no aliasing const query = sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/\").files.select(\"Title\").top(3); console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files?$select=Title&$top=3 const r = await query(); console.log(r);;","title":"Example without aliasing"},{"location":"v2/sp/alias-parameters/#example-with-aliasing","text":"import { sp } from \"@pnp/sp/presets/all\"; // same query with aliasing const query = sp.web.getFolderByServerRelativeUrl(\"!@p1::/sites/dev/Shared Documents/\").files.select(\"Title\").top(3); console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3 const r = await query(); console.log(r);","title":"Example with aliasing"},{"location":"v2/sp/alias-parameters/#example-with-aliasing-and-batching","text":"Aliasing is supported with batching as well: import { sp } from \"@pnp/sp/presets/all\"; // same query with aliasing and batching const batch = sp.web.createBatch(); const query = sp.web.getFolderByServerRelativeUrl(\"!@p1::/sites/dev/Shared Documents/\").files.select(\"Title\").top(3); console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3 query.inBatch(batch)().then(r => { console.log(r); }); batch.execute();","title":"Example with aliasing and batching"},{"location":"v2/sp/alm/","text":"@pnp/sp/appcatalog \u00b6 The ALM api allows you to manage app installations both in the tenant app catalog and individual site app catalogs. Some of the methods are still in beta and as such may change in the future. This article outlines how to call this api using @pnp/sp. Remember all these actions are bound by permissions so it is likely most users will not have the rights to perform these ALM actions. Understanding the App Catalog Hierarchy \u00b6 Before you begin provisioning applications it is important to understand the relationship between a local web catalog and the tenant app catalog. Some of the methods described below only work within the context of the tenant app catalog web, such as adding an app to the catalog and the app actions retract, remove, and deploy. You can install, uninstall, and upgrade an app in any web. Read more in the official documentation . Referencing an App Catalog \u00b6 There are several ways using @pnp/sp to get a reference to an app catalog. These methods are to provide you the greatest amount of flexibility in gaining access to the app catalog. Ultimately each method produces an AppCatalog instance differentiated only by the web to which it points. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/appcatalog\"; import \"@pnp/sp/webs\"; // get the current context web's app catalog const catalog = await sp.web.getAppCatalog()(); // you can also chain off the app catalog const apps = await sp.web.getAppCatalog()(); console.log(apps); import { sp } from \"@pnp/sp\"; import \"@pnp/sp/appcatalog\"; import \"@pnp/sp/webs\"; // you can get the tenant app catalog (or any app catalog) by using the getTenantAppCatalogWeb method const appCatWeb = await sp.getTenantAppCatalogWeb()(); const appCatalog = await appCatWeb.getAppCatalog()(); // you can get the tenant app catalog (or any app catalog) by passing in a url // get the tenant app catalog const tenantCatalog = await sp.web.getAppCatalog(\"https://mytenant.sharepoint.com/sites/appcatalog\")(); // get a different app catalog const catalog = await sp.web.getAppCatalog(\"https://mytenant.sharepoint.com/sites/anothersite\")(); // alternatively you can create a new app catalog instance directly by importing the AppCatalog class import { IAppCatalog, AppCatalog } from '@pnp/sp/appcatalog'; const catalog: IAppCatalog = await AppCatalog(\"https://mytenant.sharepoint.com/sites/apps\")(); // and finally you can combine use of the Web and AppCatalog classes to create an AppCatalog instance from an existing Web import { Web } from '@pnp/sp/webs'; import { AppCatalog } from '@pnp/sp/appcatalog'; const web = Web(\"https://mytenant.sharepoint.com/sites/apps\"); const catalog = await AppCatalog(web)(); The following examples make use of a variable \"catalog\" which is assumed to represent an AppCatalog instance obtained using one of the above methods, supporting code is omitted for brevity. List Available Apps \u00b6 The AppCatalog is itself a queryable collection so you can query this object directly to get a list of available apps. Also, the odata operators work on the catalog to sort, filter, and select. // get available apps await catalog(); // get available apps selecting two fields await catalog.select(\"Title\", \"Deployed\")(); Add an App \u00b6 This action must be performed in the context of the tenant app catalog // this represents the file bytes of the app package file const blob = new Blob(); // there is an optional third argument to control overwriting existing files const r = await catalog.add(\"myapp.app\", blob); // this is at its core a file add operation so you have access to the response data as well // as a File instance representing the created file console.log(JSON.stringify(r.data, null, 4)); // all file operations are available const nameData = await r.file.select(\"Name\")(); Get an App \u00b6 You can get the details of a single app by GUID id. This is also the branch point to perform specific app actions const app = await catalog.getAppById(\"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\")(); Perform app actions \u00b6 Remember: retract, deploy, and remove only work in the context of the tenant app catalog web. All of these methods return void and you can monitor success by wrapping the call in a try/catch block. const myAppId = \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\"; // deploy await catalog.getAppById(myAppId).deploy(); // retract await catalog.getAppById(myAppId).retract(); // install await catalog.getAppById(myAppId).install(); // uninstall await catalog.getAppById(myAppId).uninstall(); // upgrade await catalog.getAppById(myAppId).upgrade(); // remove await catalog.getAppById(myAppId).remove(); Synchronize a solution/app to the Microsoft Teams App Catalog \u00b6 By default this REST call requires the SharePoint item id of the app, not the app id. PnPjs will try to fetch the SharePoint item id by default. You can still use this the second parameter useSharePointItemId to pass your own item id in the first parameter id . // Using the app id await catalog.syncSolutionToTeams(\"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\"); // Using the SharePoint apps item id await catalog.syncSolutionToTeams(\"123\", true); Notes \u00b6 The app catalog is just a document library under the hood, so you can also perform non-ALM actions on the library if needed. But you should be aware of possible side-effects to the ALM life-cycle when doing so.","title":"@pnp/sp/appcatalog"},{"location":"v2/sp/alm/#pnpspappcatalog","text":"The ALM api allows you to manage app installations both in the tenant app catalog and individual site app catalogs. Some of the methods are still in beta and as such may change in the future. This article outlines how to call this api using @pnp/sp. Remember all these actions are bound by permissions so it is likely most users will not have the rights to perform these ALM actions.","title":"@pnp/sp/appcatalog"},{"location":"v2/sp/alm/#understanding-the-app-catalog-hierarchy","text":"Before you begin provisioning applications it is important to understand the relationship between a local web catalog and the tenant app catalog. Some of the methods described below only work within the context of the tenant app catalog web, such as adding an app to the catalog and the app actions retract, remove, and deploy. You can install, uninstall, and upgrade an app in any web. Read more in the official documentation .","title":"Understanding the App Catalog Hierarchy"},{"location":"v2/sp/alm/#referencing-an-app-catalog","text":"There are several ways using @pnp/sp to get a reference to an app catalog. These methods are to provide you the greatest amount of flexibility in gaining access to the app catalog. Ultimately each method produces an AppCatalog instance differentiated only by the web to which it points. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/appcatalog\"; import \"@pnp/sp/webs\"; // get the current context web's app catalog const catalog = await sp.web.getAppCatalog()(); // you can also chain off the app catalog const apps = await sp.web.getAppCatalog()(); console.log(apps); import { sp } from \"@pnp/sp\"; import \"@pnp/sp/appcatalog\"; import \"@pnp/sp/webs\"; // you can get the tenant app catalog (or any app catalog) by using the getTenantAppCatalogWeb method const appCatWeb = await sp.getTenantAppCatalogWeb()(); const appCatalog = await appCatWeb.getAppCatalog()(); // you can get the tenant app catalog (or any app catalog) by passing in a url // get the tenant app catalog const tenantCatalog = await sp.web.getAppCatalog(\"https://mytenant.sharepoint.com/sites/appcatalog\")(); // get a different app catalog const catalog = await sp.web.getAppCatalog(\"https://mytenant.sharepoint.com/sites/anothersite\")(); // alternatively you can create a new app catalog instance directly by importing the AppCatalog class import { IAppCatalog, AppCatalog } from '@pnp/sp/appcatalog'; const catalog: IAppCatalog = await AppCatalog(\"https://mytenant.sharepoint.com/sites/apps\")(); // and finally you can combine use of the Web and AppCatalog classes to create an AppCatalog instance from an existing Web import { Web } from '@pnp/sp/webs'; import { AppCatalog } from '@pnp/sp/appcatalog'; const web = Web(\"https://mytenant.sharepoint.com/sites/apps\"); const catalog = await AppCatalog(web)(); The following examples make use of a variable \"catalog\" which is assumed to represent an AppCatalog instance obtained using one of the above methods, supporting code is omitted for brevity.","title":"Referencing an App Catalog"},{"location":"v2/sp/alm/#list-available-apps","text":"The AppCatalog is itself a queryable collection so you can query this object directly to get a list of available apps. Also, the odata operators work on the catalog to sort, filter, and select. // get available apps await catalog(); // get available apps selecting two fields await catalog.select(\"Title\", \"Deployed\")();","title":"List Available Apps"},{"location":"v2/sp/alm/#add-an-app","text":"This action must be performed in the context of the tenant app catalog // this represents the file bytes of the app package file const blob = new Blob(); // there is an optional third argument to control overwriting existing files const r = await catalog.add(\"myapp.app\", blob); // this is at its core a file add operation so you have access to the response data as well // as a File instance representing the created file console.log(JSON.stringify(r.data, null, 4)); // all file operations are available const nameData = await r.file.select(\"Name\")();","title":"Add an App"},{"location":"v2/sp/alm/#get-an-app","text":"You can get the details of a single app by GUID id. This is also the branch point to perform specific app actions const app = await catalog.getAppById(\"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\")();","title":"Get an App"},{"location":"v2/sp/alm/#perform-app-actions","text":"Remember: retract, deploy, and remove only work in the context of the tenant app catalog web. All of these methods return void and you can monitor success by wrapping the call in a try/catch block. const myAppId = \"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\"; // deploy await catalog.getAppById(myAppId).deploy(); // retract await catalog.getAppById(myAppId).retract(); // install await catalog.getAppById(myAppId).install(); // uninstall await catalog.getAppById(myAppId).uninstall(); // upgrade await catalog.getAppById(myAppId).upgrade(); // remove await catalog.getAppById(myAppId).remove();","title":"Perform app actions"},{"location":"v2/sp/alm/#synchronize-a-solutionapp-to-the-microsoft-teams-app-catalog","text":"By default this REST call requires the SharePoint item id of the app, not the app id. PnPjs will try to fetch the SharePoint item id by default. You can still use this the second parameter useSharePointItemId to pass your own item id in the first parameter id . // Using the app id await catalog.syncSolutionToTeams(\"5137dff1-0b79-4ebc-8af4-ca01f7bd393c\"); // Using the SharePoint apps item id await catalog.syncSolutionToTeams(\"123\", true);","title":"Synchronize a solution/app to the Microsoft Teams App Catalog"},{"location":"v2/sp/alm/#notes","text":"The app catalog is just a document library under the hood, so you can also perform non-ALM actions on the library if needed. But you should be aware of possible side-effects to the ALM life-cycle when doing so.","title":"Notes"},{"location":"v2/sp/attachments/","text":"@pnp/sp/attachments \u00b6 The ability to attach file to list items allows users to track documents outside of a document library. You can use the PnP JS Core library to work with attachments as outlined below. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/attachments\"; Preset: All import { sp, IFeatures, Features } from \"@pnp/sp/presets/all\"; Get attachments \u00b6 import { sp } from \"@pnp/sp\"; import { IAttachmentInfo } from \"@pnp/sp/attachments\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); // get all the attachments const info: IAttachmentInfo[] = await item.attachmentFiles(); // get a single file by file name const info2: IAttachmentInfo = await item.attachmentFiles.getByName(\"file.txt\")(); // select specific properties using odata operators and use Pick to type the result const info3: Pick[] = await item.attachmentFiles.select(\"ServerRelativeUrl\")(); Add an Attachment \u00b6 You can add an attachment to a list item using the add method. This method takes either a string, Blob, or ArrayBuffer. import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.add(\"file2.txt\", \"Here is my content\"); Add Multiple \u00b6 This method allows you to pass an array of AttachmentFileInfo plain objects that will be added one at a time as attachments. Essentially automating the promise chaining. import { sp } from \"@pnp/sp\"; import { IList } from \"@pnp/sp/lists\"; import { IAttachmentFileInfo } from \"@pnp/sp/attachments\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const list: IList = sp.web.lists.getByTitle(\"MyList\"); let fileInfos: IAttachmentFileInfo[] = []; fileInfos.push({ name: \"My file name 1\", content: \"string, blob, or array\" }); fileInfos.push({ name: \"My file name 2\", content: \"string, blob, or array\" }); await list.items.getById(2).attachmentFiles.addMultiple(fileInfos); Delete Multiple \u00b6 import { sp } from \"@pnp/sp\"; import { IList } from \"./@pnp/sp/lists/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const list: IList = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(2).attachmentFiles.deleteMultiple(\"1.txt\", \"2.txt\"); Read Attachment Content \u00b6 You can read the content of an attachment as a string, Blob, ArrayBuffer, or json using the methods supplied. import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); const text = await item.attachmentFiles.getByName(\"file.txt\").getText(); // use this in the browser, does not work in nodejs const blob = await item.attachmentFiles.getByName(\"file.mp4\").getBlob(); // use this in nodejs const buffer = await item.attachmentFiles.getByName(\"file.mp4\").getBuffer(); // file must be valid json const json = await item.attachmentFiles.getByName(\"file.json\").getJSON(); Update Attachment Content \u00b6 You can also update the content of an attachment. This API is limited compared to the full file API - so if you need to upload large files consider using a document library. import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.getByName(\"file2.txt\").setContent(\"My new content!!!\"); Delete Attachment \u00b6 import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.getByName(\"file2.txt\").delete(); Recycle Attachment \u00b6 Delete the attachment and send it to recycle bin import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.getByName(\"file2.txt\").recycle(); Recycle Multiple Attachments \u00b6 Delete multiple attachments and send them to recycle bin import { sp } from \"@pnp/sp\"; import { IList } from \"@pnp/sp/lists/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const list: IList = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(2).attachmentFiles.recycleMultiple(\"1.txt\",\"2.txt\");","title":"@pnp/sp/attachments"},{"location":"v2/sp/attachments/#pnpspattachments","text":"The ability to attach file to list items allows users to track documents outside of a document library. You can use the PnP JS Core library to work with attachments as outlined below. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/attachments\"; Preset: All import { sp, IFeatures, Features } from \"@pnp/sp/presets/all\";","title":"@pnp/sp/attachments"},{"location":"v2/sp/attachments/#get-attachments","text":"import { sp } from \"@pnp/sp\"; import { IAttachmentInfo } from \"@pnp/sp/attachments\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); // get all the attachments const info: IAttachmentInfo[] = await item.attachmentFiles(); // get a single file by file name const info2: IAttachmentInfo = await item.attachmentFiles.getByName(\"file.txt\")(); // select specific properties using odata operators and use Pick to type the result const info3: Pick[] = await item.attachmentFiles.select(\"ServerRelativeUrl\")();","title":"Get attachments"},{"location":"v2/sp/attachments/#add-an-attachment","text":"You can add an attachment to a list item using the add method. This method takes either a string, Blob, or ArrayBuffer. import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.add(\"file2.txt\", \"Here is my content\");","title":"Add an Attachment"},{"location":"v2/sp/attachments/#add-multiple","text":"This method allows you to pass an array of AttachmentFileInfo plain objects that will be added one at a time as attachments. Essentially automating the promise chaining. import { sp } from \"@pnp/sp\"; import { IList } from \"@pnp/sp/lists\"; import { IAttachmentFileInfo } from \"@pnp/sp/attachments\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const list: IList = sp.web.lists.getByTitle(\"MyList\"); let fileInfos: IAttachmentFileInfo[] = []; fileInfos.push({ name: \"My file name 1\", content: \"string, blob, or array\" }); fileInfos.push({ name: \"My file name 2\", content: \"string, blob, or array\" }); await list.items.getById(2).attachmentFiles.addMultiple(fileInfos);","title":"Add Multiple"},{"location":"v2/sp/attachments/#delete-multiple","text":"import { sp } from \"@pnp/sp\"; import { IList } from \"./@pnp/sp/lists/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const list: IList = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(2).attachmentFiles.deleteMultiple(\"1.txt\", \"2.txt\");","title":"Delete Multiple"},{"location":"v2/sp/attachments/#read-attachment-content","text":"You can read the content of an attachment as a string, Blob, ArrayBuffer, or json using the methods supplied. import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); const text = await item.attachmentFiles.getByName(\"file.txt\").getText(); // use this in the browser, does not work in nodejs const blob = await item.attachmentFiles.getByName(\"file.mp4\").getBlob(); // use this in nodejs const buffer = await item.attachmentFiles.getByName(\"file.mp4\").getBuffer(); // file must be valid json const json = await item.attachmentFiles.getByName(\"file.json\").getJSON();","title":"Read Attachment Content"},{"location":"v2/sp/attachments/#update-attachment-content","text":"You can also update the content of an attachment. This API is limited compared to the full file API - so if you need to upload large files consider using a document library. import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.getByName(\"file2.txt\").setContent(\"My new content!!!\");","title":"Update Attachment Content"},{"location":"v2/sp/attachments/#delete-attachment","text":"import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.getByName(\"file2.txt\").delete();","title":"Delete Attachment"},{"location":"v2/sp/attachments/#recycle-attachment","text":"Delete the attachment and send it to recycle bin import { sp } from \"@pnp/sp\"; import { IItem } from \"@pnp/sp/items/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const item: IItem = sp.web.lists.getByTitle(\"MyList\").items.getById(1); await item.attachmentFiles.getByName(\"file2.txt\").recycle();","title":"Recycle Attachment"},{"location":"v2/sp/attachments/#recycle-multiple-attachments","text":"Delete multiple attachments and send them to recycle bin import { sp } from \"@pnp/sp\"; import { IList } from \"@pnp/sp/lists/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/attachments\"; const list: IList = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(2).attachmentFiles.recycleMultiple(\"1.txt\",\"2.txt\");","title":"Recycle Multiple Attachments"},{"location":"v2/sp/clientside-pages/","text":"@pnp/sp/clientside-pages \u00b6 The 'clientside-pages' module allows you to create, edit, and delete modern SharePoint pages. There are methods to update the page settings and add/remove client-side web parts. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { ClientsidePageFromFile, ClientsideText, ClientsideWebpartPropertyTypes, CreateClientsidePage, ClientsideWebpart, IClientsidePage } from \"@pnp/sp/clientside-pages\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/clientside-pages\"; Preset: All import { sp, ClientsidePageFromFile, ClientsideText, ClientsideWebpartPropertyTypes, CreateClientsidePage, ClientsideWebpart, IClientsidePage } from \"@pnp/sp/presets/all\"; Create a new Page \u00b6 You can create a new client-side page in several ways, all are equivalent. Create using IWeb.addClientsidePage \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; import { PromotedState } from \"@pnp/sp/clientside-pages\"; // Create a page providing a file name const page = await sp.web.addClientsidePage(\"mypage1\"); // ... other operations on the page as outlined below // the page is initially not published, you must publish it so it appears for others users await page.save(); // include title and page layout const page2 = await sp.web.addClientsidePage(\"mypage\", \"My Page Title\", \"Article\"); // you must publish the new page await page2.save(); // include title, page layout, and specifying the publishing status (Added in 2.0.4) const page3 = await sp.web.addClientsidePage(\"mypage\", \"My Page Title\", \"Article\", PromotedState.PromoteOnPublish); // you must publish the new page, after which the page will immediately be promoted to a news article await page3.save(); Create using CreateClientsidePage method \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { Web } from \"@pnp/sp/webs\"; import { CreateClientsidePage, PromotedState } from \"@pnp/sp/clientside-pages\"; const page1 = await CreateClientsidePage(sp.web, \"mypage2\", \"My Page Title\"); // you must publish the new page await page1.save(true); // specify the page layout type parameter const page2 = await CreateClientsidePage(sp.web, \"mypage3\", \"My Page Title\", \"Article\"); // you must publish the new page await page2.save(); // specify the page layout type parameter while also specifying the publishing status (Added in 2.0.4) const page2half = await CreateClientsidePage(sp.web, \"mypage3\", \"My Page Title\", \"Article\", PromotedState.PromoteOnPublish); // you must publish the new page, after which the page will immediately be promoted to a news article await page2half.save(); // use the web factory to create a page in a specific web const page3 = await CreateClientsidePage(Web(\"https://{absolute web url}\"), \"mypage4\", \"My Page Title\"); // you must publish the new page await page3.save(); Load Pages \u00b6 There are a few ways to load pages, each of which results in an IClientsidePage instance being returned. Load using IWeb.loadClientsidePage \u00b6 This method takes a server relative path to the page to load. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { Web } from \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; // use from the sp.web fluent chain const page = await sp.web.loadClientsidePage(\"/sites/dev/sitepages/mypage3.aspx\"); // use the web factory to target a specific web const page2 = await Web(\"https://{absolute web url}\").loadClientsidePage(\"/sites/dev/sitepages/mypage3.aspx\"); Load using ClientsidePageFromFile \u00b6 This method takes an IFile instance and loads an IClientsidePage instance. import { sp } from \"@pnp/sp\"; import { ClientsidePageFromFile } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files/web\"; const page = await ClientsidePageFromFile(sp.web.getFileByServerRelativePath(\"/sites/dev/sitepages/mypage3.aspx\")); Edit Sections and Columns \u00b6 Client-side pages are made up of sections, columns, and controls. Sections contain columns which contain controls. There are methods to operate on these within the page, in addition to the standard array methods available in JavaScript. These samples use a variable page that is understood to be an IClientsidePage instance which is either created or loaded as outlined in previous sections. // our page instance const page: IClientsidePage; // add two columns with factor 6 - this is a two column layout as the total factor in a section should add up to 12 const section1 = page.addSection(); section1.addColumn(6); section1.addColumn(6); // create a three column layout in a new section const section2 = page.addSection(); section2.addColumn(4); section2.addColumn(4); section2.addColumn(4); // publish our changes await page.save(); Manipulate Sections and Columns \u00b6 // our page instance const page: IClientsidePage; // drop all the columns in this section // this will also DELETE all controls contained in the columns page.sections[1].columns.length = 0; // create a new column layout page.sections[1].addColumn(4); page.sections[1].addColumn(8); // publish our changes await page.save(); Vertical Section \u00b6 The vertical section, if on the page, is stored within the sections array. However, you access it slightly differently to make things easier. // our page instance const page: IClientsidePage; // add or get a vertical section (handles case where section already exists) const vertSection = page.addVerticalSection(); // **************************************************************** // if you know or want to test if a vertical section is present: if (page.hasVerticalSection) { // access the vertical section (this method will NOT create the section if it does not exist) page.verticalSection.addControl(new ClientsideText(\"hello\")); } else { const vertSection = page.addVerticalSection(); vertSection.addControl(new ClientsideText(\"hello\")); } Reorder Sections \u00b6 // our page instance const page: IClientsidePage; // swap the order of two sections // this will preserve the controls within the columns page.sections = [page.sections[1], page.sections[0]]; // publish our changes await page.save(); Reorder Columns \u00b6 The sections and columns are arrays, so normal array operations work as expected // our page instance const page: IClientsidePage; // swap the order of two columns // this will preserve the controls within the columns page.sections[1].columns = [page.sections[1].columns[1], page.sections[1].columns[0]]; // publish our changes await page.save(); Clientside Controls \u00b6 Once you have your sections and columns defined you will want to add/edit controls within those columns. Add Text Content \u00b6 import { ClientsideText } from \"@pnp/sp/clientside-pages\"; // our page instance const page: IClientsidePage; page.addSection().addControl(new ClientsideText(\"@pnp/sp is a great library!\")); await page.save(); Add Controls \u00b6 Adding controls involves loading the available client-side part definitions from the server or creating a text part. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; import { ClientsideWebpart } from \"@pnp/sp/clientside-pages\"; // this will be a ClientsidePageComponent array // this can be cached on the client in production scenarios const partDefs = await sp.web.getClientsideWebParts(); // find the definition we want, here by id const partDef = partDefs.filter(c => c.Id === \"490d7c76-1824-45b2-9de3-676421c997fa\"); // optionally ensure you found the def if (partDef.length < 1) { // we didn't find it so we throw an error throw new Error(\"Could not find the web part\"); } // create a ClientWebPart instance from the definition const part = ClientsideWebpart.fromComponentDef(partDef[0]); // set the properties on the web part. Here for the embed web part we only have to supply an embedCode - in this case a YouTube video. // the structure of the properties varies for each web part and each version of a web part, so you will need to ensure you are setting // the properties correctly part.setProperties<{ embedCode: string }>({ embedCode: \"https://www.youtube.com/watch?v=IWQFZ7Lx-rg\", }); // we add that part to a new section page.addSection().addControl(part); await page.save(); Handle Different Webpart's Settings \u00b6 There are many ways that client side web parts are implemented and we can't provide handling within the library for all possibilities. This example shows how to handle a property set within the serverProcessedContent, in this case a List part's display title. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { ClientsideWebpart } from \"@pnp/sp/clientside-pages\"; // we create a class to wrap our functionality in a reusable way class ListWebpart extends ClientsideWebpart { constructor(control: ClientsideWebpart) { super((control).json); } // add property getter/setter for what we need, in this case \"listTitle\" within searchablePlainTexts public get DisplayTitle(): string { return this.json.webPartData?.serverProcessedContent?.searchablePlainTexts?.listTitle || \"\"; } public set DisplayTitle(value: string) { this.json.webPartData.serverProcessedContent.searchablePlainTexts.listTitle = value; } } // now we load our page const page = await sp.web.loadClientsidePage(\"/sites/dev/SitePages/List-Web-Part.aspx\"); // get our part and pass it to the constructor of our wrapper class const part = new ListWebpart(page.sections[0].columns[0].getControl(0)); part.DisplayTitle = \"My New Title!\"; await page.save(); Unfortunately each webpart can be authored differently, so there isn't a way to know how the setting for a given webpart are stored without loading it and examining the properties. Page Operations \u00b6 There are other operation you can perform on a page in addition to manipulating the content. pageLayout \u00b6 You can get and set the page layout. Changing the layout after creating the page may have side effects and should be done cautiously. // our page instance const page: IClientsidePage; // get the current value const value = page.pageLayout; // set the value page.pageLayout = \"Article\"; await page.save(); bannerImageUrl \u00b6 // our page instance const page: IClientsidePage; // get the current value const value = page.bannerImageUrl; // set the value page.bannerImageUrl = \"/server/relative/path/to/image.png\"; await page.save(); Banner images need to exist within the same site collection as the page where you want to use them. thumbnailUrl \u00b6 Allows you to set the thumbnail used for the page independently of the banner. If you set the bannerImageUrl property and not thumbnailUrl the thumbnail will be reset to match the banner, mimicking the UI functionality. // our page instance const page: IClientsidePage; // get the current value const value = page.thumbnailUrl; // set the value page.thumbnailUrl = \"/server/relative/path/to/image.png\"; await page.save(); topicHeader \u00b6 // our page instance const page: IClientsidePage; // get the current value const value = page.topicHeader; // set the value page.topicHeader = \"My cool header!\"; await page.save(); // clear the topic header and hide it page.topicHeader = \"\"; await page.save(); title \u00b6 // our page instance const page: IClientsidePage; // get the current value const value = page.title; // set the value page.title = \"My page title\"; await page.save(); description \u00b6 Descriptions are limited to 255 chars // our page instance const page: IClientsidePage; // get the current value const value = page.description; // set the value page.description = \"A description\"; await page.save(); layoutType \u00b6 Sets the layout type of the page. The valid values are: \"FullWidthImage\", \"NoImage\", \"ColorBlock\", \"CutInShape\" // our page instance const page: IClientsidePage; // get the current value const value = page.layoutType; // set the value page.layoutType = \"ColorBlock\"; await page.save(); headerTextAlignment \u00b6 Sets the header text alignment to one of \"Left\" or \"Center\" // our page instance const page: IClientsidePage; // get the current value const value = page.headerTextAlignment; // set the value page.headerTextAlignment = \"Center\"; await page.save(); showTopicHeader \u00b6 Sets if the topic header is displayed on a page. // our page instance const page: IClientsidePage; // get the current value const value = page.showTopicHeader; // show the header page.showTopicHeader = true; await page.save(); // hide the header page.showTopicHeader = false; await page.save(); showPublishDate \u00b6 Sets if the publish date is displayed on a page. // our page instance const page: IClientsidePage; // get the current value const value = page.showPublishDate; // show the date page.showPublishDate = true; await page.save(); // hide the date page.showPublishDate = false; await page.save(); Get / Set author details \u00b6 Added in 2.0.4 // our page instance const page: IClientsidePage; // get the author details (string | null) const value = page.authorByLine; // set the author by user id const user = await web.currentUser.select(\"Id\", \"LoginName\")(); const userId = user.Id; const userLogin = user.LoginName; await page.setAuthorById(userId); await page.save(); await page.setAuthorByLoginName(userLogin); await page.save(); you must still save the page after setting the author to persist your changes as shown in the example. load \u00b6 Loads the page from the server. This will overwrite any local unsaved changes. // our page instance const page: IClientsidePage; await page.load(); save \u00b6 Saves any changes to the page, optionally keeping them in draft state. // our page instance const page: IClientsidePage; // changes are published await page.save(); // changes remain in draft await page.save(false); discardPageCheckout \u00b6 Discards any current checkout of the page by the current user. // our page instance const page: IClientsidePage; await page.discardPageCheckout(); promoteToNews \u00b6 Promotes the page as a news article. // our page instance const page: IClientsidePage; await page.promoteToNews(); enableComments & disableComments \u00b6 Used to control the availability of comments on a page. // you need to import the comments sub-module or use the all preset import \"@pnp/sp/comments/clientside-page\"; // our page instance const page: IClientsidePage; // turn on comments await page.enableComments(); // turn off comments await page.disableComments(); findControlById \u00b6 Finds a control within the page by id. import { ClientsideText } from \"@pnp/sp/clientside-pages\"; // our page instance const page: IClientsidePage; const control = page.findControlById(\"06d4cdf6-bce6-4200-8b93-667a1b0a6c9d\"); // you can also type the control const control = page.findControlById(\"06d4cdf6-bce6-4200-8b93-667a1b0a6c9d\"); findControl \u00b6 Finds a control within the page using the supplied delegate. Can also be used to iterate through all controls in the page. // our page instance const page: IClientsidePage; // find the first control whose order is 9 const control = page.findControl((c) => c.order === 9); // iterate all the controls and output the id to the console page.findControl((c) => { console.log(c.id); return false; }); like & unlike \u00b6 Updates the page's like value for the current user. // our page instance const page: IClientsidePage; // like this page await page.like(); // unlike this page await page.unlike(); getLikedByInformation \u00b6 Gets the likes information for this page. // our page instance const page: IClientsidePage; const info = await page.getLikedByInformation(); copy \u00b6 Creates a copy of the page, including all controls. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // our page instance const page: IClientsidePage; // creates a published copy of the page const pageCopy = await page.copy(sp.web, \"newpagename\", \"New Page Title\"); // creates a draft (unpublished) copy of the page const pageCopy2 = await page.copy(sp.web, \"newpagename\", \"New Page Title\", false); // edits to pageCopy2 ... // publish the page pageCopy2.save(); copyTo \u00b6 Copies the contents of a page to another existing page instance. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // our page instances, loaded in any of the ways shown above const source: IClientsidePage; const target: IClientsidePage; const target2: IClientsidePage; // creates a published copy of the page await source.copyTo(target); // creates a draft (unpublished) copy of the page await source.copyTo(target2, false); // edits to target2... // publish the page target2.save(); setBannerImage \u00b6 Sets the banner image url and optionally additional properties. Allows you to set additional properties if needed, if you do not need to set the additional properties they are equivalent. Banner images need to exist within the same site collection as the page where you want to use them. // our page instance const page: IClientsidePage; page.setBannerImage(\"/server/relative/path/to/image.png\"); // save the changes await page.save(); // set additional props page.setBannerImage(\"/server/relative/path/to/image.png\", { altText: \"Image description\", imageSourceType: 2, translateX: 30, translateY: 1234, }); // save the changes await page.save(); This sample shows the full process of adding a page, image file, and setting the banner image in nodejs. The same code would work in a browser with an update on how you get the file - likely from a file input or similar. import { SPFetchClient } from \"@pnp/nodejs\"; import { join } from \"path\"; import { readFileSync } from \"fs\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/clientside-pages\"; // configure your node options sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{Site Url}\", \"{Client Id}\", \"{Client Secret}\"); }, }, }); // add the banner image const dirname = join(\"C:/path/to/file\", \"img-file.jpg\"); const file: Uint8Array = new Uint8Array(readFileSync(dirname)); const far = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents\").files.add(\"banner.jpg\", file, true); // add the page const page = await sp.web.addClientsidePage(\"MyPage\", \"Page Title\"); // set the banner image page.setBannerImage(far.data.ServerRelativeUrl); // publish the page await page.save(); setBannerImageFromExternalUrl \u00b6 Added in 2.0.12 Allows you to set the banner image from a source outside the current site collection. The image file will be copied to the SiteAssets library and referenced from there. // our page instance const page: IClientsidePage; // you must await this method await page.setBannerImageFromExternalUrl(\"https://absolute.url/to/my/image.jpg\"); // save the changes await page.save(); You can optionally supply additional props for the banner image, these match the properties when calling setBannerImage // our page instance const page: IClientsidePage; // you must await this method await page.setBannerImageFromExternalUrl(\"https://absolute.url/to/my/image.jpg\", { altText: \"Image description\", imageSourceType: 2, translateX: 30, translateY: 1234, }); // save the changes await page.save();","title":"@pnp/sp/clientside-pages"},{"location":"v2/sp/clientside-pages/#pnpspclientside-pages","text":"The 'clientside-pages' module allows you to create, edit, and delete modern SharePoint pages. There are methods to update the page settings and add/remove client-side web parts. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { ClientsidePageFromFile, ClientsideText, ClientsideWebpartPropertyTypes, CreateClientsidePage, ClientsideWebpart, IClientsidePage } from \"@pnp/sp/clientside-pages\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/clientside-pages\"; Preset: All import { sp, ClientsidePageFromFile, ClientsideText, ClientsideWebpartPropertyTypes, CreateClientsidePage, ClientsideWebpart, IClientsidePage } from \"@pnp/sp/presets/all\";","title":"@pnp/sp/clientside-pages"},{"location":"v2/sp/clientside-pages/#create-a-new-page","text":"You can create a new client-side page in several ways, all are equivalent.","title":"Create a new Page"},{"location":"v2/sp/clientside-pages/#create-using-iwebaddclientsidepage","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; import { PromotedState } from \"@pnp/sp/clientside-pages\"; // Create a page providing a file name const page = await sp.web.addClientsidePage(\"mypage1\"); // ... other operations on the page as outlined below // the page is initially not published, you must publish it so it appears for others users await page.save(); // include title and page layout const page2 = await sp.web.addClientsidePage(\"mypage\", \"My Page Title\", \"Article\"); // you must publish the new page await page2.save(); // include title, page layout, and specifying the publishing status (Added in 2.0.4) const page3 = await sp.web.addClientsidePage(\"mypage\", \"My Page Title\", \"Article\", PromotedState.PromoteOnPublish); // you must publish the new page, after which the page will immediately be promoted to a news article await page3.save();","title":"Create using IWeb.addClientsidePage"},{"location":"v2/sp/clientside-pages/#create-using-createclientsidepage-method","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { Web } from \"@pnp/sp/webs\"; import { CreateClientsidePage, PromotedState } from \"@pnp/sp/clientside-pages\"; const page1 = await CreateClientsidePage(sp.web, \"mypage2\", \"My Page Title\"); // you must publish the new page await page1.save(true); // specify the page layout type parameter const page2 = await CreateClientsidePage(sp.web, \"mypage3\", \"My Page Title\", \"Article\"); // you must publish the new page await page2.save(); // specify the page layout type parameter while also specifying the publishing status (Added in 2.0.4) const page2half = await CreateClientsidePage(sp.web, \"mypage3\", \"My Page Title\", \"Article\", PromotedState.PromoteOnPublish); // you must publish the new page, after which the page will immediately be promoted to a news article await page2half.save(); // use the web factory to create a page in a specific web const page3 = await CreateClientsidePage(Web(\"https://{absolute web url}\"), \"mypage4\", \"My Page Title\"); // you must publish the new page await page3.save();","title":"Create using CreateClientsidePage method"},{"location":"v2/sp/clientside-pages/#load-pages","text":"There are a few ways to load pages, each of which results in an IClientsidePage instance being returned.","title":"Load Pages"},{"location":"v2/sp/clientside-pages/#load-using-iwebloadclientsidepage","text":"This method takes a server relative path to the page to load. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { Web } from \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; // use from the sp.web fluent chain const page = await sp.web.loadClientsidePage(\"/sites/dev/sitepages/mypage3.aspx\"); // use the web factory to target a specific web const page2 = await Web(\"https://{absolute web url}\").loadClientsidePage(\"/sites/dev/sitepages/mypage3.aspx\");","title":"Load using IWeb.loadClientsidePage"},{"location":"v2/sp/clientside-pages/#load-using-clientsidepagefromfile","text":"This method takes an IFile instance and loads an IClientsidePage instance. import { sp } from \"@pnp/sp\"; import { ClientsidePageFromFile } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files/web\"; const page = await ClientsidePageFromFile(sp.web.getFileByServerRelativePath(\"/sites/dev/sitepages/mypage3.aspx\"));","title":"Load using ClientsidePageFromFile"},{"location":"v2/sp/clientside-pages/#edit-sections-and-columns","text":"Client-side pages are made up of sections, columns, and controls. Sections contain columns which contain controls. There are methods to operate on these within the page, in addition to the standard array methods available in JavaScript. These samples use a variable page that is understood to be an IClientsidePage instance which is either created or loaded as outlined in previous sections. // our page instance const page: IClientsidePage; // add two columns with factor 6 - this is a two column layout as the total factor in a section should add up to 12 const section1 = page.addSection(); section1.addColumn(6); section1.addColumn(6); // create a three column layout in a new section const section2 = page.addSection(); section2.addColumn(4); section2.addColumn(4); section2.addColumn(4); // publish our changes await page.save();","title":"Edit Sections and Columns"},{"location":"v2/sp/clientside-pages/#manipulate-sections-and-columns","text":"// our page instance const page: IClientsidePage; // drop all the columns in this section // this will also DELETE all controls contained in the columns page.sections[1].columns.length = 0; // create a new column layout page.sections[1].addColumn(4); page.sections[1].addColumn(8); // publish our changes await page.save();","title":"Manipulate Sections and Columns"},{"location":"v2/sp/clientside-pages/#vertical-section","text":"The vertical section, if on the page, is stored within the sections array. However, you access it slightly differently to make things easier. // our page instance const page: IClientsidePage; // add or get a vertical section (handles case where section already exists) const vertSection = page.addVerticalSection(); // **************************************************************** // if you know or want to test if a vertical section is present: if (page.hasVerticalSection) { // access the vertical section (this method will NOT create the section if it does not exist) page.verticalSection.addControl(new ClientsideText(\"hello\")); } else { const vertSection = page.addVerticalSection(); vertSection.addControl(new ClientsideText(\"hello\")); }","title":"Vertical Section"},{"location":"v2/sp/clientside-pages/#reorder-sections","text":"// our page instance const page: IClientsidePage; // swap the order of two sections // this will preserve the controls within the columns page.sections = [page.sections[1], page.sections[0]]; // publish our changes await page.save();","title":"Reorder Sections"},{"location":"v2/sp/clientside-pages/#reorder-columns","text":"The sections and columns are arrays, so normal array operations work as expected // our page instance const page: IClientsidePage; // swap the order of two columns // this will preserve the controls within the columns page.sections[1].columns = [page.sections[1].columns[1], page.sections[1].columns[0]]; // publish our changes await page.save();","title":"Reorder Columns"},{"location":"v2/sp/clientside-pages/#clientside-controls","text":"Once you have your sections and columns defined you will want to add/edit controls within those columns.","title":"Clientside Controls"},{"location":"v2/sp/clientside-pages/#add-text-content","text":"import { ClientsideText } from \"@pnp/sp/clientside-pages\"; // our page instance const page: IClientsidePage; page.addSection().addControl(new ClientsideText(\"@pnp/sp is a great library!\")); await page.save();","title":"Add Text Content"},{"location":"v2/sp/clientside-pages/#add-controls","text":"Adding controls involves loading the available client-side part definitions from the server or creating a text part. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; import { ClientsideWebpart } from \"@pnp/sp/clientside-pages\"; // this will be a ClientsidePageComponent array // this can be cached on the client in production scenarios const partDefs = await sp.web.getClientsideWebParts(); // find the definition we want, here by id const partDef = partDefs.filter(c => c.Id === \"490d7c76-1824-45b2-9de3-676421c997fa\"); // optionally ensure you found the def if (partDef.length < 1) { // we didn't find it so we throw an error throw new Error(\"Could not find the web part\"); } // create a ClientWebPart instance from the definition const part = ClientsideWebpart.fromComponentDef(partDef[0]); // set the properties on the web part. Here for the embed web part we only have to supply an embedCode - in this case a YouTube video. // the structure of the properties varies for each web part and each version of a web part, so you will need to ensure you are setting // the properties correctly part.setProperties<{ embedCode: string }>({ embedCode: \"https://www.youtube.com/watch?v=IWQFZ7Lx-rg\", }); // we add that part to a new section page.addSection().addControl(part); await page.save();","title":"Add Controls"},{"location":"v2/sp/clientside-pages/#handle-different-webparts-settings","text":"There are many ways that client side web parts are implemented and we can't provide handling within the library for all possibilities. This example shows how to handle a property set within the serverProcessedContent, in this case a List part's display title. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { ClientsideWebpart } from \"@pnp/sp/clientside-pages\"; // we create a class to wrap our functionality in a reusable way class ListWebpart extends ClientsideWebpart { constructor(control: ClientsideWebpart) { super((control).json); } // add property getter/setter for what we need, in this case \"listTitle\" within searchablePlainTexts public get DisplayTitle(): string { return this.json.webPartData?.serverProcessedContent?.searchablePlainTexts?.listTitle || \"\"; } public set DisplayTitle(value: string) { this.json.webPartData.serverProcessedContent.searchablePlainTexts.listTitle = value; } } // now we load our page const page = await sp.web.loadClientsidePage(\"/sites/dev/SitePages/List-Web-Part.aspx\"); // get our part and pass it to the constructor of our wrapper class const part = new ListWebpart(page.sections[0].columns[0].getControl(0)); part.DisplayTitle = \"My New Title!\"; await page.save(); Unfortunately each webpart can be authored differently, so there isn't a way to know how the setting for a given webpart are stored without loading it and examining the properties.","title":"Handle Different Webpart's Settings"},{"location":"v2/sp/clientside-pages/#page-operations","text":"There are other operation you can perform on a page in addition to manipulating the content.","title":"Page Operations"},{"location":"v2/sp/clientside-pages/#pagelayout","text":"You can get and set the page layout. Changing the layout after creating the page may have side effects and should be done cautiously. // our page instance const page: IClientsidePage; // get the current value const value = page.pageLayout; // set the value page.pageLayout = \"Article\"; await page.save();","title":"pageLayout"},{"location":"v2/sp/clientside-pages/#bannerimageurl","text":"// our page instance const page: IClientsidePage; // get the current value const value = page.bannerImageUrl; // set the value page.bannerImageUrl = \"/server/relative/path/to/image.png\"; await page.save(); Banner images need to exist within the same site collection as the page where you want to use them.","title":"bannerImageUrl"},{"location":"v2/sp/clientside-pages/#thumbnailurl","text":"Allows you to set the thumbnail used for the page independently of the banner. If you set the bannerImageUrl property and not thumbnailUrl the thumbnail will be reset to match the banner, mimicking the UI functionality. // our page instance const page: IClientsidePage; // get the current value const value = page.thumbnailUrl; // set the value page.thumbnailUrl = \"/server/relative/path/to/image.png\"; await page.save();","title":"thumbnailUrl"},{"location":"v2/sp/clientside-pages/#topicheader","text":"// our page instance const page: IClientsidePage; // get the current value const value = page.topicHeader; // set the value page.topicHeader = \"My cool header!\"; await page.save(); // clear the topic header and hide it page.topicHeader = \"\"; await page.save();","title":"topicHeader"},{"location":"v2/sp/clientside-pages/#title","text":"// our page instance const page: IClientsidePage; // get the current value const value = page.title; // set the value page.title = \"My page title\"; await page.save();","title":"title"},{"location":"v2/sp/clientside-pages/#description","text":"Descriptions are limited to 255 chars // our page instance const page: IClientsidePage; // get the current value const value = page.description; // set the value page.description = \"A description\"; await page.save();","title":"description"},{"location":"v2/sp/clientside-pages/#layouttype","text":"Sets the layout type of the page. The valid values are: \"FullWidthImage\", \"NoImage\", \"ColorBlock\", \"CutInShape\" // our page instance const page: IClientsidePage; // get the current value const value = page.layoutType; // set the value page.layoutType = \"ColorBlock\"; await page.save();","title":"layoutType"},{"location":"v2/sp/clientside-pages/#headertextalignment","text":"Sets the header text alignment to one of \"Left\" or \"Center\" // our page instance const page: IClientsidePage; // get the current value const value = page.headerTextAlignment; // set the value page.headerTextAlignment = \"Center\"; await page.save();","title":"headerTextAlignment"},{"location":"v2/sp/clientside-pages/#showtopicheader","text":"Sets if the topic header is displayed on a page. // our page instance const page: IClientsidePage; // get the current value const value = page.showTopicHeader; // show the header page.showTopicHeader = true; await page.save(); // hide the header page.showTopicHeader = false; await page.save();","title":"showTopicHeader"},{"location":"v2/sp/clientside-pages/#showpublishdate","text":"Sets if the publish date is displayed on a page. // our page instance const page: IClientsidePage; // get the current value const value = page.showPublishDate; // show the date page.showPublishDate = true; await page.save(); // hide the date page.showPublishDate = false; await page.save();","title":"showPublishDate"},{"location":"v2/sp/clientside-pages/#get-set-author-details","text":"Added in 2.0.4 // our page instance const page: IClientsidePage; // get the author details (string | null) const value = page.authorByLine; // set the author by user id const user = await web.currentUser.select(\"Id\", \"LoginName\")(); const userId = user.Id; const userLogin = user.LoginName; await page.setAuthorById(userId); await page.save(); await page.setAuthorByLoginName(userLogin); await page.save(); you must still save the page after setting the author to persist your changes as shown in the example.","title":"Get / Set author details"},{"location":"v2/sp/clientside-pages/#load","text":"Loads the page from the server. This will overwrite any local unsaved changes. // our page instance const page: IClientsidePage; await page.load();","title":"load"},{"location":"v2/sp/clientside-pages/#save","text":"Saves any changes to the page, optionally keeping them in draft state. // our page instance const page: IClientsidePage; // changes are published await page.save(); // changes remain in draft await page.save(false);","title":"save"},{"location":"v2/sp/clientside-pages/#discardpagecheckout","text":"Discards any current checkout of the page by the current user. // our page instance const page: IClientsidePage; await page.discardPageCheckout();","title":"discardPageCheckout"},{"location":"v2/sp/clientside-pages/#promotetonews","text":"Promotes the page as a news article. // our page instance const page: IClientsidePage; await page.promoteToNews();","title":"promoteToNews"},{"location":"v2/sp/clientside-pages/#enablecomments-disablecomments","text":"Used to control the availability of comments on a page. // you need to import the comments sub-module or use the all preset import \"@pnp/sp/comments/clientside-page\"; // our page instance const page: IClientsidePage; // turn on comments await page.enableComments(); // turn off comments await page.disableComments();","title":"enableComments & disableComments"},{"location":"v2/sp/clientside-pages/#findcontrolbyid","text":"Finds a control within the page by id. import { ClientsideText } from \"@pnp/sp/clientside-pages\"; // our page instance const page: IClientsidePage; const control = page.findControlById(\"06d4cdf6-bce6-4200-8b93-667a1b0a6c9d\"); // you can also type the control const control = page.findControlById(\"06d4cdf6-bce6-4200-8b93-667a1b0a6c9d\");","title":"findControlById"},{"location":"v2/sp/clientside-pages/#findcontrol","text":"Finds a control within the page using the supplied delegate. Can also be used to iterate through all controls in the page. // our page instance const page: IClientsidePage; // find the first control whose order is 9 const control = page.findControl((c) => c.order === 9); // iterate all the controls and output the id to the console page.findControl((c) => { console.log(c.id); return false; });","title":"findControl"},{"location":"v2/sp/clientside-pages/#like-unlike","text":"Updates the page's like value for the current user. // our page instance const page: IClientsidePage; // like this page await page.like(); // unlike this page await page.unlike();","title":"like & unlike"},{"location":"v2/sp/clientside-pages/#getlikedbyinformation","text":"Gets the likes information for this page. // our page instance const page: IClientsidePage; const info = await page.getLikedByInformation();","title":"getLikedByInformation"},{"location":"v2/sp/clientside-pages/#copy","text":"Creates a copy of the page, including all controls. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // our page instance const page: IClientsidePage; // creates a published copy of the page const pageCopy = await page.copy(sp.web, \"newpagename\", \"New Page Title\"); // creates a draft (unpublished) copy of the page const pageCopy2 = await page.copy(sp.web, \"newpagename\", \"New Page Title\", false); // edits to pageCopy2 ... // publish the page pageCopy2.save();","title":"copy"},{"location":"v2/sp/clientside-pages/#copyto","text":"Copies the contents of a page to another existing page instance. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; // our page instances, loaded in any of the ways shown above const source: IClientsidePage; const target: IClientsidePage; const target2: IClientsidePage; // creates a published copy of the page await source.copyTo(target); // creates a draft (unpublished) copy of the page await source.copyTo(target2, false); // edits to target2... // publish the page target2.save();","title":"copyTo"},{"location":"v2/sp/clientside-pages/#setbannerimage","text":"Sets the banner image url and optionally additional properties. Allows you to set additional properties if needed, if you do not need to set the additional properties they are equivalent. Banner images need to exist within the same site collection as the page where you want to use them. // our page instance const page: IClientsidePage; page.setBannerImage(\"/server/relative/path/to/image.png\"); // save the changes await page.save(); // set additional props page.setBannerImage(\"/server/relative/path/to/image.png\", { altText: \"Image description\", imageSourceType: 2, translateX: 30, translateY: 1234, }); // save the changes await page.save(); This sample shows the full process of adding a page, image file, and setting the banner image in nodejs. The same code would work in a browser with an update on how you get the file - likely from a file input or similar. import { SPFetchClient } from \"@pnp/nodejs\"; import { join } from \"path\"; import { readFileSync } from \"fs\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/clientside-pages\"; // configure your node options sp.setup({ sp: { fetchClientFactory: () => { return new SPFetchClient(\"{Site Url}\", \"{Client Id}\", \"{Client Secret}\"); }, }, }); // add the banner image const dirname = join(\"C:/path/to/file\", \"img-file.jpg\"); const file: Uint8Array = new Uint8Array(readFileSync(dirname)); const far = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents\").files.add(\"banner.jpg\", file, true); // add the page const page = await sp.web.addClientsidePage(\"MyPage\", \"Page Title\"); // set the banner image page.setBannerImage(far.data.ServerRelativeUrl); // publish the page await page.save();","title":"setBannerImage"},{"location":"v2/sp/clientside-pages/#setbannerimagefromexternalurl","text":"Added in 2.0.12 Allows you to set the banner image from a source outside the current site collection. The image file will be copied to the SiteAssets library and referenced from there. // our page instance const page: IClientsidePage; // you must await this method await page.setBannerImageFromExternalUrl(\"https://absolute.url/to/my/image.jpg\"); // save the changes await page.save(); You can optionally supply additional props for the banner image, these match the properties when calling setBannerImage // our page instance const page: IClientsidePage; // you must await this method await page.setBannerImageFromExternalUrl(\"https://absolute.url/to/my/image.jpg\", { altText: \"Image description\", imageSourceType: 2, translateX: 30, translateY: 1234, }); // save the changes await page.save();","title":"setBannerImageFromExternalUrl"},{"location":"v2/sp/column-defaults/","text":"@pnp/sp/column-defaults \u00b6 The column defaults sub-module allows you to manage the default column values on a library or library folder. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { IFieldDefault, IFieldDefaultProps, AllowedDefaultColumnValues } from \"@pnp/sp/column-defaults\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/column-defaults\"; Preset: All import { sp, IFieldDefault, IFieldDefaultProps, AllowedDefaultColumnValues } from \"@pnp/sp/presents/all\"; Get Folder Defaults \u00b6 You can get the default values for a specific folder as shown below: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/column-defaults\"; const defaults = await sp.web.getFolderByServerRelativePath(\"/sites/dev/DefaultColumnValues/fld_GHk5\").getDefaultColumnValues(); /* The resulting structure will have the form: [ { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues/fld_GHk5\", \"value\": \"{the default value}\" }, { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues/fld_GHk5\", \"value\": \"{the default value}\" } ] */ Set Folder Defaults \u00b6 When setting the defaults for a folder you need to include the field's internal name and the value. For more examples of other field types see the section Pattern for setting defaults on various column types Note: Be very careful when setting the path as the site collection url is case sensitive import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.getFolderByServerRelativePath(\"/sites/dev/DefaultColumnValues/fld_GHk5\").setDefaultColumnValues([{ name: \"TextField\", value: \"Something\", }, { name: \"NumberField\", value: 14, }]); Get Library Defaults \u00b6 You can also get all of the defaults for the entire library. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/column-defaults\"; const defaults = await sp.web.lists.getByTitle(\"DefaultColumnValues\").getDefaultColumnValues(); /* The resulting structure will have the form: [ { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues\", \"value\": \"{the default value}\" }, { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues/fld_GHk5\", \"value\": \"{a different default value}\" } ] */ Set Library Defaults \u00b6 You can also set the defaults for an entire library at once (root and all sub-folders). This may be helpful in provisioning a library or other scenarios. When setting the defaults for the entire library you must also include the path value with is the server relative path to the folder. When setting the defaults for a folder you need to include the field's internal name and the value. For more examples of other field types see the section Pattern for setting defaults on various column types Note: Be very careful when setting the path as the site collection url is case sensitive import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.lists.getByTitle(\"DefaultColumnValues\").setDefaultColumnValues([{ name: \"TextField\", path: \"/sites/dev/DefaultColumnValues\", value: \"#PnPjs Rocks!\", }]); Clear Folder Defaults \u00b6 If you want to clear all of the folder defaults you can use the clear method: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.getFolderByServerRelativePath(\"/sites/dev/DefaultColumnValues/fld_GHk5\").clearDefaultColumnValues(); Clear Library Defaults \u00b6 If you need to clear all of the default column values in a library you can pass an empty array to the list's setDefaultColumnValues method. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.lists.getByTitle(\"DefaultColumnValues\").setDefaultColumnValues([]); Pattern for setting defaults on various column types \u00b6 The following is an example of the structure for setting the default column value when using the setDefaultColumnValues that covers the various field types. [{ // Text/Boolean/CurrencyDateTime/Choice/User name: \"TextField\": path: \"/sites/dev/DefaultColumnValues\", value: \"#PnPjs Rocks!\", }, { //Number name: \"NumberField\", path: \"/sites/dev/DefaultColumnValues\", value: 42, }, { //Date name: \"NumberField\", path: \"/sites/dev/DefaultColumnValues\", value: \"1900-01-01T00:00:00Z\", }, { //Date - Today name: \"NumberField\", path: \"/sites/dev/DefaultColumnValues\", value: \"[today]\", }, { //MultiChoice name: \"MultiChoiceField\", path: \"/sites/dev/DefaultColumnValues\", value: [\"Item 1\", \"Item 2\"], }, { //MultiChoice - single value name: \"MultiChoiceField\", path: \"/sites/dev/DefaultColumnValues/folder2\", value: [\"Item 1\"], }, { //Taxonomy - single value name: \"TaxonomyField\", path: \"/sites/dev/DefaultColumnValues\", value: { wssId:\"-1\", termName: \"TaxValueName\", termId: \"924d2077-d5e3-4507-9f36-4a3655e74274\" } }, { //Taxonomy - multiple value name: \"TaxonomyMultiField\", path: \"/sites/dev/DefaultColumnValues\", value: [{ wssId:\"-1\", termName: \"TaxValueName\", termId: \"924d2077-d5e3-4507-9f36-4a3655e74274\" },{ wssId:\"-1\", termName: \"TaxValueName2\", termId: \"95d4c307-dde5-49d8-b861-392e145d94d3\" },] }]); Taxonomy Full Example \u00b6 This example shows fully how to get the taxonomy values and set them as a default column value using PnPjs. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/column-defaults\"; import \"@pnp/sp/taxonomy\"; // get the term's info we want to use as the default const term = await sp.termStore.sets.getById(\"ea6fc521-d293-4f3d-9e84-f3a5bc0936ce\").getTermById(\"775c9cf6-c3cd-4db9-8cfa-fc0aeefad93a\")(); // get the default term label const defLabel = term.labels.find(v => v.isDefault); // set the default value using -1, the term id, and the term's default label name await sp.web.lists.getByTitle(\"MetaDataDocLib\").rootFolder.setDefaultColumnValues([{ name: \"MetaDataColumnInternalName\", value: { wssId: \"-1\", termId: term.id, termName: defLabel.name, } }]) // check that the defaults have updated const newDefaults = await sp.web.lists.getByTitle(\"MetaDataDocLib\").getDefaultColumnValues();","title":"@pnp/sp/column-defaults"},{"location":"v2/sp/column-defaults/#pnpspcolumn-defaults","text":"The column defaults sub-module allows you to manage the default column values on a library or library folder. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { IFieldDefault, IFieldDefaultProps, AllowedDefaultColumnValues } from \"@pnp/sp/column-defaults\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/column-defaults\"; Preset: All import { sp, IFieldDefault, IFieldDefaultProps, AllowedDefaultColumnValues } from \"@pnp/sp/presents/all\";","title":"@pnp/sp/column-defaults"},{"location":"v2/sp/column-defaults/#get-folder-defaults","text":"You can get the default values for a specific folder as shown below: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/column-defaults\"; const defaults = await sp.web.getFolderByServerRelativePath(\"/sites/dev/DefaultColumnValues/fld_GHk5\").getDefaultColumnValues(); /* The resulting structure will have the form: [ { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues/fld_GHk5\", \"value\": \"{the default value}\" }, { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues/fld_GHk5\", \"value\": \"{the default value}\" } ] */","title":"Get Folder Defaults"},{"location":"v2/sp/column-defaults/#set-folder-defaults","text":"When setting the defaults for a folder you need to include the field's internal name and the value. For more examples of other field types see the section Pattern for setting defaults on various column types Note: Be very careful when setting the path as the site collection url is case sensitive import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.getFolderByServerRelativePath(\"/sites/dev/DefaultColumnValues/fld_GHk5\").setDefaultColumnValues([{ name: \"TextField\", value: \"Something\", }, { name: \"NumberField\", value: 14, }]);","title":"Set Folder Defaults"},{"location":"v2/sp/column-defaults/#get-library-defaults","text":"You can also get all of the defaults for the entire library. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/column-defaults\"; const defaults = await sp.web.lists.getByTitle(\"DefaultColumnValues\").getDefaultColumnValues(); /* The resulting structure will have the form: [ { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues\", \"value\": \"{the default value}\" }, { \"name\": \"{field internal name}\", \"path\": \"/sites/dev/DefaultColumnValues/fld_GHk5\", \"value\": \"{a different default value}\" } ] */","title":"Get Library Defaults"},{"location":"v2/sp/column-defaults/#set-library-defaults","text":"You can also set the defaults for an entire library at once (root and all sub-folders). This may be helpful in provisioning a library or other scenarios. When setting the defaults for the entire library you must also include the path value with is the server relative path to the folder. When setting the defaults for a folder you need to include the field's internal name and the value. For more examples of other field types see the section Pattern for setting defaults on various column types Note: Be very careful when setting the path as the site collection url is case sensitive import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.lists.getByTitle(\"DefaultColumnValues\").setDefaultColumnValues([{ name: \"TextField\", path: \"/sites/dev/DefaultColumnValues\", value: \"#PnPjs Rocks!\", }]);","title":"Set Library Defaults"},{"location":"v2/sp/column-defaults/#clear-folder-defaults","text":"If you want to clear all of the folder defaults you can use the clear method: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.getFolderByServerRelativePath(\"/sites/dev/DefaultColumnValues/fld_GHk5\").clearDefaultColumnValues();","title":"Clear Folder Defaults"},{"location":"v2/sp/column-defaults/#clear-library-defaults","text":"If you need to clear all of the default column values in a library you can pass an empty array to the list's setDefaultColumnValues method. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/column-defaults\"; await sp.web.lists.getByTitle(\"DefaultColumnValues\").setDefaultColumnValues([]);","title":"Clear Library Defaults"},{"location":"v2/sp/column-defaults/#pattern-for-setting-defaults-on-various-column-types","text":"The following is an example of the structure for setting the default column value when using the setDefaultColumnValues that covers the various field types. [{ // Text/Boolean/CurrencyDateTime/Choice/User name: \"TextField\": path: \"/sites/dev/DefaultColumnValues\", value: \"#PnPjs Rocks!\", }, { //Number name: \"NumberField\", path: \"/sites/dev/DefaultColumnValues\", value: 42, }, { //Date name: \"NumberField\", path: \"/sites/dev/DefaultColumnValues\", value: \"1900-01-01T00:00:00Z\", }, { //Date - Today name: \"NumberField\", path: \"/sites/dev/DefaultColumnValues\", value: \"[today]\", }, { //MultiChoice name: \"MultiChoiceField\", path: \"/sites/dev/DefaultColumnValues\", value: [\"Item 1\", \"Item 2\"], }, { //MultiChoice - single value name: \"MultiChoiceField\", path: \"/sites/dev/DefaultColumnValues/folder2\", value: [\"Item 1\"], }, { //Taxonomy - single value name: \"TaxonomyField\", path: \"/sites/dev/DefaultColumnValues\", value: { wssId:\"-1\", termName: \"TaxValueName\", termId: \"924d2077-d5e3-4507-9f36-4a3655e74274\" } }, { //Taxonomy - multiple value name: \"TaxonomyMultiField\", path: \"/sites/dev/DefaultColumnValues\", value: [{ wssId:\"-1\", termName: \"TaxValueName\", termId: \"924d2077-d5e3-4507-9f36-4a3655e74274\" },{ wssId:\"-1\", termName: \"TaxValueName2\", termId: \"95d4c307-dde5-49d8-b861-392e145d94d3\" },] }]);","title":"Pattern for setting defaults on various column types"},{"location":"v2/sp/column-defaults/#taxonomy-full-example","text":"This example shows fully how to get the taxonomy values and set them as a default column value using PnPjs. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/column-defaults\"; import \"@pnp/sp/taxonomy\"; // get the term's info we want to use as the default const term = await sp.termStore.sets.getById(\"ea6fc521-d293-4f3d-9e84-f3a5bc0936ce\").getTermById(\"775c9cf6-c3cd-4db9-8cfa-fc0aeefad93a\")(); // get the default term label const defLabel = term.labels.find(v => v.isDefault); // set the default value using -1, the term id, and the term's default label name await sp.web.lists.getByTitle(\"MetaDataDocLib\").rootFolder.setDefaultColumnValues([{ name: \"MetaDataColumnInternalName\", value: { wssId: \"-1\", termId: term.id, termName: defLabel.name, } }]) // check that the defaults have updated const newDefaults = await sp.web.lists.getByTitle(\"MetaDataDocLib\").getDefaultColumnValues();","title":"Taxonomy Full Example"},{"location":"v2/sp/comments-likes/","text":"@pnp/sp/comments and likes \u00b6 Comments can be accessed through either IItem or IClientsidePage instances, though in slightly different ways. For information on loading clientside pages or items please refer to those articles. These APIs are currently in BETA and are subject to change or may not work on all tenants. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/comments\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; ClientsidePage Comments \u00b6 The IClientsidePage interface has three methods to provide easier access to the comments for a page, without requiring that you load the item separately. Add Comments \u00b6 You can add a comment using the addComment method as shown import { CreateClientsidePage } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/comments/clientside-page\"; const page = await CreateClientsidePage(sp.web, \"mypage\", \"My Page Title\", \"Article\"); // optionally publish the page first await page.save(); const comment = await page.addComment(\"A test comment\"); Get Page Comments \u00b6 import { CreateClientsidePage } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/comments/clientside-page\"; const page = await CreateClientsidePage(sp.web, \"mypage\", \"My Page Title\", \"Article\"); // optionally publish the page first await page.save(); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); const comments = await page.getComments(); enableComments & disableComments \u00b6 Used to control the availability of comments on a page // you need to import the comments sub-module or use the all preset import \"@pnp/sp/comments/clientside-page\"; // our page instance const page: IClientsidePage; // turn on comments await page.enableComments(); // turn off comments await page.disableComments(); GetById \u00b6 import { CreateClientsidePage } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/comments/clientside-page\"; const page = await CreateClientsidePage(sp.web, \"mypage\", \"My Page Title\", \"Article\"); // optionally publish the page first await page.save(); const comment = await page.addComment(\"A test comment\"); const commentData = await page.getCommentById(parseInt(comment.id, 10)); Clear Comments \u00b6 Item Comments \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/comments/item\"; const item = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/SitePages/Test_8q5L.aspx\").getItem(); // as an example, or any of the below options await item.like(); The below examples use a variable named \"item\" which is taken to represent an IItem instance. Comments \u00b6 Get Item Comments \u00b6 const comments = await item.comments(); You can also get the comments merged with instances of the Comment class to immediately start accessing the properties and methods: import { spODataEntityArray } from \"@pnp/sp/odata\"; import { Comment, ICommentData } from \"@pnp/sp/comments\"; const comments = await item.comments(spODataEntityArray(Comment)); // these will be Comment instances in the array comments[0].replies.add({ text: \"#PnPjs is pretty ok!\" }); //load the top 20 replies and comments for an item including likedBy information const comments = await item.comments.expand(\"replies\", \"likedBy\", \"replies/likedBy\").top(20)(); Add Comment \u00b6 // you can add a comment as a string item.comments.add(\"string comment\"); // or you can add it as an object to include mentions item.comments.add({ text: \"comment from object property\" }); Delete a Comment \u00b6 import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); // these will be Comment instances in the array comments[0].delete() Like Comment \u00b6 import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); // these will be Comment instances in the array comments[0].like() Unlike Comment \u00b6 import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); comments[0].unlike() Reply to a Comment \u00b6 import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); const comment: Comment & CommentData = await comments[0].replies.add({ text: \"#PnPjs is pretty ok!\" }); Load Replies to a Comment \u00b6 import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); const replies = await comments[0].replies(); Like \u00b6 You can like/unlike client-side pages, items, and comments on items. See above for how to like or unlike a comment. Below you can see how to like and unlike an items, as well as get the liked by data. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/comments/item\"; import { ILikeData, ILikedByInformation } from \"@pnp/sp/comments\"; // like an item await item.like(); // unlike an item await item.unlike(); // get the liked by data const likedByData: ILikeData[] = await item.getLikedBy(); // get the liked by information const likedByInfo: ILikedByInformation = await item.getLikedByInformation(); To like/unlike a client-side page and get liked by information. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/comments/clientside-page\"; import { ILikedByInformation } from \"@pnp/sp/comments\"; // like a page await page.like(); // unlike a page await page.unlike(); // get the liked by information const likedByInfo: ILikedByInformation = await page.getLikedByInformation();","title":"@pnp/sp/comments and likes"},{"location":"v2/sp/comments-likes/#pnpspcomments-and-likes","text":"Comments can be accessed through either IItem or IClientsidePage instances, though in slightly different ways. For information on loading clientside pages or items please refer to those articles. These APIs are currently in BETA and are subject to change or may not work on all tenants. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/comments\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"@pnp/sp/comments and likes"},{"location":"v2/sp/comments-likes/#clientsidepage-comments","text":"The IClientsidePage interface has three methods to provide easier access to the comments for a page, without requiring that you load the item separately.","title":"ClientsidePage Comments"},{"location":"v2/sp/comments-likes/#add-comments","text":"You can add a comment using the addComment method as shown import { CreateClientsidePage } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/comments/clientside-page\"; const page = await CreateClientsidePage(sp.web, \"mypage\", \"My Page Title\", \"Article\"); // optionally publish the page first await page.save(); const comment = await page.addComment(\"A test comment\");","title":"Add Comments"},{"location":"v2/sp/comments-likes/#get-page-comments","text":"import { CreateClientsidePage } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/comments/clientside-page\"; const page = await CreateClientsidePage(sp.web, \"mypage\", \"My Page Title\", \"Article\"); // optionally publish the page first await page.save(); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); await page.addComment(\"A test comment\"); const comments = await page.getComments();","title":"Get Page Comments"},{"location":"v2/sp/comments-likes/#enablecomments-disablecomments","text":"Used to control the availability of comments on a page // you need to import the comments sub-module or use the all preset import \"@pnp/sp/comments/clientside-page\"; // our page instance const page: IClientsidePage; // turn on comments await page.enableComments(); // turn off comments await page.disableComments();","title":"enableComments & disableComments"},{"location":"v2/sp/comments-likes/#getbyid","text":"import { CreateClientsidePage } from \"@pnp/sp/clientside-pages\"; import \"@pnp/sp/comments/clientside-page\"; const page = await CreateClientsidePage(sp.web, \"mypage\", \"My Page Title\", \"Article\"); // optionally publish the page first await page.save(); const comment = await page.addComment(\"A test comment\"); const commentData = await page.getCommentById(parseInt(comment.id, 10));","title":"GetById"},{"location":"v2/sp/comments-likes/#clear-comments","text":"","title":"Clear Comments"},{"location":"v2/sp/comments-likes/#item-comments","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files/web\"; import \"@pnp/sp/items\"; import \"@pnp/sp/comments/item\"; const item = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/SitePages/Test_8q5L.aspx\").getItem(); // as an example, or any of the below options await item.like(); The below examples use a variable named \"item\" which is taken to represent an IItem instance.","title":"Item Comments"},{"location":"v2/sp/comments-likes/#comments","text":"","title":"Comments"},{"location":"v2/sp/comments-likes/#get-item-comments","text":"const comments = await item.comments(); You can also get the comments merged with instances of the Comment class to immediately start accessing the properties and methods: import { spODataEntityArray } from \"@pnp/sp/odata\"; import { Comment, ICommentData } from \"@pnp/sp/comments\"; const comments = await item.comments(spODataEntityArray(Comment)); // these will be Comment instances in the array comments[0].replies.add({ text: \"#PnPjs is pretty ok!\" }); //load the top 20 replies and comments for an item including likedBy information const comments = await item.comments.expand(\"replies\", \"likedBy\", \"replies/likedBy\").top(20)();","title":"Get Item Comments"},{"location":"v2/sp/comments-likes/#add-comment","text":"// you can add a comment as a string item.comments.add(\"string comment\"); // or you can add it as an object to include mentions item.comments.add({ text: \"comment from object property\" });","title":"Add Comment"},{"location":"v2/sp/comments-likes/#delete-a-comment","text":"import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); // these will be Comment instances in the array comments[0].delete()","title":"Delete a Comment"},{"location":"v2/sp/comments-likes/#like-comment","text":"import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); // these will be Comment instances in the array comments[0].like()","title":"Like Comment"},{"location":"v2/sp/comments-likes/#unlike-comment","text":"import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); comments[0].unlike()","title":"Unlike Comment"},{"location":"v2/sp/comments-likes/#reply-to-a-comment","text":"import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); const comment: Comment & CommentData = await comments[0].replies.add({ text: \"#PnPjs is pretty ok!\" });","title":"Reply to a Comment"},{"location":"v2/sp/comments-likes/#load-replies-to-a-comment","text":"import { spODataEntityArray, Comment, CommentData } from \"@pnp/sp\"; const comments = await item.comments(spODataEntityArray(Comment)); const replies = await comments[0].replies();","title":"Load Replies to a Comment"},{"location":"v2/sp/comments-likes/#like","text":"You can like/unlike client-side pages, items, and comments on items. See above for how to like or unlike a comment. Below you can see how to like and unlike an items, as well as get the liked by data. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/comments/item\"; import { ILikeData, ILikedByInformation } from \"@pnp/sp/comments\"; // like an item await item.like(); // unlike an item await item.unlike(); // get the liked by data const likedByData: ILikeData[] = await item.getLikedBy(); // get the liked by information const likedByInfo: ILikedByInformation = await item.getLikedByInformation(); To like/unlike a client-side page and get liked by information. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/comments/clientside-page\"; import { ILikedByInformation } from \"@pnp/sp/comments\"; // like a page await page.like(); // unlike a page await page.unlike(); // get the liked by information const likedByInfo: ILikedByInformation = await page.getLikedByInformation();","title":"Like"},{"location":"v2/sp/content-types/","text":"@pnp/sp/content-types \u00b6 Content Types are used to define sets of columns in SharePoint. IContentTypes \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import { ContentTypes, IContentTypes } from \"@pnp/sp/content-types\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/content-types\"; Preset: All import { sp, ContentTypes, IContentTypes } from \"@pnp/sp/presets/all\"; Add an existing Content Type to a collection \u00b6 The following example shows how to add the built in Picture Content Type to the Documents library. sp.web.lists.getByTitle(\"Documents\").contentTypes.addAvailableContentType(\"0x010102\"); Get a Content Type by Id \u00b6 const d: IContentType = await sp.web.contentTypes.getById(\"0x01\")(); // log content type name to console console.log(d.name); Add a new Content Type \u00b6 To add a new Content Type to a collection, parameters id and name are required. For more information on creating content type IDs reference the Microsoft Documentation . While this documentation references SharePoint 2010 the structure of the IDs has not changed. sp.web.contentTypes.add(\"0x01008D19F38845B0884EBEBE239FDF359184\", \"My Content Type\"); It is also possible to provide a description and group parameter. For other settings, we can use the parameter named 'additionalSettings' which is a TypedHash, meaning you can send whatever properties you'd like in the body (provided that the property is supported by the SharePoint API). //Adding a content type with id, name, description, group and setting it to read only mode (using additionalsettings) sp.web.contentTypes.add(\"0x01008D19F38845B0884EBEBE239FDF359184\", \"My Content Type\", \"This is my content type.\", \"_PnP Content Types\", { ReadOnly: true }); IContentType \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { ContentType, IContentType } from \"@pnp/sp/content-types\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/content-types\"; Preset: All import { sp, ContentType, IContentType } from \"@pnp/sp/presets/all\"; Get the field links \u00b6 Use this method to get a collection containing all the field links (SP.FieldLink) for a Content Type. // get field links from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").fieldLinks(); // log collection of fieldlinks to console console.log(d); Get Content Type fields \u00b6 To get a collection with all fields on the Content Type, simply use this method. // get fields from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").fields(); // log collection of fields to console console.log(d); Get parent Content Type \u00b6 // get parent Content Type from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").parent(); // log name of parent Content Type to console console.log(d.Name) Get Content Type Workflow associations \u00b6 // get workflow associations from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").workflowAssociations(); // log collection of workflow associations to console console.log(d);","title":"@pnp/sp/content-types"},{"location":"v2/sp/content-types/#pnpspcontent-types","text":"Content Types are used to define sets of columns in SharePoint.","title":"@pnp/sp/content-types"},{"location":"v2/sp/content-types/#icontenttypes","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import { ContentTypes, IContentTypes } from \"@pnp/sp/content-types\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/content-types\"; Preset: All import { sp, ContentTypes, IContentTypes } from \"@pnp/sp/presets/all\";","title":"IContentTypes"},{"location":"v2/sp/content-types/#add-an-existing-content-type-to-a-collection","text":"The following example shows how to add the built in Picture Content Type to the Documents library. sp.web.lists.getByTitle(\"Documents\").contentTypes.addAvailableContentType(\"0x010102\");","title":"Add an existing Content Type to a collection"},{"location":"v2/sp/content-types/#get-a-content-type-by-id","text":"const d: IContentType = await sp.web.contentTypes.getById(\"0x01\")(); // log content type name to console console.log(d.name);","title":"Get a Content Type by Id"},{"location":"v2/sp/content-types/#add-a-new-content-type","text":"To add a new Content Type to a collection, parameters id and name are required. For more information on creating content type IDs reference the Microsoft Documentation . While this documentation references SharePoint 2010 the structure of the IDs has not changed. sp.web.contentTypes.add(\"0x01008D19F38845B0884EBEBE239FDF359184\", \"My Content Type\"); It is also possible to provide a description and group parameter. For other settings, we can use the parameter named 'additionalSettings' which is a TypedHash, meaning you can send whatever properties you'd like in the body (provided that the property is supported by the SharePoint API). //Adding a content type with id, name, description, group and setting it to read only mode (using additionalsettings) sp.web.contentTypes.add(\"0x01008D19F38845B0884EBEBE239FDF359184\", \"My Content Type\", \"This is my content type.\", \"_PnP Content Types\", { ReadOnly: true });","title":"Add a new Content Type"},{"location":"v2/sp/content-types/#icontenttype","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { ContentType, IContentType } from \"@pnp/sp/content-types\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/content-types\"; Preset: All import { sp, ContentType, IContentType } from \"@pnp/sp/presets/all\";","title":"IContentType"},{"location":"v2/sp/content-types/#get-the-field-links","text":"Use this method to get a collection containing all the field links (SP.FieldLink) for a Content Type. // get field links from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").fieldLinks(); // log collection of fieldlinks to console console.log(d);","title":"Get the field links"},{"location":"v2/sp/content-types/#get-content-type-fields","text":"To get a collection with all fields on the Content Type, simply use this method. // get fields from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").fields(); // log collection of fields to console console.log(d);","title":"Get Content Type fields"},{"location":"v2/sp/content-types/#get-parent-content-type","text":"// get parent Content Type from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").parent(); // log name of parent Content Type to console console.log(d.Name)","title":"Get parent Content Type"},{"location":"v2/sp/content-types/#get-content-type-workflow-associations","text":"// get workflow associations from built in Content Type Document (Id: \"0x0101\") const d = await sp.web.contentTypes.getById(\"0x0101\").workflowAssociations(); // log collection of workflow associations to console console.log(d);","title":"Get Content Type Workflow associations"},{"location":"v2/sp/custom-irequestclient/","text":"Custom IRequestClient \u00b6 Scenario: You have some special requirements involving auth scenarios or other needs that the library can't directly support. You may need to create a custom IRequestClient implementation to meet those needs as we can't customize the library to handle every case. This article walks you through how to create a custom IRequestClient and register it for use by the library. It is very unlikely this is a step you ever need to take and we encourage you to ask a question in the issues list before going down this path. Create the Client \u00b6 The easiest way to create a new IRequestClient is to subclass the existing SPHttpClient. You can always write a full client from scratch so long as it supports the IRequestClient interface but you need to handle all of the logic for retry, headers, and the request digest. Here we show implementing a client to solve the need discussed in pull request 1264 as an example. // we subclass SPHttpClient class CustomSPHttpClient extends SPHttpClient { // optionally add a constructor, done here as an example constructor(impl?: IHttpClientImpl) { super(impl); } // override the fetchRaw method to ensure we always include the credentials = \"include\" option // you could also override fetch, but fetchRaw ensures no matter what all requests get your custom logic is applied public fetchRaw(url: string, options?: IFetchOptions): Promise { options.credentials = \"include\"; return super.fetchRaw(url, options); } } The final step is to register the custom client with the library so it is used instead of the default. For that we import the registerCustomRequestClientFactory function and call it before our request generating code. You can reset to the default client factory by passing null to this same function. import { sp, registerCustomRequestClientFactory } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; registerCustomRequestClientFactory(() => new CustomSPHttpClient()); // configure your other options sp.setup({ // ... }); // this request will be executed through your custom client const w = await sp.web(); Unregister Custom Client \u00b6 // unregister custom client factory registerCustomRequestClientFactory(null); IRequestClient Interface \u00b6 If you want to 100% roll your own client you need to implement the below interface, found in common. import { IRequestClient } from \"@pnp/core\"; export interface IRequestClient { fetch(url: string, options?: IFetchOptions): Promise; fetchRaw(url: string, options?: IFetchOptions): Promise; get(url: string, options?: IFetchOptions): Promise; post(url: string, options?: IFetchOptions): Promise; patch(url: string, options?: IFetchOptions): Promise; delete(url: string, options?: IFetchOptions): Promise; } Supportability Note \u00b6 We cannot provide support for your custom client implementation, and creating your own client assumes an intimate knowledge of how SharePoint requests work. Again, this is very likely something you will never need to do - and we recommend exhausting all other options before taking this route.","title":"Custom IRequestClient"},{"location":"v2/sp/custom-irequestclient/#custom-irequestclient","text":"Scenario: You have some special requirements involving auth scenarios or other needs that the library can't directly support. You may need to create a custom IRequestClient implementation to meet those needs as we can't customize the library to handle every case. This article walks you through how to create a custom IRequestClient and register it for use by the library. It is very unlikely this is a step you ever need to take and we encourage you to ask a question in the issues list before going down this path.","title":"Custom IRequestClient"},{"location":"v2/sp/custom-irequestclient/#create-the-client","text":"The easiest way to create a new IRequestClient is to subclass the existing SPHttpClient. You can always write a full client from scratch so long as it supports the IRequestClient interface but you need to handle all of the logic for retry, headers, and the request digest. Here we show implementing a client to solve the need discussed in pull request 1264 as an example. // we subclass SPHttpClient class CustomSPHttpClient extends SPHttpClient { // optionally add a constructor, done here as an example constructor(impl?: IHttpClientImpl) { super(impl); } // override the fetchRaw method to ensure we always include the credentials = \"include\" option // you could also override fetch, but fetchRaw ensures no matter what all requests get your custom logic is applied public fetchRaw(url: string, options?: IFetchOptions): Promise { options.credentials = \"include\"; return super.fetchRaw(url, options); } } The final step is to register the custom client with the library so it is used instead of the default. For that we import the registerCustomRequestClientFactory function and call it before our request generating code. You can reset to the default client factory by passing null to this same function. import { sp, registerCustomRequestClientFactory } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; registerCustomRequestClientFactory(() => new CustomSPHttpClient()); // configure your other options sp.setup({ // ... }); // this request will be executed through your custom client const w = await sp.web();","title":"Create the Client"},{"location":"v2/sp/custom-irequestclient/#unregister-custom-client","text":"// unregister custom client factory registerCustomRequestClientFactory(null);","title":"Unregister Custom Client"},{"location":"v2/sp/custom-irequestclient/#irequestclient-interface","text":"If you want to 100% roll your own client you need to implement the below interface, found in common. import { IRequestClient } from \"@pnp/core\"; export interface IRequestClient { fetch(url: string, options?: IFetchOptions): Promise; fetchRaw(url: string, options?: IFetchOptions): Promise; get(url: string, options?: IFetchOptions): Promise; post(url: string, options?: IFetchOptions): Promise; patch(url: string, options?: IFetchOptions): Promise; delete(url: string, options?: IFetchOptions): Promise; }","title":"IRequestClient Interface"},{"location":"v2/sp/custom-irequestclient/#supportability-note","text":"We cannot provide support for your custom client implementation, and creating your own client assumes an intimate knowledge of how SharePoint requests work. Again, this is very likely something you will never need to do - and we recommend exhausting all other options before taking this route.","title":"Supportability Note"},{"location":"v2/sp/entity-merging/","text":"@pnp/sp - entity merging \u00b6 Sometimes when we make a query entity's data we would like then to immediately run other commands on the returned entity. To have data returned as its representing type we make use of the spODataEntity and spODataEntityArray parsers. The below approach works for all instance types such as List, Web, Item, or Field as examples. Importing spODataEntity and spODataEntityArray \u00b6 You can import spODataEntity and spODataEntityArray in two ways, depending on your use case. The simplest way is to use the presets/all import as shown in the examples. The downside of this approach is that you can't take advantage of selective imports. If you want to take advantage of selective imports while using either of the entity parsers you can use: import { spODataEntity, spODataEntityArray } from \"@pnp/sp/odata\"; The full selective import for the first sample would be: import { sp } from \"@pnp/sp\"; import { spODataEntity } from \"@pnp/sp/odata\"; import { Item, IItem } from \"@pnp/sp/items\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; Request a single entity \u00b6 If we are loading a single entity we use the spODataEntity method. Here we show loading a list item using the Item class and a simple get query. import { sp, spODataEntity, Item, IItem } from \"@pnp/sp/presets/all\"; // interface defining the returned properties interface MyProps { Id: number; } try { // get a list item loaded with data and merged into an instance of Item const item = await sp.web.lists.getByTitle(\"ListTitle\").items.getById(1).usingParser(spODataEntity(Item))(); // log the item id, all properties specified in MyProps will be type checked Logger.write(`Item id: ${item.Id}`); // now we can call update because we have an instance of the Item type to work with as well await item.update({ Title: \"New title.\", }); } catch (e) { Logger.error(e); } Request a collection \u00b6 The same pattern works when requesting a collection of objects with the exception of using the spODataEntityArray method. import { sp, spODataEntityArray, Item, IItem } from \"@pnp/sp/presets/all\"; // interface defining the returned properties interface MyProps { Id: number; Title: string; } try { // get a list item loaded with data and merged into an instance of Item const items = await sp.web.lists.getByTitle(\"OrderByList\").items.select(\"Id\", \"Title\").usingParser(spODataEntityArray(Item))(); Logger.write(`Item id: ${items.length}`); Logger.write(`Item id: ${items[0].Title}`); // now we can call update because we have an instance of the Item type to work with as well await items[0].update({ Title: \"New title.\", }); } catch (e) { Logger.error(e); }","title":"@pnp/sp - entity merging"},{"location":"v2/sp/entity-merging/#pnpsp-entity-merging","text":"Sometimes when we make a query entity's data we would like then to immediately run other commands on the returned entity. To have data returned as its representing type we make use of the spODataEntity and spODataEntityArray parsers. The below approach works for all instance types such as List, Web, Item, or Field as examples.","title":"@pnp/sp - entity merging"},{"location":"v2/sp/entity-merging/#importing-spodataentity-and-spodataentityarray","text":"You can import spODataEntity and spODataEntityArray in two ways, depending on your use case. The simplest way is to use the presets/all import as shown in the examples. The downside of this approach is that you can't take advantage of selective imports. If you want to take advantage of selective imports while using either of the entity parsers you can use: import { spODataEntity, spODataEntityArray } from \"@pnp/sp/odata\"; The full selective import for the first sample would be: import { sp } from \"@pnp/sp\"; import { spODataEntity } from \"@pnp/sp/odata\"; import { Item, IItem } from \"@pnp/sp/items\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\";","title":"Importing spODataEntity and spODataEntityArray"},{"location":"v2/sp/entity-merging/#request-a-single-entity","text":"If we are loading a single entity we use the spODataEntity method. Here we show loading a list item using the Item class and a simple get query. import { sp, spODataEntity, Item, IItem } from \"@pnp/sp/presets/all\"; // interface defining the returned properties interface MyProps { Id: number; } try { // get a list item loaded with data and merged into an instance of Item const item = await sp.web.lists.getByTitle(\"ListTitle\").items.getById(1).usingParser(spODataEntity(Item))(); // log the item id, all properties specified in MyProps will be type checked Logger.write(`Item id: ${item.Id}`); // now we can call update because we have an instance of the Item type to work with as well await item.update({ Title: \"New title.\", }); } catch (e) { Logger.error(e); }","title":"Request a single entity"},{"location":"v2/sp/entity-merging/#request-a-collection","text":"The same pattern works when requesting a collection of objects with the exception of using the spODataEntityArray method. import { sp, spODataEntityArray, Item, IItem } from \"@pnp/sp/presets/all\"; // interface defining the returned properties interface MyProps { Id: number; Title: string; } try { // get a list item loaded with data and merged into an instance of Item const items = await sp.web.lists.getByTitle(\"OrderByList\").items.select(\"Id\", \"Title\").usingParser(spODataEntityArray(Item))(); Logger.write(`Item id: ${items.length}`); Logger.write(`Item id: ${items[0].Title}`); // now we can call update because we have an instance of the Item type to work with as well await items[0].update({ Title: \"New title.\", }); } catch (e) { Logger.error(e); }","title":"Request a collection"},{"location":"v2/sp/features/","text":"@pnp/sp/features \u00b6 Features module provides method to get the details of activated features. And to activate/deactivate features scoped at Site Collection and Web. IFeatures \u00b6 Represents a collection of features. SharePoint Sites and Webs will have a collection of features Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features/site\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features/web\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; Preset: All import { sp, IFeatures, Features } from \"@pnp/sp/presets/all\"; getById \u00b6 Gets the information about a feature for the given GUID import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; const webFeature = await sp.web.features.getById(webFeatureId)(); const siteFeatureId = \"guid-of-site-scope-feature\"; const siteFeature = await sp.site.features.getById(siteFeatureId)(); add \u00b6 Adds (activates) a feature at the Site or Web level import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; let res = await sp.web.features.add(webFeatureId); // Activate with force res = await sp.web.features.add(webFeatureId, true); remove \u00b6 Removes and deactivates the specified feature from the SharePoint Site or Web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; let res = await sp.web.features.remove(webFeatureId); // Deactivate with force res = await sp.web.features.remove(webFeatureId, true); IFeature \u00b6 Represents an instance of a SharePoint feature. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features/site\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features/web\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; Preset: All import { sp, IFeatures, Features, IFeature, Feature } from \"@pnp/sp/presets/all\"; deactivate \u00b6 Deactivates the specified feature from the SharePoint Site or Web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; sp.web.features.getById(webFeatureId).deactivate() // Deactivate with force sp.web.features.getById(webFeatureId).deactivate(true)","title":"@pnp/sp/features"},{"location":"v2/sp/features/#pnpspfeatures","text":"Features module provides method to get the details of activated features. And to activate/deactivate features scoped at Site Collection and Web.","title":"@pnp/sp/features"},{"location":"v2/sp/features/#ifeatures","text":"Represents a collection of features. SharePoint Sites and Webs will have a collection of features Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features/site\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features/web\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; Preset: All import { sp, IFeatures, Features } from \"@pnp/sp/presets/all\";","title":"IFeatures"},{"location":"v2/sp/features/#getbyid","text":"Gets the information about a feature for the given GUID import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; const webFeature = await sp.web.features.getById(webFeatureId)(); const siteFeatureId = \"guid-of-site-scope-feature\"; const siteFeature = await sp.site.features.getById(siteFeatureId)();","title":"getById"},{"location":"v2/sp/features/#add","text":"Adds (activates) a feature at the Site or Web level import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; let res = await sp.web.features.add(webFeatureId); // Activate with force res = await sp.web.features.add(webFeatureId, true);","title":"add"},{"location":"v2/sp/features/#remove","text":"Removes and deactivates the specified feature from the SharePoint Site or Web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; let res = await sp.web.features.remove(webFeatureId); // Deactivate with force res = await sp.web.features.remove(webFeatureId, true);","title":"remove"},{"location":"v2/sp/features/#ifeature","text":"Represents an instance of a SharePoint feature. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features/site\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features/web\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/features\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; Preset: All import { sp, IFeatures, Features, IFeature, Feature } from \"@pnp/sp/presets/all\";","title":"IFeature"},{"location":"v2/sp/features/#deactivate","text":"Deactivates the specified feature from the SharePoint Site or Web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/features\"; //Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a const webFeatureId = \"guid-of-web-feature\"; sp.web.features.getById(webFeatureId).deactivate() // Deactivate with force sp.web.features.getById(webFeatureId).deactivate(true)","title":"deactivate"},{"location":"v2/sp/fields/","text":"@pnp/sp/lists \u00b6 Fields in SharePoint can be applied to both webs and lists. When referencing a webs' fields you are effectively looking at site columns which are common fields that can be utilized in any list/library in the site. When referencing a lists' fields you are looking at the fields only associated to that particular list. IFields \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import { Fields, IFields } from \"@pnp/sp/fields\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/fields\"; Preset: All import { sp, Fields, IFields } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Fields, IFields } from \"@pnp/sp/presets/core\"; Get Field by Id \u00b6 Gets a field from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/fields\"; // get the field by Id for web const field: IField = sp.web.fields.getById(\"03b05ff4-d95d-45ed-841d-3855f77a2483\"); // get the field by Id for list 'My List' const field2: IFieldInfo = await sp.web.lists.getByTitle(\"My List\").fields.getById(\"03b05ff4-d95d-45ed-841d-3855f77a2483\")(); // we can use this 'field' variable to execute more queries on the field: const r = await field.select(\"Title\")(); // show the response from the server console.log(r.Title); Get Field by Title \u00b6 You can also get a field from the collection by title. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\" import \"@pnp/sp/fields\"; // get the field with the title 'Author' for web const field: IField = sp.web.fields.getByTitle(\"Author\"); // get the field with the title 'Author' for list 'My List' const field2: IFieldInfo = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"Author\")(); // we can use this 'field' variable to run more queries on the field: const r = await field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Get Field by Internal Name or Title \u00b6 You can also get a field from the collection regardless of if the string is the fields internal name or title which can be different. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\" import \"@pnp/sp/fields\"; // get the field with the internal name 'ModifiedBy' for web const field: IField = sp.web.fields.getByInternalNameOrTitle(\"ModifiedBy\"); // get the field with the internal name 'ModifiedBy' for list 'My List' const field2: IFieldInfo = await sp.web.lists.getByTitle(\"My List\").fields.getByInternalNameOrTitle(\"ModifiedBy\")(); // we can use this 'field' variable to run more queries on the field: const r = await field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Create a Field using an XML schema \u00b6 Create a new field by defining an XML schema that assigns all the properties for the field. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // define the schema for your new field, in this case a date field with a default date of today. const fieldSchema = `[today]`; // create the new field in the web const field: IFieldAddResult = await sp.web.fields.createFieldAsXml(fieldSchema); // create the new field in the list 'My List' const field2: IFieldAddResult = await sp.web.lists.getByTitle(\"My List\").fields.createFieldAsXml(fieldSchema); // we can use this 'field' variable to run more queries on the list: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a New Field \u00b6 Use the add method to create a new field where you define the field type import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new field called 'My Field' in web. const field: IFieldAddResult = await sp.web.fields.add(\"My Field\", \"SP.FieldText\", { FieldTypeKind: 3, Group: \"My Group\" }); // create a new field called 'My Field' in the list 'My List' const field2: IFieldAddResult = await sp.web.lists.getByTitle(\"My List\").fields.add(\"My Field\", \"SP.FieldText\", { FieldTypeKind: 3, Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Site Field to a List \u00b6 Use the createFieldAsXml method to add a site field to a list. import { sp } from \"@pnp/sp\"; import { IFieldAddResult } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new field called 'My Field' in web. const field: IFieldAddResult = await sp.web.fields.add(\"My Field\", \"SP.FieldText\", { FieldTypeKind: 3, Group: \"My Group\" }); // add the site field 'My Field' to the list 'My List' const r = await sp.web.lists.getByTitle(\"My List\").fields.createFieldAsXml(field.data.SchemaXml); // log the field Id to console console.log(r.data.Id); Add a Text Field \u00b6 Use the addText method to create a new text field. import { sp } from \"@pnp/sp\"; import { IFieldAddResult } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new text field called 'My Field' in web. const field: IFieldAddResult = await sp.web.fields.addText(\"My Field\", 255, { Group: \"My Group\" }); // create a new text field called 'My Field' in the list 'My List'. const field2: IFieldAddResult = await sp.web.lists.getByTitle(\"My List\").fields.addText(\"My Field\", 255, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Calculated Field \u00b6 Use the addCalculated method to create a new calculated field. import { sp } from \"@pnp/sp\"; import { DateTimeFieldFormatType, FieldTypes } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new calculated field called 'My Field' in web const field = await sp.web.fields.addCalculated(\"My Field\", \"=Modified+1\", DateTimeFieldFormatType.DateOnly, FieldTypes.DateTime, { Group: \"MyGroup\" }); // create a new calculated field called 'My Field' in the list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addCalculated(\"My Field\", \"=Modified+1\", DateTimeFieldFormatType.DateOnly, FieldTypes.DateTime, { Group: \"MyGroup\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Date/Time Field \u00b6 Use the addDateTime method to create a new date/time field. import { sp } from \"@pnp/sp\"; import { DateTimeFieldFormatType, CalendarType, DateTimeFieldFriendlyFormatType } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new date/time field called 'My Field' in web const field = await sp.web.fields.addDateTime(\"My Field\", DateTimeFieldFormatType.DateOnly, CalendarType.Gregorian, DateTimeFieldFriendlyFormatType.Disabled, { Group: \"My Group\" }); // create a new date/time field called 'My Field' in the list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addDateTime(\"My Field\", DateTimeFieldFormatType.DateOnly, CalendarType.Gregorian, DateTimeFieldFriendlyFormatType.Disabled, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Currency Field \u00b6 Use the addCurrency method to create a new currency field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new currency field called 'My Field' in web const field = await sp.web.fields.addCurrency(\"My Field\", 0, 100, 1033, { Group: \"My Group\" }); // create a new currency field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addCurrency(\"My Field\", 0, 100, 1033, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Multi-line Text Field \u00b6 Use the addMultilineText method to create a new multi-line text field. For Enhanced Rich Text mode, see the next section. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new multi-line text field called 'My Field' in web const field = await sp.web.fields.addMultilineText(\"My Field\", 6, true, false, false, true, { Group: \"My Group\" }); // create a new multi-line text field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addMultilineText(\"My Field\", 6, true, false, false, true, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Multi-line Text Field with Enhanced Rich Text \u00b6 The REST endpoint doesn't support setting the RichTextMode field therefore you will need to revert to Xml to create the field. The following is an example that will create a multi-line text field in Enhanced Rich Text mode. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; //Create a new multi-line text field called 'My Field' in web const field = await sp.web.lists.getByTitle(\"My List\").fields.createFieldAsXml( `` ); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Number Field \u00b6 Use the addNumber method to create a new number field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new number field called 'My Field' in web const field = await sp.web.fields.addNumber(\"My Field\", 1, 100, { Group: \"My Group\" }); // create a new number field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addNumber(\"My Field\", 1, 100, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a URL Field \u00b6 Use the addUrl method to create a new url field. import { sp } from \"@pnp/sp\"; import { UrlFieldFormatType } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new url field called 'My Field' in web const field = await sp.web.fields.addUrl(\"My Field\", UrlFieldFormatType.Hyperlink, { Group: \"My Group\" }); // create a new url field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addUrl(\"My Field\", UrlFieldFormatType.Hyperlink, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a User Field \u00b6 Use the addUser method to create a new user field. import { sp } from \"@pnp/sp\"; import { FieldUserSelectionMode } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new user field called 'My Field' in web const field = await sp.web.fields.addUser(\"My Field\", FieldUserSelectionMode.PeopleOnly, { Group: \"My Group\" }); // create a new user field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addUser(\"My Field\", FieldUserSelectionMode.PeopleOnly, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Lookup Field \u00b6 Use the addLookup method to create a new lookup field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; const list = await sp.web.lists.getByTitle(\"My Lookup List\")(); // create a new lookup field called 'My Field' based on an existing list 'My Lookup List' showing 'Title' field in web. const field = await sp.web.fields.addLookup(\"My Field\", list.Id, \"Title\", { Group: \"My Group\" }); // create a new lookup field called 'My Field' based on an existing list 'My Lookup List' showing 'Title' field in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addLookup(\"My Field\", list.Id, \"Title\", { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); // ** // Adding a lookup that supports multiple values takes two calls: const fieldAddResult = await sp.web.fields.addLookup(\"Test Lookup 124\", \"GUID\", \"Title\"); await fieldAddResult.field.update({ Description: 'New Description' }, \"SP.FieldLookup\"); Add a Choice Field \u00b6 Use the addChoice method to create a new choice field. import { sp } from \"@pnp/sp\"; import { ChoiceFieldFormatType } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; const choices = [`ChoiceA`, `ChoiceB`, `ChoiceC`]; // create a new choice field called 'My Field' in web const field = await sp.web.fields.addChoice(\"My Field\", choices, ChoiceFieldFormatType.Dropdown, false, { Group: \"My Group\" }); // create a new choice field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addChoice(\"My Field\", choices, ChoiceFieldFormatType.Dropdown, false, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Multi-Choice Field \u00b6 Use the addMultiChoice method to create a new multi-choice field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; const choices = [`ChoiceA`, `ChoiceB`, `ChoiceC`]; // create a new multi-choice field called 'My Field' in web const field = await sp.web.fields.addMultiChoice(\"My Field\", choices, false, { Group: \"My Group\" }); // create a new multi-choice field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addMultiChoice(\"My Field\", choices, false, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Boolean Field \u00b6 Use the addBoolean method to create a new boolean field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new boolean field called 'My Field' in web const field = await sp.web.fields.addBoolean(\"My Field\", { Group: \"My Group\" }); // create a new boolean field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addBoolean(\"My Field\", { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Dependent Lookup Field \u00b6 Use the addDependentLookupField method to create a new dependent lookup field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new dependent lookup field called 'My Dep Field' showing 'Description' based on an existing 'My Field' lookup field in web. const field = await sp.web.fields.getByTitle(\"My Field\")(); const fieldDep = await sp.web.fields.addDependentLookupField(\"My Dep Field\", field.Id, \"Description\"); // create a new dependent lookup field called 'My Dep Field' showing 'Description' based on an existing 'My Field' lookup field in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\")(); const fieldDep2 = await sp.web.lists.getByTitle(\"My List\").fields.addDependentLookupField(\"My Dep Field\", field2.Id, \"Description\"); // we can use this 'fieldDep' variable to run more queries on the field: const r = await fieldDep.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Add a Location Field \u00b6 Use the addLocation method to create a new location field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new location field called 'My Field' in web const field = await sp.web.fields.addLocation(\"My Field\", { Group: \"My Group\" }); // create a new location field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addLocation(\"My Field\", { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); Delete a Field \u00b6 Use the delete method to delete a field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/fields\"; // delete one or more fields from web, returns boolean const result = await sp.web.fields.getByTitle(\"My Field\").delete(); const result2 = await sp.web.fields.getByTitle(\"My Field 2\").delete(); // delete one or more fields from list 'My List', returns boolean const result = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").delete(); const result2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field 2\").delete(); Update a Field \u00b6 Use the update method to update a field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // update the field called 'My Field' with a description in web, returns FieldUpdateResult const fieldUpdate = await sp.web.fields.getByTitle(\"My Field\").update({ Description: \"My Description\" }); // update the field called 'My Field' with a description in list 'My List', returns FieldUpdateResult const fieldUpdate2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").update({ Description: \"My Description\" }); // if you need to update a field with properties for a specific field type you can optionally include the field type as a second param // if you do not include it we will look up the type, but that adds a call to the server const fieldUpdate2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Look up Field\").update({ RelationshipDeleteBehavior: 1 }, \"SP.FieldLookup\"); Show a Field in the Display Form \u00b6 Use the setShowInDisplayForm method to add a field to the display form. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // show field called 'My Field' in display form throughout web await sp.web.fields.getByTitle(\"My Field\").setShowInDisplayForm(true); // show field called 'My Field' in display form for list 'My List' await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").setShowInDisplayForm(true); Show a Field in the Edit Form \u00b6 Use the setShowInEditForm method to add a field to the edit form. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // show field called 'My Field' in edit form throughout web await sp.web.fields.getByTitle(\"My Field\").setShowInEditForm(true); // show field called 'My Field' in edit form for list 'My List' await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").setShowInEditForm(true); Show a Field in the New Form \u00b6 Use the setShowInNewForm method to add a field to the display form. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // show field called 'My Field' in new form throughout web await sp.web.fields.getByTitle(\"My Field\").setShowInNewForm(true); // show field called 'My Field' in new form for list 'My List' await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").setShowInNewForm(true);","title":"@pnp/sp/lists"},{"location":"v2/sp/fields/#pnpsplists","text":"Fields in SharePoint can be applied to both webs and lists. When referencing a webs' fields you are effectively looking at site columns which are common fields that can be utilized in any list/library in the site. When referencing a lists' fields you are looking at the fields only associated to that particular list.","title":"@pnp/sp/lists"},{"location":"v2/sp/fields/#ifields","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import { Fields, IFields } from \"@pnp/sp/fields\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/fields\"; Preset: All import { sp, Fields, IFields } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Fields, IFields } from \"@pnp/sp/presets/core\";","title":"IFields"},{"location":"v2/sp/fields/#get-field-by-id","text":"Gets a field from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/fields\"; // get the field by Id for web const field: IField = sp.web.fields.getById(\"03b05ff4-d95d-45ed-841d-3855f77a2483\"); // get the field by Id for list 'My List' const field2: IFieldInfo = await sp.web.lists.getByTitle(\"My List\").fields.getById(\"03b05ff4-d95d-45ed-841d-3855f77a2483\")(); // we can use this 'field' variable to execute more queries on the field: const r = await field.select(\"Title\")(); // show the response from the server console.log(r.Title);","title":"Get Field by Id"},{"location":"v2/sp/fields/#get-field-by-title","text":"You can also get a field from the collection by title. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\" import \"@pnp/sp/fields\"; // get the field with the title 'Author' for web const field: IField = sp.web.fields.getByTitle(\"Author\"); // get the field with the title 'Author' for list 'My List' const field2: IFieldInfo = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"Author\")(); // we can use this 'field' variable to run more queries on the field: const r = await field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Get Field by Title"},{"location":"v2/sp/fields/#get-field-by-internal-name-or-title","text":"You can also get a field from the collection regardless of if the string is the fields internal name or title which can be different. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\" import \"@pnp/sp/fields\"; // get the field with the internal name 'ModifiedBy' for web const field: IField = sp.web.fields.getByInternalNameOrTitle(\"ModifiedBy\"); // get the field with the internal name 'ModifiedBy' for list 'My List' const field2: IFieldInfo = await sp.web.lists.getByTitle(\"My List\").fields.getByInternalNameOrTitle(\"ModifiedBy\")(); // we can use this 'field' variable to run more queries on the field: const r = await field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Get Field by Internal Name or Title"},{"location":"v2/sp/fields/#create-a-field-using-an-xml-schema","text":"Create a new field by defining an XML schema that assigns all the properties for the field. import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // define the schema for your new field, in this case a date field with a default date of today. const fieldSchema = `[today]`; // create the new field in the web const field: IFieldAddResult = await sp.web.fields.createFieldAsXml(fieldSchema); // create the new field in the list 'My List' const field2: IFieldAddResult = await sp.web.lists.getByTitle(\"My List\").fields.createFieldAsXml(fieldSchema); // we can use this 'field' variable to run more queries on the list: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Create a Field using an XML schema"},{"location":"v2/sp/fields/#add-a-new-field","text":"Use the add method to create a new field where you define the field type import { sp } from \"@pnp/sp\"; import { IField } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new field called 'My Field' in web. const field: IFieldAddResult = await sp.web.fields.add(\"My Field\", \"SP.FieldText\", { FieldTypeKind: 3, Group: \"My Group\" }); // create a new field called 'My Field' in the list 'My List' const field2: IFieldAddResult = await sp.web.lists.getByTitle(\"My List\").fields.add(\"My Field\", \"SP.FieldText\", { FieldTypeKind: 3, Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a New Field"},{"location":"v2/sp/fields/#add-a-site-field-to-a-list","text":"Use the createFieldAsXml method to add a site field to a list. import { sp } from \"@pnp/sp\"; import { IFieldAddResult } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new field called 'My Field' in web. const field: IFieldAddResult = await sp.web.fields.add(\"My Field\", \"SP.FieldText\", { FieldTypeKind: 3, Group: \"My Group\" }); // add the site field 'My Field' to the list 'My List' const r = await sp.web.lists.getByTitle(\"My List\").fields.createFieldAsXml(field.data.SchemaXml); // log the field Id to console console.log(r.data.Id);","title":"Add a Site Field to a List"},{"location":"v2/sp/fields/#add-a-text-field","text":"Use the addText method to create a new text field. import { sp } from \"@pnp/sp\"; import { IFieldAddResult } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new text field called 'My Field' in web. const field: IFieldAddResult = await sp.web.fields.addText(\"My Field\", 255, { Group: \"My Group\" }); // create a new text field called 'My Field' in the list 'My List'. const field2: IFieldAddResult = await sp.web.lists.getByTitle(\"My List\").fields.addText(\"My Field\", 255, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Text Field"},{"location":"v2/sp/fields/#add-a-calculated-field","text":"Use the addCalculated method to create a new calculated field. import { sp } from \"@pnp/sp\"; import { DateTimeFieldFormatType, FieldTypes } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new calculated field called 'My Field' in web const field = await sp.web.fields.addCalculated(\"My Field\", \"=Modified+1\", DateTimeFieldFormatType.DateOnly, FieldTypes.DateTime, { Group: \"MyGroup\" }); // create a new calculated field called 'My Field' in the list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addCalculated(\"My Field\", \"=Modified+1\", DateTimeFieldFormatType.DateOnly, FieldTypes.DateTime, { Group: \"MyGroup\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Calculated Field"},{"location":"v2/sp/fields/#add-a-datetime-field","text":"Use the addDateTime method to create a new date/time field. import { sp } from \"@pnp/sp\"; import { DateTimeFieldFormatType, CalendarType, DateTimeFieldFriendlyFormatType } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new date/time field called 'My Field' in web const field = await sp.web.fields.addDateTime(\"My Field\", DateTimeFieldFormatType.DateOnly, CalendarType.Gregorian, DateTimeFieldFriendlyFormatType.Disabled, { Group: \"My Group\" }); // create a new date/time field called 'My Field' in the list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addDateTime(\"My Field\", DateTimeFieldFormatType.DateOnly, CalendarType.Gregorian, DateTimeFieldFriendlyFormatType.Disabled, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Date/Time Field"},{"location":"v2/sp/fields/#add-a-currency-field","text":"Use the addCurrency method to create a new currency field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new currency field called 'My Field' in web const field = await sp.web.fields.addCurrency(\"My Field\", 0, 100, 1033, { Group: \"My Group\" }); // create a new currency field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addCurrency(\"My Field\", 0, 100, 1033, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Currency Field"},{"location":"v2/sp/fields/#add-a-multi-line-text-field","text":"Use the addMultilineText method to create a new multi-line text field. For Enhanced Rich Text mode, see the next section. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new multi-line text field called 'My Field' in web const field = await sp.web.fields.addMultilineText(\"My Field\", 6, true, false, false, true, { Group: \"My Group\" }); // create a new multi-line text field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addMultilineText(\"My Field\", 6, true, false, false, true, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Multi-line Text Field"},{"location":"v2/sp/fields/#add-a-multi-line-text-field-with-enhanced-rich-text","text":"The REST endpoint doesn't support setting the RichTextMode field therefore you will need to revert to Xml to create the field. The following is an example that will create a multi-line text field in Enhanced Rich Text mode. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; //Create a new multi-line text field called 'My Field' in web const field = await sp.web.lists.getByTitle(\"My List\").fields.createFieldAsXml( `` ); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Multi-line Text Field with Enhanced Rich Text"},{"location":"v2/sp/fields/#add-a-number-field","text":"Use the addNumber method to create a new number field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new number field called 'My Field' in web const field = await sp.web.fields.addNumber(\"My Field\", 1, 100, { Group: \"My Group\" }); // create a new number field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addNumber(\"My Field\", 1, 100, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Number Field"},{"location":"v2/sp/fields/#add-a-url-field","text":"Use the addUrl method to create a new url field. import { sp } from \"@pnp/sp\"; import { UrlFieldFormatType } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new url field called 'My Field' in web const field = await sp.web.fields.addUrl(\"My Field\", UrlFieldFormatType.Hyperlink, { Group: \"My Group\" }); // create a new url field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addUrl(\"My Field\", UrlFieldFormatType.Hyperlink, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a URL Field"},{"location":"v2/sp/fields/#add-a-user-field","text":"Use the addUser method to create a new user field. import { sp } from \"@pnp/sp\"; import { FieldUserSelectionMode } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new user field called 'My Field' in web const field = await sp.web.fields.addUser(\"My Field\", FieldUserSelectionMode.PeopleOnly, { Group: \"My Group\" }); // create a new user field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addUser(\"My Field\", FieldUserSelectionMode.PeopleOnly, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a User Field"},{"location":"v2/sp/fields/#add-a-lookup-field","text":"Use the addLookup method to create a new lookup field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; const list = await sp.web.lists.getByTitle(\"My Lookup List\")(); // create a new lookup field called 'My Field' based on an existing list 'My Lookup List' showing 'Title' field in web. const field = await sp.web.fields.addLookup(\"My Field\", list.Id, \"Title\", { Group: \"My Group\" }); // create a new lookup field called 'My Field' based on an existing list 'My Lookup List' showing 'Title' field in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addLookup(\"My Field\", list.Id, \"Title\", { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id); // ** // Adding a lookup that supports multiple values takes two calls: const fieldAddResult = await sp.web.fields.addLookup(\"Test Lookup 124\", \"GUID\", \"Title\"); await fieldAddResult.field.update({ Description: 'New Description' }, \"SP.FieldLookup\");","title":"Add a Lookup Field"},{"location":"v2/sp/fields/#add-a-choice-field","text":"Use the addChoice method to create a new choice field. import { sp } from \"@pnp/sp\"; import { ChoiceFieldFormatType } from \"@pnp/sp/fields/types\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; const choices = [`ChoiceA`, `ChoiceB`, `ChoiceC`]; // create a new choice field called 'My Field' in web const field = await sp.web.fields.addChoice(\"My Field\", choices, ChoiceFieldFormatType.Dropdown, false, { Group: \"My Group\" }); // create a new choice field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addChoice(\"My Field\", choices, ChoiceFieldFormatType.Dropdown, false, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Choice Field"},{"location":"v2/sp/fields/#add-a-multi-choice-field","text":"Use the addMultiChoice method to create a new multi-choice field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; const choices = [`ChoiceA`, `ChoiceB`, `ChoiceC`]; // create a new multi-choice field called 'My Field' in web const field = await sp.web.fields.addMultiChoice(\"My Field\", choices, false, { Group: \"My Group\" }); // create a new multi-choice field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addMultiChoice(\"My Field\", choices, false, { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Multi-Choice Field"},{"location":"v2/sp/fields/#add-a-boolean-field","text":"Use the addBoolean method to create a new boolean field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new boolean field called 'My Field' in web const field = await sp.web.fields.addBoolean(\"My Field\", { Group: \"My Group\" }); // create a new boolean field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addBoolean(\"My Field\", { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Boolean Field"},{"location":"v2/sp/fields/#add-a-dependent-lookup-field","text":"Use the addDependentLookupField method to create a new dependent lookup field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new dependent lookup field called 'My Dep Field' showing 'Description' based on an existing 'My Field' lookup field in web. const field = await sp.web.fields.getByTitle(\"My Field\")(); const fieldDep = await sp.web.fields.addDependentLookupField(\"My Dep Field\", field.Id, \"Description\"); // create a new dependent lookup field called 'My Dep Field' showing 'Description' based on an existing 'My Field' lookup field in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\")(); const fieldDep2 = await sp.web.lists.getByTitle(\"My List\").fields.addDependentLookupField(\"My Dep Field\", field2.Id, \"Description\"); // we can use this 'fieldDep' variable to run more queries on the field: const r = await fieldDep.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Dependent Lookup Field"},{"location":"v2/sp/fields/#add-a-location-field","text":"Use the addLocation method to create a new location field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // create a new location field called 'My Field' in web const field = await sp.web.fields.addLocation(\"My Field\", { Group: \"My Group\" }); // create a new location field called 'My Field' in list 'My List' const field2 = await sp.web.lists.getByTitle(\"My List\").fields.addLocation(\"My Field\", { Group: \"My Group\" }); // we can use this 'field' variable to run more queries on the field: const r = await field.field.select(\"Id\")(); // log the field Id to console console.log(r.Id);","title":"Add a Location Field"},{"location":"v2/sp/fields/#delete-a-field","text":"Use the delete method to delete a field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/fields\"; // delete one or more fields from web, returns boolean const result = await sp.web.fields.getByTitle(\"My Field\").delete(); const result2 = await sp.web.fields.getByTitle(\"My Field 2\").delete(); // delete one or more fields from list 'My List', returns boolean const result = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").delete(); const result2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field 2\").delete();","title":"Delete a Field"},{"location":"v2/sp/fields/#update-a-field","text":"Use the update method to update a field. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // update the field called 'My Field' with a description in web, returns FieldUpdateResult const fieldUpdate = await sp.web.fields.getByTitle(\"My Field\").update({ Description: \"My Description\" }); // update the field called 'My Field' with a description in list 'My List', returns FieldUpdateResult const fieldUpdate2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").update({ Description: \"My Description\" }); // if you need to update a field with properties for a specific field type you can optionally include the field type as a second param // if you do not include it we will look up the type, but that adds a call to the server const fieldUpdate2 = await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Look up Field\").update({ RelationshipDeleteBehavior: 1 }, \"SP.FieldLookup\");","title":"Update a Field"},{"location":"v2/sp/fields/#show-a-field-in-the-display-form","text":"Use the setShowInDisplayForm method to add a field to the display form. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // show field called 'My Field' in display form throughout web await sp.web.fields.getByTitle(\"My Field\").setShowInDisplayForm(true); // show field called 'My Field' in display form for list 'My List' await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").setShowInDisplayForm(true);","title":"Show a Field in the Display Form"},{"location":"v2/sp/fields/#show-a-field-in-the-edit-form","text":"Use the setShowInEditForm method to add a field to the edit form. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // show field called 'My Field' in edit form throughout web await sp.web.fields.getByTitle(\"My Field\").setShowInEditForm(true); // show field called 'My Field' in edit form for list 'My List' await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").setShowInEditForm(true);","title":"Show a Field in the Edit Form"},{"location":"v2/sp/fields/#show-a-field-in-the-new-form","text":"Use the setShowInNewForm method to add a field to the display form. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/fields\"; // show field called 'My Field' in new form throughout web await sp.web.fields.getByTitle(\"My Field\").setShowInNewForm(true); // show field called 'My Field' in new form for list 'My List' await sp.web.lists.getByTitle(\"My List\").fields.getByTitle(\"My Field\").setShowInNewForm(true);","title":"Show a Field in the New Form"},{"location":"v2/sp/files/","text":"@pnp/sp/files \u00b6 One of the more challenging tasks on the client side is working with SharePoint files, especially if they are large files. We have added some methods to the library to help and their use is outlined below. Reading Files \u00b6 Reading files from the client using REST is covered in the below examples. The important thing to remember is choosing which format you want the file in so you can appropriately process it. You can retrieve a file as Blob, Buffer, JSON, or Text. If you have a special requirement you could also write your own parser . import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; const blob: Blob = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.avi\").getBlob(); const buffer: ArrayBuffer = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.avi\").getBuffer(); const json: any = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.json\").getJSON(); const text: string = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.txt\").getText(); // all of these also work from a file object no matter how you access it const text2: string = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/documents\").files.getByName(\"file.txt\").getText(); getFileByUrl \u00b6 Added in 2.0.4 This method supports opening files from sharing links or absolute urls. The file must reside in the site from which you are trying to open the file. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files/web\"; const url = \"{absolute file url OR sharing url}\"; // file is an IFile and supports all the file operations const file = sp.web.getFileByUrl(url); // for example const fileContent = await file.getText(); Adding Files \u00b6 Likewise you can add files using one of two methods, add or addChunked. AddChunked is appropriate for larger files, generally larger than 10 MB but this may differ based on your bandwidth/latency so you can adjust the code to use the chunked method. The below example shows getting the file object from an input and uploading it to SharePoint, choosing the upload method based on file size. declare var require: (s: string) => any; import { ConsoleListener, Logger, LogLevel } from \"@pnp/logging\"; import { sp } from \"@pnp/sp\"; import { Web } from \"@pnp/sp/webs\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import { auth } from \"./auth\"; let $ = require(\"jquery\"); // <-- used here for illustration let siteUrl = \"https://mytenant.sharepoint.com/sites/dev\"; // comment this out for non-node execution // auth(siteUrl); Logger.subscribe(new ConsoleListener()); Logger.activeLogLevel = LogLevel.Verbose; let web = Web(siteUrl); $(() => { $(\"#testingdiv\").append(\"\"); $(\"#thebuttontodoit\").on('click', async (e) => { e.preventDefault(); let input = document.getElementById(\"thefileinput\"); let file = input.files[0]; // you can adjust this number to control what size files are uploaded in chunks if (file.size <= 10485760) { // small upload await web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.add(file.name, file, true); Logger.write(\"done\"); } else { // large upload await web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.addChunked(file.name, file, data => { Logger.log({ data: data, level: LogLevel.Verbose, message: \"progress\" }); }, true); Logger.write(\"done!\") } }); }); Adding a file using Nodejs Streams \u00b6 If you are working in nodejs you can also add a file using a stream. This example makes a copy of a file using streams. // triggers auto-application of extensions, in this case to add getStream import \"@pnp/nodejs\"; // get a stream of an existing file const sr = await sp.web.getFileByServerRelativePath(\"/sites/dev/shared documents/old.md\").getStream(); // now add the stream as a new file, remember to set the content-length header const fr = await sp.web.lists.getByTitle(\"Documents\").rootFolder.files.configure({ headers: { \"content-length\": `${sr.knownLength}`, }, }).add(\"new.md\", sr.body); Setting Associated Item Values \u00b6 You can also update the file properties of a newly uploaded file using code similar to the below snippet: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; const file = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.add(\"file.name\", \"file\", true); const item = await file.file.getItem(); await item.update({ Title: \"A Title\", OtherField: \"My Other Value\" }); AddUsingPath \u00b6 If you need to support the percent or pound characters you can use the addUsingPath method of IFiles import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; const file = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.addUsingPath(\"file%#%.name\", \"content\"); Update File Content \u00b6 You can of course use similar methods to update existing files as shown below. This overwrites the existing content in the file. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/test.txt\").setContent(\"New string content for the file.\"); await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/test.mp4\").setContentChunked(file); Check in, Check out, and Approve & Deny \u00b6 The library provides helper methods for checking in, checking out, and approving files. Examples of these methods are shown below. Check In \u00b6 Check in takes two optional arguments, comment and check in type. import { sp } from \"@pnp/sp\"; import { CheckinType } from \"@pnp/sp/files\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // default options with empty comment and CheckinType.Major await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkin(); console.log(\"File checked in!\"); // supply a comment (< 1024 chars) and using default check in type CheckinType.Major await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkin(\"A comment\"); console.log(\"File checked in!\"); // Supply both comment and check in type await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkin(\"A comment\", CheckinType.Overwrite); console.log(\"File checked in!\"); Check Out \u00b6 Check out takes no arguments. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkout(); console.log(\"File checked out!\"); Approve and Deny \u00b6 You can also approve or deny files in libraries that use approval. Approve takes a single required argument of comment, the comment is optional for deny. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").approve(\"Approval Comment\"); console.log(\"File approved!\"); // deny with no comment await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").deny(); console.log(\"File denied!\"); // deny with a supplied comment. await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").deny(\"Deny comment\"); console.log(\"File denied!\"); Publish and Unpublish \u00b6 You can both publish and unpublish a file using the library. Both methods take an optional comment argument. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // publish with no comment await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").publish(); console.log(\"File published!\"); // publish with a supplied comment. await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").publish(\"Publish comment\"); console.log(\"File published!\"); // unpublish with no comment await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").unpublish(); console.log(\"File unpublished!\"); // unpublish with a supplied comment. await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").unpublish(\"Unpublish comment\"); console.log(\"File unpublished!\"); Advanced Upload Options \u00b6 Both the addChunked and setContentChunked methods support options beyond just supplying the file content. progress function \u00b6 A method that is called each time a chunk is uploaded and provides enough information to report progress or update a progress bar easily. The method has the signature: (data: ChunkedFileUploadProgressData) => void The data interface is: export interface ChunkedFileUploadProgressData { stage: \"starting\" | \"continue\" | \"finishing\"; blockNumber: number; totalBlocks: number; chunkSize: number; currentPointer: number; fileSize: number; } chunkSize \u00b6 This property controls the size of the individual chunks and is defaulted to 10485760 bytes (10 MB). You can adjust this based on your bandwidth needs - especially if writing code for mobile uploads or you are seeing frequent timeouts. getItem \u00b6 This method allows you to get the item associated with this file. You can optionally specify one or more select fields. The result will be merged with a new Item instance so you will have both the returned property values and chaining ability in a single object. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/security\"; const item = await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.txt\").getItem(); console.log(item); const item2 = await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.txt\").getItem(\"Title\", \"Modified\"); console.log(item2); // you can also chain directly off this item instance const perms = await item.getCurrentUserEffectivePermissions(); console.log(perms); You can also supply a generic typing parameter and the resulting type will be a union type of Item and the generic type parameter. This allows you to have proper intellisense and type checking. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/items\"; import \"@pnp/sp/security\"; // also supports typing the objects so your type will be a union type const item = await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.txt\").getItem<{ Id: number, Title: string }>(\"Id\", \"Title\"); // You get intellisense and proper typing of the returned object console.log(`Id: ${item.Id} -- ${item.Title}`); // You can also chain directly off this item instance const perms = await item.getCurrentUserEffectivePermissions(); console.log(perms); move \u00b6 It's possible to move a file to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").moveTo(destinationUrl); copy \u00b6 It's possible to copy a file to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").copyTo(destinationUrl, false); move by path \u00b6 It's possible to move a file to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev2/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").moveByPath(destinationUrl, false, true); copy by path \u00b6 It's possible to copy a file to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev2/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").copyByPath(destinationUrl, false, true); getFileById \u00b6 You can get a file by Id from a web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import { IFile } from \"@pnp/sp/files\"; const file: IFile = sp.web.getFileById(\"2b281c7b-ece9-4b76-82f9-f5cf5e152ba0\"); delete \u00b6 Deletes a file import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; await sp.web.rootFolder.files.getByName(\"name.txt\").delete(); delete with params \u00b6 Added in 2.0.9 Deletes a file with options import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; await sp.web.rootFolder.files.getByName(\"name.txt\").deleteWithParams({ BypassSharedLock: true, }); exists \u00b6 Added in 2.0.9 Checks to see if a file exists import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; const exists = await sp.web.rootFolder.files.getByName(\"name.txt\").exists();","title":"@pnp/sp/files"},{"location":"v2/sp/files/#pnpspfiles","text":"One of the more challenging tasks on the client side is working with SharePoint files, especially if they are large files. We have added some methods to the library to help and their use is outlined below.","title":"@pnp/sp/files"},{"location":"v2/sp/files/#reading-files","text":"Reading files from the client using REST is covered in the below examples. The important thing to remember is choosing which format you want the file in so you can appropriately process it. You can retrieve a file as Blob, Buffer, JSON, or Text. If you have a special requirement you could also write your own parser . import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; const blob: Blob = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.avi\").getBlob(); const buffer: ArrayBuffer = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.avi\").getBuffer(); const json: any = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.json\").getJSON(); const text: string = await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/file.txt\").getText(); // all of these also work from a file object no matter how you access it const text2: string = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/documents\").files.getByName(\"file.txt\").getText();","title":"Reading Files"},{"location":"v2/sp/files/#getfilebyurl","text":"Added in 2.0.4 This method supports opening files from sharing links or absolute urls. The file must reside in the site from which you are trying to open the file. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files/web\"; const url = \"{absolute file url OR sharing url}\"; // file is an IFile and supports all the file operations const file = sp.web.getFileByUrl(url); // for example const fileContent = await file.getText();","title":"getFileByUrl"},{"location":"v2/sp/files/#adding-files","text":"Likewise you can add files using one of two methods, add or addChunked. AddChunked is appropriate for larger files, generally larger than 10 MB but this may differ based on your bandwidth/latency so you can adjust the code to use the chunked method. The below example shows getting the file object from an input and uploading it to SharePoint, choosing the upload method based on file size. declare var require: (s: string) => any; import { ConsoleListener, Logger, LogLevel } from \"@pnp/logging\"; import { sp } from \"@pnp/sp\"; import { Web } from \"@pnp/sp/webs\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import { auth } from \"./auth\"; let $ = require(\"jquery\"); // <-- used here for illustration let siteUrl = \"https://mytenant.sharepoint.com/sites/dev\"; // comment this out for non-node execution // auth(siteUrl); Logger.subscribe(new ConsoleListener()); Logger.activeLogLevel = LogLevel.Verbose; let web = Web(siteUrl); $(() => { $(\"#testingdiv\").append(\"\"); $(\"#thebuttontodoit\").on('click', async (e) => { e.preventDefault(); let input = document.getElementById(\"thefileinput\"); let file = input.files[0]; // you can adjust this number to control what size files are uploaded in chunks if (file.size <= 10485760) { // small upload await web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.add(file.name, file, true); Logger.write(\"done\"); } else { // large upload await web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.addChunked(file.name, file, data => { Logger.log({ data: data, level: LogLevel.Verbose, message: \"progress\" }); }, true); Logger.write(\"done!\") } }); });","title":"Adding Files"},{"location":"v2/sp/files/#adding-a-file-using-nodejs-streams","text":"If you are working in nodejs you can also add a file using a stream. This example makes a copy of a file using streams. // triggers auto-application of extensions, in this case to add getStream import \"@pnp/nodejs\"; // get a stream of an existing file const sr = await sp.web.getFileByServerRelativePath(\"/sites/dev/shared documents/old.md\").getStream(); // now add the stream as a new file, remember to set the content-length header const fr = await sp.web.lists.getByTitle(\"Documents\").rootFolder.files.configure({ headers: { \"content-length\": `${sr.knownLength}`, }, }).add(\"new.md\", sr.body);","title":"Adding a file using Nodejs Streams"},{"location":"v2/sp/files/#setting-associated-item-values","text":"You can also update the file properties of a newly uploaded file using code similar to the below snippet: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; const file = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.add(\"file.name\", \"file\", true); const item = await file.file.getItem(); await item.update({ Title: \"A Title\", OtherField: \"My Other Value\" });","title":"Setting Associated Item Values"},{"location":"v2/sp/files/#addusingpath","text":"If you need to support the percent or pound characters you can use the addUsingPath method of IFiles import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; const file = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared%20Documents/test/\").files.addUsingPath(\"file%#%.name\", \"content\");","title":"AddUsingPath"},{"location":"v2/sp/files/#update-file-content","text":"You can of course use similar methods to update existing files as shown below. This overwrites the existing content in the file. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/test.txt\").setContent(\"New string content for the file.\"); await sp.web.getFileByServerRelativeUrl(\"/sites/dev/documents/test.mp4\").setContentChunked(file);","title":"Update File Content"},{"location":"v2/sp/files/#check-in-check-out-and-approve-deny","text":"The library provides helper methods for checking in, checking out, and approving files. Examples of these methods are shown below.","title":"Check in, Check out, and Approve & Deny"},{"location":"v2/sp/files/#check-in","text":"Check in takes two optional arguments, comment and check in type. import { sp } from \"@pnp/sp\"; import { CheckinType } from \"@pnp/sp/files\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // default options with empty comment and CheckinType.Major await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkin(); console.log(\"File checked in!\"); // supply a comment (< 1024 chars) and using default check in type CheckinType.Major await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkin(\"A comment\"); console.log(\"File checked in!\"); // Supply both comment and check in type await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkin(\"A comment\", CheckinType.Overwrite); console.log(\"File checked in!\");","title":"Check In"},{"location":"v2/sp/files/#check-out","text":"Check out takes no arguments. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").checkout(); console.log(\"File checked out!\");","title":"Check Out"},{"location":"v2/sp/files/#approve-and-deny","text":"You can also approve or deny files in libraries that use approval. Approve takes a single required argument of comment, the comment is optional for deny. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").approve(\"Approval Comment\"); console.log(\"File approved!\"); // deny with no comment await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").deny(); console.log(\"File denied!\"); // deny with a supplied comment. await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").deny(\"Deny comment\"); console.log(\"File denied!\");","title":"Approve and Deny"},{"location":"v2/sp/files/#publish-and-unpublish","text":"You can both publish and unpublish a file using the library. Both methods take an optional comment argument. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // publish with no comment await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").publish(); console.log(\"File published!\"); // publish with a supplied comment. await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").publish(\"Publish comment\"); console.log(\"File published!\"); // unpublish with no comment await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").unpublish(); console.log(\"File unpublished!\"); // unpublish with a supplied comment. await sp.web.getFileByServerRelativeUrl(\"/sites/dev/shared documents/file.txt\").unpublish(\"Unpublish comment\"); console.log(\"File unpublished!\");","title":"Publish and Unpublish"},{"location":"v2/sp/files/#advanced-upload-options","text":"Both the addChunked and setContentChunked methods support options beyond just supplying the file content.","title":"Advanced Upload Options"},{"location":"v2/sp/files/#progress-function","text":"A method that is called each time a chunk is uploaded and provides enough information to report progress or update a progress bar easily. The method has the signature: (data: ChunkedFileUploadProgressData) => void The data interface is: export interface ChunkedFileUploadProgressData { stage: \"starting\" | \"continue\" | \"finishing\"; blockNumber: number; totalBlocks: number; chunkSize: number; currentPointer: number; fileSize: number; }","title":"progress function"},{"location":"v2/sp/files/#chunksize","text":"This property controls the size of the individual chunks and is defaulted to 10485760 bytes (10 MB). You can adjust this based on your bandwidth needs - especially if writing code for mobile uploads or you are seeing frequent timeouts.","title":"chunkSize"},{"location":"v2/sp/files/#getitem","text":"This method allows you to get the item associated with this file. You can optionally specify one or more select fields. The result will be merged with a new Item instance so you will have both the returned property values and chaining ability in a single object. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/security\"; const item = await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.txt\").getItem(); console.log(item); const item2 = await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.txt\").getItem(\"Title\", \"Modified\"); console.log(item2); // you can also chain directly off this item instance const perms = await item.getCurrentUserEffectivePermissions(); console.log(perms); You can also supply a generic typing parameter and the resulting type will be a union type of Item and the generic type parameter. This allows you to have proper intellisense and type checking. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/items\"; import \"@pnp/sp/security\"; // also supports typing the objects so your type will be a union type const item = await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.txt\").getItem<{ Id: number, Title: string }>(\"Id\", \"Title\"); // You get intellisense and proper typing of the returned object console.log(`Id: ${item.Id} -- ${item.Title}`); // You can also chain directly off this item instance const perms = await item.getCurrentUserEffectivePermissions(); console.log(perms);","title":"getItem"},{"location":"v2/sp/files/#move","text":"It's possible to move a file to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").moveTo(destinationUrl);","title":"move"},{"location":"v2/sp/files/#copy","text":"It's possible to copy a file to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").copyTo(destinationUrl, false);","title":"copy"},{"location":"v2/sp/files/#move-by-path","text":"It's possible to move a file to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev2/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").moveByPath(destinationUrl, false, true);","title":"move by path"},{"location":"v2/sp/files/#copy-by-path","text":"It's possible to copy a file to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; // destination is a server-relative url of a new file const destinationUrl = `/sites/dev2/SiteAssets/new-file.docx`; await sp.web.getFileByServerRelativePath(\"/sites/dev/Shared Documents/test.docx\").copyByPath(destinationUrl, false, true);","title":"copy by path"},{"location":"v2/sp/files/#getfilebyid","text":"You can get a file by Id from a web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; import { IFile } from \"@pnp/sp/files\"; const file: IFile = sp.web.getFileById(\"2b281c7b-ece9-4b76-82f9-f5cf5e152ba0\");","title":"getFileById"},{"location":"v2/sp/files/#delete","text":"Deletes a file import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; await sp.web.rootFolder.files.getByName(\"name.txt\").delete();","title":"delete"},{"location":"v2/sp/files/#delete-with-params","text":"Added in 2.0.9 Deletes a file with options import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; await sp.web.rootFolder.files.getByName(\"name.txt\").deleteWithParams({ BypassSharedLock: true, });","title":"delete with params"},{"location":"v2/sp/files/#exists","text":"Added in 2.0.9 Checks to see if a file exists import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/files\"; const exists = await sp.web.rootFolder.files.getByName(\"name.txt\").exists();","title":"exists"},{"location":"v2/sp/folders/","text":"@pnp/sp/folders \u00b6 Folders serve as a container for your files and list items. IFolders \u00b6 Represents a collection of folders. SharePoint webs, lists, and list items have a collection of folders under their properties. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IFolders, Folders } from \"@pnp/sp/folders\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; Selective 5 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/folders/item\"; Preset: All import { sp, IFolders, Folders } from \"@pnp/sp/presets/all\"; Get folders collection for various SharePoint objects \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/lists\"; // gets web's folders const webFolders = await sp.web.folders(); // gets list's folders const listFolders = await sp.web.lists.getByTitle(\"My List\").rootFolder.folders(); // gets item's folders const itemFolders = await sp.web.lists.getByTitle(\"My List\").items.getById(1).folder.folders(); add \u00b6 Adds a new folder to collection of folders import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // creates a new folder for web with specified url const folderAddResult = await sp.web.folders.add(\"folder url\"); getByName \u00b6 Gets a folder instance from a collection by folder's name import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folder = await sp.web.folders.getByName(\"folder name\")(); IFolder \u00b6 Represents an instance of a SharePoint folder. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IFolders, Folders } from \"@pnp/sp/folders\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; Selective 5 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/folders/item\"; Preset: All import { sp, IFolders, Folders } from \"@pnp/sp/presets/all\"; Get a folder object associated with different SharePoint artifacts (web, list, list item) \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // web's folder const rootFolder = await sp.web.rootFolder(); // list's folder const listRootFolder = await sp.web.lists.getByTitle(\"234\").rootFolder(); // item's folder const itemFolder = await sp.web.lists.getByTitle(\"234\").items.getById(1).folder(); getItem \u00b6 Gets list item associated with a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folderItem = await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").getItem(); move \u00b6 It's possible to move a folder to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").moveTo(destinationUrl); copy \u00b6 It's possible to copy a folder to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").copyTo(destinationUrl); move by path \u00b6 It's possible to move a folder to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").moveByPath(destinationUrl, true); copy by path \u00b6 It's possible to copy a folder to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").copyByPath(destinationUrl, true); delete \u00b6 Deletes a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.rootFolder.folders.getByName(\"My Folder\").delete(); delete with params \u00b6 Added in 2.0.9 Deletes a folder with options import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.rootFolder.folders.getByName(\"My Folder\").deleteWithParams({ BypassSharedLock: true, DeleteIfEmpty: true, }); recycle \u00b6 Recycles a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.rootFolder.folders.getByName(\"My Folder\").recycle(); serverRelativeUrl \u00b6 Gets folder's server relative url import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const relUrl = await sp.web.rootFolder.folders.getByName(\"SiteAssets\").serverRelativeUrl(); update \u00b6 Updates folder's properties import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.getFolderByServerRelativePath(\"Shared Documents/Folder2\").update({ \"Name\": \"New name\", }); contentTypeOrder \u00b6 Gets content type order of a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const order = await sp.web.getFolderByServerRelativePath(\"Shared Documents\").contentTypeOrder(); folders \u00b6 Gets all child folders associated with the current folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folders = await sp.web.rootFolder.folders(); files \u00b6 Gets all files inside a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/files/folder\"; const files = await sp.web.getFolderByServerRelativePath(\"Shared Documents\").files(); listItemAllFields \u00b6 Gets this folder's list item field values import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const itemFields = await sp.web.getFolderByServerRelativePath(\"Shared Documents/My Folder\").listItemAllFields(); parentFolder \u00b6 Gets the parent folder, if available import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const parentFolder = await sp.web.getFolderByServerRelativePath(\"Shared Documents/My Folder\").parentFolder(); properties \u00b6 Gets this folder's properties import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const properties = await sp.web.getFolderByServerRelativePath(\"Shared Documents/Folder2\").properties(); uniqueContentTypeOrder \u00b6 Gets a value that specifies the content type order. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const contentTypeOrder = await sp.web.getFolderByServerRelativePath(\"Shared Documents/Folder2\").uniqueContentTypeOrder(); Rename a folder \u00b6 You can rename a folder by updating FileLeafRef property: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folder = sp.web.getFolderByServerRelativePath(\"Shared Documents/My Folder\"); const item = await folder.getItem(); const result = await item.update({ FileLeafRef: \"Folder2\" }); Create a folder with custom content type \u00b6 Below code creates a new folder under Document library and assigns custom folder content type to a newly created folder. Additionally it sets a field of a custom folder content type. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/lists\"; const newFolderResult = await sp.web.rootFolder.folders.getByName(\"Shared Documents\").folders.add(\"My New Folder\"); const item = await newFolderResult.folder.listItemAllFields(); await sp.web.lists.getByTitle(\"Documents\").items.getById(item.ID).update({ ContentTypeId: \"0x0120001E76ED75A3E3F3408811F0BF56C4CDDD\", MyFolderField: \"field value\", Title: \"My New Folder\", }); addSubFolderUsingPath \u00b6 Added in 2.0.9 You can use the addSubFolderUsingPath method to add a folder with some special chars supported import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import { IFolder } from \"@pnp/sp/folders\"; // add a folder to site assets const folder: IFolder = await web.rootFolder.folders.getByName(\"SiteAssets\").addSubFolderUsingPath(\"folder name\"); getFolderById \u00b6 You can get a folder by Id from a web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import { IFolder } from \"@pnp/sp/folders\"; const folder: IFolder = sp.web.getFolderById(\"2b281c7b-ece9-4b76-82f9-f5cf5e152ba0\"); getParentInfos \u00b6 Added in 2.0.12 Gets information about folder, including details about the parent list, parent list root folder, and parent web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folder: IFolder = sp.web.getFolderById(\"2b281c7b-ece9-4b76-82f9-f5cf5e152ba0\"); await folder.getParentInfos();","title":"@pnp/sp/folders"},{"location":"v2/sp/folders/#pnpspfolders","text":"Folders serve as a container for your files and list items.","title":"@pnp/sp/folders"},{"location":"v2/sp/folders/#ifolders","text":"Represents a collection of folders. SharePoint webs, lists, and list items have a collection of folders under their properties. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IFolders, Folders } from \"@pnp/sp/folders\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; Selective 5 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/folders/item\"; Preset: All import { sp, IFolders, Folders } from \"@pnp/sp/presets/all\";","title":"IFolders"},{"location":"v2/sp/folders/#get-folders-collection-for-various-sharepoint-objects","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/lists\"; // gets web's folders const webFolders = await sp.web.folders(); // gets list's folders const listFolders = await sp.web.lists.getByTitle(\"My List\").rootFolder.folders(); // gets item's folders const itemFolders = await sp.web.lists.getByTitle(\"My List\").items.getById(1).folder.folders();","title":"Get folders collection for various SharePoint objects"},{"location":"v2/sp/folders/#add","text":"Adds a new folder to collection of folders import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // creates a new folder for web with specified url const folderAddResult = await sp.web.folders.add(\"folder url\");","title":"add"},{"location":"v2/sp/folders/#getbyname","text":"Gets a folder instance from a collection by folder's name import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folder = await sp.web.folders.getByName(\"folder name\")();","title":"getByName"},{"location":"v2/sp/folders/#ifolder","text":"Represents an instance of a SharePoint folder. Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IFolders, Folders } from \"@pnp/sp/folders\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/web\"; Selective 4 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; Selective 5 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders/list\"; import \"@pnp/sp/folders/item\"; Preset: All import { sp, IFolders, Folders } from \"@pnp/sp/presets/all\";","title":"IFolder"},{"location":"v2/sp/folders/#get-a-folder-object-associated-with-different-sharepoint-artifacts-web-list-list-item","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // web's folder const rootFolder = await sp.web.rootFolder(); // list's folder const listRootFolder = await sp.web.lists.getByTitle(\"234\").rootFolder(); // item's folder const itemFolder = await sp.web.lists.getByTitle(\"234\").items.getById(1).folder();","title":"Get a folder object associated with different SharePoint artifacts (web, list, list item)"},{"location":"v2/sp/folders/#getitem","text":"Gets list item associated with a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folderItem = await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").getItem();","title":"getItem"},{"location":"v2/sp/folders/#move","text":"It's possible to move a folder to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").moveTo(destinationUrl);","title":"move"},{"location":"v2/sp/folders/#copy","text":"It's possible to copy a folder to a new destination within a site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").copyTo(destinationUrl);","title":"copy"},{"location":"v2/sp/folders/#move-by-path","text":"It's possible to move a folder to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").moveByPath(destinationUrl, true);","title":"move by path"},{"location":"v2/sp/folders/#copy-by-path","text":"It's possible to copy a folder to a new destination within the same or a different site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; // destination is a server-relative url of a new folder const destinationUrl = `/sites/my-site/SiteAssets/new-folder`; await sp.web.rootFolder.folders.getByName(\"SiteAssets\").folders.getByName(\"My Folder\").copyByPath(destinationUrl, true);","title":"copy by path"},{"location":"v2/sp/folders/#delete","text":"Deletes a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.rootFolder.folders.getByName(\"My Folder\").delete();","title":"delete"},{"location":"v2/sp/folders/#delete-with-params","text":"Added in 2.0.9 Deletes a folder with options import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.rootFolder.folders.getByName(\"My Folder\").deleteWithParams({ BypassSharedLock: true, DeleteIfEmpty: true, });","title":"delete with params"},{"location":"v2/sp/folders/#recycle","text":"Recycles a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.rootFolder.folders.getByName(\"My Folder\").recycle();","title":"recycle"},{"location":"v2/sp/folders/#serverrelativeurl","text":"Gets folder's server relative url import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const relUrl = await sp.web.rootFolder.folders.getByName(\"SiteAssets\").serverRelativeUrl();","title":"serverRelativeUrl"},{"location":"v2/sp/folders/#update","text":"Updates folder's properties import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; await sp.web.getFolderByServerRelativePath(\"Shared Documents/Folder2\").update({ \"Name\": \"New name\", });","title":"update"},{"location":"v2/sp/folders/#contenttypeorder","text":"Gets content type order of a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const order = await sp.web.getFolderByServerRelativePath(\"Shared Documents\").contentTypeOrder();","title":"contentTypeOrder"},{"location":"v2/sp/folders/#folders","text":"Gets all child folders associated with the current folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folders = await sp.web.rootFolder.folders();","title":"folders"},{"location":"v2/sp/folders/#files","text":"Gets all files inside a folder import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/files/folder\"; const files = await sp.web.getFolderByServerRelativePath(\"Shared Documents\").files();","title":"files"},{"location":"v2/sp/folders/#listitemallfields","text":"Gets this folder's list item field values import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const itemFields = await sp.web.getFolderByServerRelativePath(\"Shared Documents/My Folder\").listItemAllFields();","title":"listItemAllFields"},{"location":"v2/sp/folders/#parentfolder","text":"Gets the parent folder, if available import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const parentFolder = await sp.web.getFolderByServerRelativePath(\"Shared Documents/My Folder\").parentFolder();","title":"parentFolder"},{"location":"v2/sp/folders/#properties","text":"Gets this folder's properties import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const properties = await sp.web.getFolderByServerRelativePath(\"Shared Documents/Folder2\").properties();","title":"properties"},{"location":"v2/sp/folders/#uniquecontenttypeorder","text":"Gets a value that specifies the content type order. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const contentTypeOrder = await sp.web.getFolderByServerRelativePath(\"Shared Documents/Folder2\").uniqueContentTypeOrder();","title":"uniqueContentTypeOrder"},{"location":"v2/sp/folders/#rename-a-folder","text":"You can rename a folder by updating FileLeafRef property: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folder = sp.web.getFolderByServerRelativePath(\"Shared Documents/My Folder\"); const item = await folder.getItem(); const result = await item.update({ FileLeafRef: \"Folder2\" });","title":"Rename a folder"},{"location":"v2/sp/folders/#create-a-folder-with-custom-content-type","text":"Below code creates a new folder under Document library and assigns custom folder content type to a newly created folder. Additionally it sets a field of a custom folder content type. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/lists\"; const newFolderResult = await sp.web.rootFolder.folders.getByName(\"Shared Documents\").folders.add(\"My New Folder\"); const item = await newFolderResult.folder.listItemAllFields(); await sp.web.lists.getByTitle(\"Documents\").items.getById(item.ID).update({ ContentTypeId: \"0x0120001E76ED75A3E3F3408811F0BF56C4CDDD\", MyFolderField: \"field value\", Title: \"My New Folder\", });","title":"Create a folder with custom content type"},{"location":"v2/sp/folders/#addsubfolderusingpath","text":"Added in 2.0.9 You can use the addSubFolderUsingPath method to add a folder with some special chars supported import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import { IFolder } from \"@pnp/sp/folders\"; // add a folder to site assets const folder: IFolder = await web.rootFolder.folders.getByName(\"SiteAssets\").addSubFolderUsingPath(\"folder name\");","title":"addSubFolderUsingPath"},{"location":"v2/sp/folders/#getfolderbyid","text":"You can get a folder by Id from a web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; import { IFolder } from \"@pnp/sp/folders\"; const folder: IFolder = sp.web.getFolderById(\"2b281c7b-ece9-4b76-82f9-f5cf5e152ba0\");","title":"getFolderById"},{"location":"v2/sp/folders/#getparentinfos","text":"Added in 2.0.12 Gets information about folder, including details about the parent list, parent list root folder, and parent web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/folders\"; const folder: IFolder = sp.web.getFolderById(\"2b281c7b-ece9-4b76-82f9-f5cf5e152ba0\"); await folder.getParentInfos();","title":"getParentInfos"},{"location":"v2/sp/forms/","text":"@pnp/sp/forms \u00b6 Forms in SharePoint are the Display, New, and Edit forms associated with a list. IFields \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import \"@pnp/sp/forms\"; import \"@pnp/sp/lists\"; Get Form by Id \u00b6 Gets a form from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/forms\"; import \"@pnp/sp/lists\"; // get the field by Id for web const form = sp.web.lists.getByTitle(\"Documents\").forms.getById(\"{c4486774-f1e2-4804-96f3-91edf3e22a19}\")();","title":"@pnp/sp/forms"},{"location":"v2/sp/forms/#pnpspforms","text":"Forms in SharePoint are the Display, New, and Edit forms associated with a list.","title":"@pnp/sp/forms"},{"location":"v2/sp/forms/#ifields","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import \"@pnp/sp/forms\"; import \"@pnp/sp/lists\";","title":"IFields"},{"location":"v2/sp/forms/#get-form-by-id","text":"Gets a form from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/forms\"; import \"@pnp/sp/lists\"; // get the field by Id for web const form = sp.web.lists.getByTitle(\"Documents\").forms.getById(\"{c4486774-f1e2-4804-96f3-91edf3e22a19}\")();","title":"Get Form by Id"},{"location":"v2/sp/hubsites/","text":"@pnp/sp/hubsites \u00b6 This module helps you with working with hub sites in your tenant. IHubSites \u00b6 Scenario Import Statement Selective import { sp } from \"@pnp/sp\"; import \"@pnp/sp/hubsites\"; Preset: All import { sp, HubSites, IHubSites } from \"@pnp/sp/presets/all\"; Get a Listing of All Hub sites \u00b6 import { sp } from \"@pnp/sp\"; import { IHubSiteInfo } from \"@pnp/sp/hubsites\"; import \"@pnp/sp/hubsites\"; // invoke the hub sites object const hubsites: IHubSiteInfo[] = await sp.hubSites(); // you can also use select to only return certain fields: const hubsites2: IHubSiteInfo[] = await sp.hubSites.select(\"ID\", \"Title\", \"RelatedHubSiteIds\")(); Get Hub site by Id \u00b6 Using the getById method on the hubsites module to get a hub site by site Id (guid). import { sp } from \"@pnp/sp\"; import { IHubSiteInfo } from \"@pnp/sp/hubsites\"; import \"@pnp/sp/hubsites\"; const hubsite: IHubSiteInfo = await sp.hubSites.getById(\"3504348e-b2be-49fb-a2a9-2d748db64beb\")(); // log hub site title to console console.log(hubsite.Title); Get ISite instance \u00b6 We provide a helper method to load the ISite instance from the HubSite import { sp } from \"@pnp/sp\"; import { ISite } from \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites\"; const site: ISite = await sp.hubSites.getById(\"3504348e-b2be-49fb-a2a9-2d748db64beb\").getSite(); const siteData = await site(); console.log(siteData.Title); Get Hub site data for a web \u00b6 import { sp } from \"@pnp/sp\"; import { IHubSiteWebData } from \"@pnp/sp/hubsites\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/hubsites/web\"; const webData: Partial = await sp.web.hubSiteData(); // you can also force a refresh of the hub site data const webData2: Partial = await sp.web.hubSiteData(true); syncHubSiteTheme \u00b6 Allows you to apply theme updates from the parent hub site collection. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/hubsites/web\"; await sp.web.syncHubSiteTheme(); Hub site Site Methods \u00b6 You manage hub sites at the Site level. joinHubSite \u00b6 Id of the hub site collection you want to join. If you want to disassociate the site collection from hub site, then pass the siteId as 00000000-0000-0000-0000-000000000000 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites/site\"; // join a site to a hub site await sp.site.joinHubSite(\"{parent hub site id}\"); // remove a site from a hub site await sp.site.joinHubSite(\"00000000-0000-0000-0000-000000000000\"); registerHubSite \u00b6 Registers the current site collection as hub site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites/site\"; // register current site as a hub site await sp.site.registerHubSite(); unRegisterHubSite \u00b6 Un-registers the current site collection as hub site collection. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites/site\"; // make a site no longer a hub await sp.site.unRegisterHubSite();","title":"@pnp/sp/hubsites"},{"location":"v2/sp/hubsites/#pnpsphubsites","text":"This module helps you with working with hub sites in your tenant.","title":"@pnp/sp/hubsites"},{"location":"v2/sp/hubsites/#ihubsites","text":"Scenario Import Statement Selective import { sp } from \"@pnp/sp\"; import \"@pnp/sp/hubsites\"; Preset: All import { sp, HubSites, IHubSites } from \"@pnp/sp/presets/all\";","title":"IHubSites"},{"location":"v2/sp/hubsites/#get-a-listing-of-all-hub-sites","text":"import { sp } from \"@pnp/sp\"; import { IHubSiteInfo } from \"@pnp/sp/hubsites\"; import \"@pnp/sp/hubsites\"; // invoke the hub sites object const hubsites: IHubSiteInfo[] = await sp.hubSites(); // you can also use select to only return certain fields: const hubsites2: IHubSiteInfo[] = await sp.hubSites.select(\"ID\", \"Title\", \"RelatedHubSiteIds\")();","title":"Get a Listing of All Hub sites"},{"location":"v2/sp/hubsites/#get-hub-site-by-id","text":"Using the getById method on the hubsites module to get a hub site by site Id (guid). import { sp } from \"@pnp/sp\"; import { IHubSiteInfo } from \"@pnp/sp/hubsites\"; import \"@pnp/sp/hubsites\"; const hubsite: IHubSiteInfo = await sp.hubSites.getById(\"3504348e-b2be-49fb-a2a9-2d748db64beb\")(); // log hub site title to console console.log(hubsite.Title);","title":"Get Hub site by Id"},{"location":"v2/sp/hubsites/#get-isite-instance","text":"We provide a helper method to load the ISite instance from the HubSite import { sp } from \"@pnp/sp\"; import { ISite } from \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites\"; const site: ISite = await sp.hubSites.getById(\"3504348e-b2be-49fb-a2a9-2d748db64beb\").getSite(); const siteData = await site(); console.log(siteData.Title);","title":"Get ISite instance"},{"location":"v2/sp/hubsites/#get-hub-site-data-for-a-web","text":"import { sp } from \"@pnp/sp\"; import { IHubSiteWebData } from \"@pnp/sp/hubsites\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/hubsites/web\"; const webData: Partial = await sp.web.hubSiteData(); // you can also force a refresh of the hub site data const webData2: Partial = await sp.web.hubSiteData(true);","title":"Get Hub site data for a web"},{"location":"v2/sp/hubsites/#synchubsitetheme","text":"Allows you to apply theme updates from the parent hub site collection. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/hubsites/web\"; await sp.web.syncHubSiteTheme();","title":"syncHubSiteTheme"},{"location":"v2/sp/hubsites/#hub-site-site-methods","text":"You manage hub sites at the Site level.","title":"Hub site Site Methods"},{"location":"v2/sp/hubsites/#joinhubsite","text":"Id of the hub site collection you want to join. If you want to disassociate the site collection from hub site, then pass the siteId as 00000000-0000-0000-0000-000000000000 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites/site\"; // join a site to a hub site await sp.site.joinHubSite(\"{parent hub site id}\"); // remove a site from a hub site await sp.site.joinHubSite(\"00000000-0000-0000-0000-000000000000\");","title":"joinHubSite"},{"location":"v2/sp/hubsites/#registerhubsite","text":"Registers the current site collection as hub site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites/site\"; // register current site as a hub site await sp.site.registerHubSite();","title":"registerHubSite"},{"location":"v2/sp/hubsites/#unregisterhubsite","text":"Un-registers the current site collection as hub site collection. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import \"@pnp/sp/hubsites/site\"; // make a site no longer a hub await sp.site.unRegisterHubSite();","title":"unRegisterHubSite"},{"location":"v2/sp/items/","text":"@pnp/sp/items \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; Preset: Core import { sp } from \"@pnp/sp/presets/core\"; GET \u00b6 Getting items from a list is one of the basic actions that most applications require. This is made easy through the library and the following examples demonstrate these actions. Basic Get \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // get all the items from a list const items: any[] = await sp.web.lists.getByTitle(\"My List\").items(); console.log(items); // get a specific item by id. const item: any = await sp.web.lists.getByTitle(\"My List\").items.getById(1)(); console.log(item); // use odata operators for more efficient queries const items2: any[] = await sp.web.lists.getByTitle(\"My List\").items.select(\"Title\", \"Description\").top(5).orderBy(\"Modified\", true)(); console.log(items2); Get Paged Items \u00b6 Working with paging can be a challenge as it is based on skip tokens and item ids, something that is hard to guess at runtime. To simplify things you can use the getPaged method on the Items class to assist. Note that there isn't a way to move backwards in the collection, this is by design. The pattern you should use to support backwards navigation in the results is to cache the results into a local array and use the standard array operators to get previous pages. Alternatively you can append the results to the UI, but this can have performance impact for large result sets. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // basic case to get paged items form a list let items = await sp.web.lists.getByTitle(\"BigList\").items.getPaged(); // you can also provide a type for the returned values instead of any let items = await sp.web.lists.getByTitle(\"BigList\").items.getPaged<{Title: string}[]>(); // the query also works with select to choose certain fields and top to set the page size let items = await sp.web.lists.getByTitle(\"BigList\").items.select(\"Title\", \"Description\").top(50).getPaged<{Title: string}[]>(); // the results object will have two properties and one method: // the results property will be an array of the items returned if (items.results.length > 0) { console.log(\"We got results!\"); for (let i = 0; i < items.results.length; i++) { // type checking works here if we specify the return type console.log(items.results[i].Title); } } // the hasNext property is used with the getNext method to handle paging // hasNext will be true so long as there are additional results if (items.hasNext) { // this will carry over the type specified in the original query for the results array items = await items.getNext(); console.log(items.results.length); } getListItemChangesSinceToken \u00b6 The GetListItemChangesSinceToken method allows clients to track changes on a list. Changes, including deleted items, are returned along with a token that represents the moment in time when those changes were requested. By including this token when you call GetListItemChangesSinceToken, the server looks for only those changes that have occurred since the token was generated. Sending a GetListItemChangesSinceToken request without including a token returns the list schema, the full list contents and a token. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // Using RowLimit. Enables paging let changes = await sp.web.lists.getByTitle(\"BigList\").getListItemChangesSinceToken({RowLimit: '5'}); // Use QueryOptions to make a XML-style query. // Because it's XML we need to escape special characters // Instead of & we use & in the query let changes = await sp.web.lists.getByTitle(\"BigList\").getListItemChangesSinceToken({QueryOptions: ''}); // Get everything. Using null with ChangeToken gets everything let changes = await sp.web.lists.getByTitle(\"BigList\").getListItemChangesSinceToken({ChangeToken: null}); Get All Items \u00b6 Using the items collection's getAll method you can get all of the items in a list regardless of the size of the list. Sample usage is shown below. Only the odata operations top, select, and filter are supported. usingCaching and inBatch are ignored - you will need to handle caching the results on your own. This method will write a warning to the Logger and should not frequently be used. Instead the standard paging operations should be used. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // basic usage const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.getAll(); console.log(allItems.length); // set page size const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.getAll(4000); console.log(allItems.length); // use select and top. top will set page size and override the any value passed to getAll const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.select(\"Title\").top(4000).getAll(); console.log(allItems.length); // we can also use filter as a supported odata operation, but this will likely fail on large lists const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.select(\"Title\").filter(\"Title eq 'Test'\").getAll(); console.log(allItems.length); Retrieving Lookup Fields \u00b6 When working with lookup fields you need to use the expand operator along with select to get the related fields from the lookup column. This works for both the items collection and item instances. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; const items = await sp.web.lists.getByTitle(\"LookupList\").items.select(\"Title\", \"Lookup/Title\", \"Lookup/ID\").expand(\"Lookup\")(); console.log(items); const item = await sp.web.lists.getByTitle(\"LookupList\").items.getById(1).select(\"Title\", \"Lookup/Title\", \"Lookup/ID\").expand(\"Lookup\")(); console.log(item); Filter using Metadata fields \u00b6 To filter on a metadata field you must use the getItemsByCAMLQuery method as $filter does not support these fields. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; const r = await sp.web.lists.getByTitle(\"TaxonomyList\").getItemsByCAMLQuery({ ViewXml: `Term 2`, }); Retrieving PublishingPageImage \u00b6 The PublishingPageImage and some other publishing-related fields aren't stored in normal fields, rather in the MetaInfo field. To get these values you need to use the technique shown below, and originally outlined in this thread . Note that a lot of information can be stored in this field so will pull back potentially a significant amount of data, so limit the rows as possible to aid performance. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { Web } from \"@pnp/sp/webs\"; try { const w = Web(\"https://{publishing site url}\"); const r = await w.lists.getByTitle(\"Pages\").items .select(\"Title\", \"FileRef\", \"FieldValuesAsText/MetaInfo\") .expand(\"FieldValuesAsText\") (); // look through the returned items. for (var i = 0; i < r.length; i++) { // the title field value console.log(r[i].Title); // find the value in the MetaInfo string using regex const matches = /PublishingPageImage:SW\\|(.*?)\\r\\n/ig.exec(r[i].FieldValuesAsText.MetaInfo); if (matches !== null && matches.length > 1) { // this wil be the value of the PublishingPageImage field console.log(matches[1]); } } } catch (e) { console.error(e); } Add Items \u00b6 There are several ways to add items to a list. The simplest just uses the add method of the items collection passing in the properties as a plain object. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { IItemAddResult } from \"@pnp/sp/items\"; // add an item to the list const iar: IItemAddResult = await sp.web.lists.getByTitle(\"My List\").items.add({ Title: \"Title\", Description: \"Description\" }); console.log(iar); Content Type \u00b6 You can also set the content type id when you create an item as shown in the example below. For more information on content type IDs reference the Microsoft Documentation . While this documentation references SharePoint 2010 the structure of the IDs has not changed. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; await sp.web.lists.getById(\"4D5A36EA-6E84-4160-8458-65C436DB765C\").items.add({ Title: \"Test 1\", ContentTypeId: \"0x01030058FD86C279252341AB303852303E4DAF\" }); User Fields \u00b6 There are two types of user fields, those that allow a single value and those that allow multiple. For both types, you first need to determine the Id field name, which you can do by doing a GET REST request on an existing item. Typically the value will be the user field internal name with \"Id\" appended. So in our example, we have two fields User1 and User2 so the Id fields are User1Id and User2Id. Next, you need to remember there are two types of user fields, those that take a single value and those that allow multiple - these are updated in different ways. For single value user fields you supply just the user's id. For multiple value fields, you need to supply an object with a \"results\" property and an array. Examples for both are shown below. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { getGUID } from \"@pnp/core\"; const i = await sp.web.lists.getByTitle(\"PeopleFields\").items.add({ Title: getGUID(), User1Id: 9, // allows a single user User2Id: { results: [16, 45] // allows multiple users } }); console.log(i); If you want to update or add user field values when using validateUpdateListItem you need to use the form shown below. You can specify multiple values in the array. import { sp } from \"@pnp/sp\"; const result = await sp.web.lists.getByTitle(\"UserFieldList\").items.getById(1).validateUpdateListItem([{ FieldName: \"UserField\", FieldValue: JSON.stringify([{ \"Key\": \"i:0#.f|membership|person@tenant.com\" }]), }, { FieldName: \"Title\", FieldValue: \"Test - Updated\", }]); Lookup Fields \u00b6 What is said for User Fields is, in general, relevant to Lookup Fields: Lookup Field types: Single-valued lookup Multiple-valued lookup Id suffix should be appended to the end of lookups EntityPropertyName in payloads Numeric Ids for lookups' items should be passed as values import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { getGUID } from \"@pnp/core\"; await sp.web.lists.getByTitle(\"LookupFields\").items.add({ Title: getGUID(), LookupFieldId: 2, // allows a single lookup value MultiLookupFieldId: { results: [ 1, 56 ] // allows multiple lookup value } }); Add Multiple Items \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"rapidadd\"); const entityTypeFullName = await list.getListItemEntityTypeFullName() let batch = sp.web.createBatch(); list.items.inBatch(batch).add({ Title: \"Batch 6\" }, entityTypeFullName).then(b => { console.log(b); }); list.items.inBatch(batch).add({ Title: \"Batch 7\" }, entityTypeFullName).then(b => { console.log(b); }); await batch.execute(); console.log(\"Done\"); Update \u00b6 The update method is very similar to the add method in that it takes a plain object representing the fields to update. The property names are the internal names of the fields. If you aren't sure you can always do a get request for an item in the list and see the field names that come back - you would use these same names to update the item. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); const i = await list.items.getById(1).update({ Title: \"My New Title\", Description: \"Here is a new description\" }); console.log(i); Getting and updating a collection using filter \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // you are getting back a collection here const items: any[] = await sp.web.lists.getByTitle(\"MyList\").items.top(1).filter(\"Title eq 'A Title'\")(); // see if we got something if (items.length > 0) { const updatedItem = await sp.web.lists.getByTitle(\"MyList\").items.getById(items[0].Id).update({ Title: \"Updated Title\", }); console.log(JSON.stringify(updatedItem)); } Update Multiple Items \u00b6 This approach avoids multiple calls for the same list's entity type name. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"rapidupdate\"); const entityTypeFullName = await list.getListItemEntityTypeFullName() let batch = sp.web.createBatch(); // note requirement of \"*\" eTag param - or use a specific eTag value as needed list.items.getById(1).inBatch(batch).update({ Title: \"Batch 6\" }, \"*\", entityTypeFullName).then(b => { console.log(b); }); list.items.getById(2).inBatch(batch).update({ Title: \"Batch 7\" }, \"*\", entityTypeFullName).then(b => { console.log(b); }); await batch.execute(); console.log(\"Done\") Recycle \u00b6 To send an item to the recycle bin use recycle. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); const recycleBinIdentifier = await list.items.getById(1).recycle(); Delete \u00b6 Delete is as simple as calling the .delete method. It optionally takes an eTag if you need to manage concurrency. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(1).delete(); Delete With Params \u00b6 Added in 2.0.9 Deletes the item object with options. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(1).deleteWithParams({ BypassSharedLock: true, }); The deleteWithParams method can only be used by accounts where UserToken.IsSystemAccount is true Resolving field names \u00b6 It's a very common mistake trying wrong field names in the requests. Field's EntityPropertyName value should be used. The easiest way to get know EntityPropertyName is to use the following snippet: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import \"@pnp/sp/fields\"; const response = await sp.web.lists .getByTitle('[Lists_Title]') .fields .select('Title, EntityPropertyName') .filter(`Hidden eq false and Title eq '[Field's_Display_Name]'`) (); console.log(response.map(field => { return { Title: field.Title, EntityPropertyName: field.EntityPropertyName }; })); Lookup fields' names should be ended with additional Id suffix. E.g. for Editor EntityPropertyName EditorId should be used. getParentInfos \u00b6 Added in 2.0.12 Gets information about an item, including details about the parent list, parent list root folder, and parent web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; const item: any = await sp.web.lists.getByTitle(\"My List\").items.getById(1)(); await item.getParentInfos();","title":"@pnp/sp/items"},{"location":"v2/sp/items/#pnpspitems","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; Preset: Core import { sp } from \"@pnp/sp/presets/core\";","title":"@pnp/sp/items"},{"location":"v2/sp/items/#get","text":"Getting items from a list is one of the basic actions that most applications require. This is made easy through the library and the following examples demonstrate these actions.","title":"GET"},{"location":"v2/sp/items/#basic-get","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // get all the items from a list const items: any[] = await sp.web.lists.getByTitle(\"My List\").items(); console.log(items); // get a specific item by id. const item: any = await sp.web.lists.getByTitle(\"My List\").items.getById(1)(); console.log(item); // use odata operators for more efficient queries const items2: any[] = await sp.web.lists.getByTitle(\"My List\").items.select(\"Title\", \"Description\").top(5).orderBy(\"Modified\", true)(); console.log(items2);","title":"Basic Get"},{"location":"v2/sp/items/#get-paged-items","text":"Working with paging can be a challenge as it is based on skip tokens and item ids, something that is hard to guess at runtime. To simplify things you can use the getPaged method on the Items class to assist. Note that there isn't a way to move backwards in the collection, this is by design. The pattern you should use to support backwards navigation in the results is to cache the results into a local array and use the standard array operators to get previous pages. Alternatively you can append the results to the UI, but this can have performance impact for large result sets. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // basic case to get paged items form a list let items = await sp.web.lists.getByTitle(\"BigList\").items.getPaged(); // you can also provide a type for the returned values instead of any let items = await sp.web.lists.getByTitle(\"BigList\").items.getPaged<{Title: string}[]>(); // the query also works with select to choose certain fields and top to set the page size let items = await sp.web.lists.getByTitle(\"BigList\").items.select(\"Title\", \"Description\").top(50).getPaged<{Title: string}[]>(); // the results object will have two properties and one method: // the results property will be an array of the items returned if (items.results.length > 0) { console.log(\"We got results!\"); for (let i = 0; i < items.results.length; i++) { // type checking works here if we specify the return type console.log(items.results[i].Title); } } // the hasNext property is used with the getNext method to handle paging // hasNext will be true so long as there are additional results if (items.hasNext) { // this will carry over the type specified in the original query for the results array items = await items.getNext(); console.log(items.results.length); }","title":"Get Paged Items"},{"location":"v2/sp/items/#getlistitemchangessincetoken","text":"The GetListItemChangesSinceToken method allows clients to track changes on a list. Changes, including deleted items, are returned along with a token that represents the moment in time when those changes were requested. By including this token when you call GetListItemChangesSinceToken, the server looks for only those changes that have occurred since the token was generated. Sending a GetListItemChangesSinceToken request without including a token returns the list schema, the full list contents and a token. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // Using RowLimit. Enables paging let changes = await sp.web.lists.getByTitle(\"BigList\").getListItemChangesSinceToken({RowLimit: '5'}); // Use QueryOptions to make a XML-style query. // Because it's XML we need to escape special characters // Instead of & we use & in the query let changes = await sp.web.lists.getByTitle(\"BigList\").getListItemChangesSinceToken({QueryOptions: ''}); // Get everything. Using null with ChangeToken gets everything let changes = await sp.web.lists.getByTitle(\"BigList\").getListItemChangesSinceToken({ChangeToken: null});","title":"getListItemChangesSinceToken"},{"location":"v2/sp/items/#get-all-items","text":"Using the items collection's getAll method you can get all of the items in a list regardless of the size of the list. Sample usage is shown below. Only the odata operations top, select, and filter are supported. usingCaching and inBatch are ignored - you will need to handle caching the results on your own. This method will write a warning to the Logger and should not frequently be used. Instead the standard paging operations should be used. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // basic usage const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.getAll(); console.log(allItems.length); // set page size const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.getAll(4000); console.log(allItems.length); // use select and top. top will set page size and override the any value passed to getAll const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.select(\"Title\").top(4000).getAll(); console.log(allItems.length); // we can also use filter as a supported odata operation, but this will likely fail on large lists const allItems: any[] = await sp.web.lists.getByTitle(\"BigList\").items.select(\"Title\").filter(\"Title eq 'Test'\").getAll(); console.log(allItems.length);","title":"Get All Items"},{"location":"v2/sp/items/#retrieving-lookup-fields","text":"When working with lookup fields you need to use the expand operator along with select to get the related fields from the lookup column. This works for both the items collection and item instances. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; const items = await sp.web.lists.getByTitle(\"LookupList\").items.select(\"Title\", \"Lookup/Title\", \"Lookup/ID\").expand(\"Lookup\")(); console.log(items); const item = await sp.web.lists.getByTitle(\"LookupList\").items.getById(1).select(\"Title\", \"Lookup/Title\", \"Lookup/ID\").expand(\"Lookup\")(); console.log(item);","title":"Retrieving Lookup Fields"},{"location":"v2/sp/items/#filter-using-metadata-fields","text":"To filter on a metadata field you must use the getItemsByCAMLQuery method as $filter does not support these fields. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; const r = await sp.web.lists.getByTitle(\"TaxonomyList\").getItemsByCAMLQuery({ ViewXml: `Term 2`, });","title":"Filter using Metadata fields"},{"location":"v2/sp/items/#retrieving-publishingpageimage","text":"The PublishingPageImage and some other publishing-related fields aren't stored in normal fields, rather in the MetaInfo field. To get these values you need to use the technique shown below, and originally outlined in this thread . Note that a lot of information can be stored in this field so will pull back potentially a significant amount of data, so limit the rows as possible to aid performance. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { Web } from \"@pnp/sp/webs\"; try { const w = Web(\"https://{publishing site url}\"); const r = await w.lists.getByTitle(\"Pages\").items .select(\"Title\", \"FileRef\", \"FieldValuesAsText/MetaInfo\") .expand(\"FieldValuesAsText\") (); // look through the returned items. for (var i = 0; i < r.length; i++) { // the title field value console.log(r[i].Title); // find the value in the MetaInfo string using regex const matches = /PublishingPageImage:SW\\|(.*?)\\r\\n/ig.exec(r[i].FieldValuesAsText.MetaInfo); if (matches !== null && matches.length > 1) { // this wil be the value of the PublishingPageImage field console.log(matches[1]); } } } catch (e) { console.error(e); }","title":"Retrieving PublishingPageImage"},{"location":"v2/sp/items/#add-items","text":"There are several ways to add items to a list. The simplest just uses the add method of the items collection passing in the properties as a plain object. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { IItemAddResult } from \"@pnp/sp/items\"; // add an item to the list const iar: IItemAddResult = await sp.web.lists.getByTitle(\"My List\").items.add({ Title: \"Title\", Description: \"Description\" }); console.log(iar);","title":"Add Items"},{"location":"v2/sp/items/#content-type","text":"You can also set the content type id when you create an item as shown in the example below. For more information on content type IDs reference the Microsoft Documentation . While this documentation references SharePoint 2010 the structure of the IDs has not changed. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; await sp.web.lists.getById(\"4D5A36EA-6E84-4160-8458-65C436DB765C\").items.add({ Title: \"Test 1\", ContentTypeId: \"0x01030058FD86C279252341AB303852303E4DAF\" });","title":"Content Type"},{"location":"v2/sp/items/#user-fields","text":"There are two types of user fields, those that allow a single value and those that allow multiple. For both types, you first need to determine the Id field name, which you can do by doing a GET REST request on an existing item. Typically the value will be the user field internal name with \"Id\" appended. So in our example, we have two fields User1 and User2 so the Id fields are User1Id and User2Id. Next, you need to remember there are two types of user fields, those that take a single value and those that allow multiple - these are updated in different ways. For single value user fields you supply just the user's id. For multiple value fields, you need to supply an object with a \"results\" property and an array. Examples for both are shown below. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { getGUID } from \"@pnp/core\"; const i = await sp.web.lists.getByTitle(\"PeopleFields\").items.add({ Title: getGUID(), User1Id: 9, // allows a single user User2Id: { results: [16, 45] // allows multiple users } }); console.log(i); If you want to update or add user field values when using validateUpdateListItem you need to use the form shown below. You can specify multiple values in the array. import { sp } from \"@pnp/sp\"; const result = await sp.web.lists.getByTitle(\"UserFieldList\").items.getById(1).validateUpdateListItem([{ FieldName: \"UserField\", FieldValue: JSON.stringify([{ \"Key\": \"i:0#.f|membership|person@tenant.com\" }]), }, { FieldName: \"Title\", FieldValue: \"Test - Updated\", }]);","title":"User Fields"},{"location":"v2/sp/items/#lookup-fields","text":"What is said for User Fields is, in general, relevant to Lookup Fields: Lookup Field types: Single-valued lookup Multiple-valued lookup Id suffix should be appended to the end of lookups EntityPropertyName in payloads Numeric Ids for lookups' items should be passed as values import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import { getGUID } from \"@pnp/core\"; await sp.web.lists.getByTitle(\"LookupFields\").items.add({ Title: getGUID(), LookupFieldId: 2, // allows a single lookup value MultiLookupFieldId: { results: [ 1, 56 ] // allows multiple lookup value } });","title":"Lookup Fields"},{"location":"v2/sp/items/#add-multiple-items","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"rapidadd\"); const entityTypeFullName = await list.getListItemEntityTypeFullName() let batch = sp.web.createBatch(); list.items.inBatch(batch).add({ Title: \"Batch 6\" }, entityTypeFullName).then(b => { console.log(b); }); list.items.inBatch(batch).add({ Title: \"Batch 7\" }, entityTypeFullName).then(b => { console.log(b); }); await batch.execute(); console.log(\"Done\");","title":"Add Multiple Items"},{"location":"v2/sp/items/#update","text":"The update method is very similar to the add method in that it takes a plain object representing the fields to update. The property names are the internal names of the fields. If you aren't sure you can always do a get request for an item in the list and see the field names that come back - you would use these same names to update the item. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); const i = await list.items.getById(1).update({ Title: \"My New Title\", Description: \"Here is a new description\" }); console.log(i);","title":"Update"},{"location":"v2/sp/items/#getting-and-updating-a-collection-using-filter","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; // you are getting back a collection here const items: any[] = await sp.web.lists.getByTitle(\"MyList\").items.top(1).filter(\"Title eq 'A Title'\")(); // see if we got something if (items.length > 0) { const updatedItem = await sp.web.lists.getByTitle(\"MyList\").items.getById(items[0].Id).update({ Title: \"Updated Title\", }); console.log(JSON.stringify(updatedItem)); }","title":"Getting and updating a collection using filter"},{"location":"v2/sp/items/#update-multiple-items","text":"This approach avoids multiple calls for the same list's entity type name. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"rapidupdate\"); const entityTypeFullName = await list.getListItemEntityTypeFullName() let batch = sp.web.createBatch(); // note requirement of \"*\" eTag param - or use a specific eTag value as needed list.items.getById(1).inBatch(batch).update({ Title: \"Batch 6\" }, \"*\", entityTypeFullName).then(b => { console.log(b); }); list.items.getById(2).inBatch(batch).update({ Title: \"Batch 7\" }, \"*\", entityTypeFullName).then(b => { console.log(b); }); await batch.execute(); console.log(\"Done\")","title":"Update Multiple Items"},{"location":"v2/sp/items/#recycle","text":"To send an item to the recycle bin use recycle. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); const recycleBinIdentifier = await list.items.getById(1).recycle();","title":"Recycle"},{"location":"v2/sp/items/#delete","text":"Delete is as simple as calling the .delete method. It optionally takes an eTag if you need to manage concurrency. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(1).delete();","title":"Delete"},{"location":"v2/sp/items/#delete-with-params","text":"Added in 2.0.9 Deletes the item object with options. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; let list = sp.web.lists.getByTitle(\"MyList\"); await list.items.getById(1).deleteWithParams({ BypassSharedLock: true, }); The deleteWithParams method can only be used by accounts where UserToken.IsSystemAccount is true","title":"Delete With Params"},{"location":"v2/sp/items/#resolving-field-names","text":"It's a very common mistake trying wrong field names in the requests. Field's EntityPropertyName value should be used. The easiest way to get know EntityPropertyName is to use the following snippet: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/items\"; import \"@pnp/sp/fields\"; const response = await sp.web.lists .getByTitle('[Lists_Title]') .fields .select('Title, EntityPropertyName') .filter(`Hidden eq false and Title eq '[Field's_Display_Name]'`) (); console.log(response.map(field => { return { Title: field.Title, EntityPropertyName: field.EntityPropertyName }; })); Lookup fields' names should be ended with additional Id suffix. E.g. for Editor EntityPropertyName EditorId should be used.","title":"Resolving field names"},{"location":"v2/sp/items/#getparentinfos","text":"Added in 2.0.12 Gets information about an item, including details about the parent list, parent list root folder, and parent web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; const item: any = await sp.web.lists.getByTitle(\"My List\").items.getById(1)(); await item.getParentInfos();","title":"getParentInfos"},{"location":"v2/sp/lists/","text":"@pnp/sp/lists \u00b6 Lists in SharePoint are collections of information built in a structural way using columns and rows. Columns for metadata, and rows representing each entry. Visually, it reminds us a lot of a database table or an Excel spreadsheet. ILists \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import { Lists, ILists } from \"@pnp/sp/lists\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; Preset: All import { sp, Lists, ILists } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Lists, ILists } from \"@pnp/sp/presets/core\"; Get List by Id \u00b6 Gets a list from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // get the list by Id const list = sp.web.lists.getById(\"03b05ff4-d95d-45ed-841d-3855f77a2483\"); // we can use this 'list' variable to execute more queries on the list: const r = await list.select(\"Title\")(); // show the response from the server console.log(r.Title); Get List by Title \u00b6 You can also get a list from the collection by title. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // get the default document library 'Documents' const list = sp.web.lists.getByTitle(\"Documents\"); // we can use this 'list' variable to run more queries on the list: const r = await list.select(\"Id\")(); // log the list Id to console console.log(r.Id); Add List \u00b6 You can add a list to the web's list collection using the .add-method. To invoke this method in its most simple form, you can provide only a title as a parameter. This will result in a standard out of the box list with all default settings, and the title you provide. // create a new list, passing only the title const listAddResult = await sp.web.lists.add(\"My new list\"); // we can work with the list created using the IListAddResult.list property: const r = await listAddResult.list.select(\"Title\")(); // log newly created list title to console console.log(r.Title); }); You can also provide other (optional) parameters like description, template and enableContentTypes. If that is not enough for you, you can use the parameter named 'additionalSettings' which is just a TypedHash, meaning you can sent whatever properties you'd like in the body (provided that the property is supported by the SharePoint API). You can find a listing of list template codes in the official docs. // this will create a list with template 101 (Document library), content types enabled and show it on the quick launch (using additionalSettings) const listAddResult = await sp.web.lists.add(\"My Doc Library\", \"This is a description of doc lib.\", 101, true, { OnQuickLaunch: true }); // get the Id of the newly added document library const r = await listAddResult.list.select(\"Id\")(); // log id to console console.log(r.Id); Ensure that a List exists (by title) \u00b6 Ensures that the specified list exists in the collection (note: this method not supported for batching). Just like with the add-method (see examples above) you can provide only the title, or any or all of the optional parameters desc, template, enableContentTypes and additionalSettings. // ensure that a list exists. If it doesn't it will be created with the provided title (the rest of the settings will be default): const listEnsureResult = await sp.web.lists.ensure(\"My List\"); // check if the list was created, or if it already existed: if (listEnsureResult.created) { console.log(\"My List was created!\"); } else { console.log(\"My List already existed!\"); } // work on the created/updated list const r = await listEnsureResult.list.select(\"Id\")(); // log the Id console.log(r.Id); If the list already exists, the other settings you provide will be used to update the existing list. // add a new list to the lists collection of the web sp.web.lists.add(\"My List 2\").then(async () => { // then call ensure on the created list with an updated description const listEnsureResult = await sp.web.lists.ensure(\"My List 2\", \"Updated description\"); // get the updated description const r = await listEnsureResult.list.select(\"Description\")(); // log the updated description console.log(r.Description); }); Ensure Site Assets Library exist \u00b6 Gets a list that is the default asset location for images or other files, which the users upload to their wiki pages. // get Site Assets library const siteAssetsList = await sp.web.lists.ensureSiteAssetsLibrary(); // get the Title const r = await siteAssetsList.select(\"Title\")(); // log Title console.log(r.Title); Ensure Site Pages Library exist \u00b6 Gets a list that is the default location for wiki pages. // get Site Pages library const siteAssetsList = await sp.web.lists.ensureSitePagesLibrary(); // get the Title const r = await siteAssetsList.select(\"Title\")(); // log Title console.log(r.Title); IList \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { List, IList } from \"@pnp/sp/lists\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/lists\"; Preset: All import { sp, List, IList } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, List, IList } from \"@pnp/sp/presets/core\"; Update a list \u00b6 Update an existing list with the provided properties. You can also provide an eTag value that will be used in the IF-Match header (default is \"*\") import { IListUpdateResult } from \"@pnp/sp/lists\"; // create a TypedHash object with the properties to update const updateProperties = { Description: \"This list title and description has been updated using PnPjs.\", Title: \"Updated title\", }; // update the list with the properties above list.update(updateProperties).then(async (l: IListUpdateResult) => { // get the updated title and description const r = await l.list.select(\"Title\", \"Description\")(); // log the updated properties to the console console.log(r.Title); console.log(r.Description); }); Get changes on a list \u00b6 From the change log, you can get a collection of changes that have occurred within the list based on the specified query. import { sp, IChangeQuery } from \"@pnp/sp\"; // build the changeQuery object, here we look att changes regarding Add, DeleteObject and Restore const changeQuery: IChangeQuery = { Add: true, ChangeTokenEnd: null, ChangeTokenStart: null, DeleteObject: true, Rename: true, Restore: true, }; // get list changes const r = await list.getChanges(changeQuery); // log changes to console console.log(r); Get list items using a CAML Query \u00b6 You can get items from SharePoint using a CAML Query. import { ICamlQuery } from \"@pnp/sp/lists\"; // build the caml query object (in this example, we include Title field and limit rows to 5) const caml: ICamlQuery = { ViewXml: \"5\", }; // get list items const r = await list.getItemsByCAMLQuery(caml); // log resulting array to console console.log(r); If you need to get and expand a lookup field, there is a spread array parameter on the getItemsByCAMLQuery. This means that you can provide multiple properties to this method depending on how many lookup fields you are working with on your list. Below is a minimal example showing how to expand one field (RoleAssignment) import { ICamlQuery } from \"@pnp/sp/lists\"; // build the caml query object (in this example, we include Title field and limit rows to 5) const caml: ICamlQuery = { ViewXml: \"5\", }; // get list items const r = await list.getItemsByCAMLQuery(caml, \"RoleAssignments\"); // log resulting item array to console console.log(r); Get list items changes using a Token \u00b6 import { IChangeLogItemQuery } from \"@pnp/sp/lists\"; // build the caml query object (in this example, we include Title field and limit rows to 5) const changeLogItemQuery: IChangeLogItemQuery = { Contains: `Item16`, QueryOptions: ` FALSE False TRUE FALSE My List`, }; // get list items const r = await list.getListItemChangesSinceToken(changeLogItemQuery); // log resulting XML to console console.log(r); Recycle a list \u00b6 Removes the list from the web's list collection and puts it in the recycle bin. await list.recycle(); Render list data \u00b6 import { IRenderListData } from \"@pnp/sp/lists\"; // render list data, top 5 items const r: IRenderListData = await list.renderListData(\"5\"); // log array of items in response console.log(r.Row); Render list data as stream \u00b6 import { IRenderListDataParameters } from \"@pnp/sp/lists\"; // setup parameters object const renderListDataParams: IRenderListDataParameters = { ViewXml: \"5\", }; // render list data as stream const r = await list.renderListDataAsStream(renderListDataParams); // log array of items in response console.log(r.Row); Reserve list item Id for idempotent list item creation \u00b6 const listItemId = await list.reserveListItemId(); // log id to console console.log(listItemId); Get list item entity type name \u00b6 const entityTypeFullName = await list.getListItemEntityTypeFullName(); // log entity type name console.log(entityTypeFullName); Add a list item using path (folder), validation and set field values \u00b6 const list = await sp.webs.lists.getByTitle(\"MyList\").select(\"Title\", \"ParentWebUrl\")(); const formValues: IListItemFormUpdateValue[] = [ { FieldName: \"Title\", FieldValue: title, }, ]; list.addValidateUpdateItemUsingPath(formValues,`${list.ParentWebUrl}/Lists/${list.Title}/MyFolder`) content-types imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/content-types\"; Selective 2 import \"@pnp/sp/content-types/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; contentTypes \u00b6 Get all content types for a list const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.contentTypes(); fields imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/fields\"; Selective 2 import \"@pnp/sp/fields/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; fields \u00b6 Get all the fields for a list const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.fields(); Add a field to the site, then add the site field to a list const fld = await sp.site.rootWeb.fields.addText(\"MyField\"); await sp.web.lists.getByTitle(\"MyList\").fields.createFieldAsXml(fld.data.SchemaXml); folders imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/folders\"; Selective 2 import \"@pnp/sp/folders/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; folders \u00b6 Get the root folder of a list. const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.rootFolder(); forms imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/forms\"; Selective 2 import \"@pnp/sp/forms/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; forms \u00b6 const r = await list.forms(); items imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/items\"; Selective 2 import \"@pnp/sp/items/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; items \u00b6 Get a collection of list items. const r = await list.items(); views imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/views\"; Selective 2 import \"@pnp/sp/views/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; views \u00b6 Get the default view of the list const list = sp.web.lists.getByTitle(\"Documents\"); const views = await list.views(); const defaultView = await list.defaultView(); Get a list view by Id const view = await list.getView(defaultView.Id).select(\"Title\")(); security imports \u00b6 To work with list security, you can import the list methods as follows: import \"@pnp/sp/security/list\"; For more information on how to call security methods for lists, please refer to the @pnp/sp/security documentation. subscriptions imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/subscriptions\"; Selective 2 import \"@pnp/sp/subscriptions/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; subscriptions \u00b6 Get all subscriptions on the list const list = sp.web.lists.getByTitle(\"Documents\"); const subscriptions = await list.subscriptions(); user-custom-actions imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/user-custom-actions\"; Selective 2 import \"@pnp/sp/user-custom-actions/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; userCustomActions \u00b6 Get a collection of the list's user custom actions. const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.userCustomActions(); getParentInfos \u00b6 Added in 2.0.12 Gets information about an list, including details about the parent list root folder, and parent web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; const list = sp.web.lists.getByTitle(\"Documents\"); await list.getParentInfos();","title":"@pnp/sp/lists"},{"location":"v2/sp/lists/#pnpsplists","text":"Lists in SharePoint are collections of information built in a structural way using columns and rows. Columns for metadata, and rows representing each entry. Visually, it reminds us a lot of a database table or an Excel spreadsheet.","title":"@pnp/sp/lists"},{"location":"v2/sp/lists/#ilists","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; import { Lists, ILists } from \"@pnp/sp/lists\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; Preset: All import { sp, Lists, ILists } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Lists, ILists } from \"@pnp/sp/presets/core\";","title":"ILists"},{"location":"v2/sp/lists/#get-list-by-id","text":"Gets a list from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // get the list by Id const list = sp.web.lists.getById(\"03b05ff4-d95d-45ed-841d-3855f77a2483\"); // we can use this 'list' variable to execute more queries on the list: const r = await list.select(\"Title\")(); // show the response from the server console.log(r.Title);","title":"Get List by Id"},{"location":"v2/sp/lists/#get-list-by-title","text":"You can also get a list from the collection by title. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; // get the default document library 'Documents' const list = sp.web.lists.getByTitle(\"Documents\"); // we can use this 'list' variable to run more queries on the list: const r = await list.select(\"Id\")(); // log the list Id to console console.log(r.Id);","title":"Get List by Title"},{"location":"v2/sp/lists/#add-list","text":"You can add a list to the web's list collection using the .add-method. To invoke this method in its most simple form, you can provide only a title as a parameter. This will result in a standard out of the box list with all default settings, and the title you provide. // create a new list, passing only the title const listAddResult = await sp.web.lists.add(\"My new list\"); // we can work with the list created using the IListAddResult.list property: const r = await listAddResult.list.select(\"Title\")(); // log newly created list title to console console.log(r.Title); }); You can also provide other (optional) parameters like description, template and enableContentTypes. If that is not enough for you, you can use the parameter named 'additionalSettings' which is just a TypedHash, meaning you can sent whatever properties you'd like in the body (provided that the property is supported by the SharePoint API). You can find a listing of list template codes in the official docs. // this will create a list with template 101 (Document library), content types enabled and show it on the quick launch (using additionalSettings) const listAddResult = await sp.web.lists.add(\"My Doc Library\", \"This is a description of doc lib.\", 101, true, { OnQuickLaunch: true }); // get the Id of the newly added document library const r = await listAddResult.list.select(\"Id\")(); // log id to console console.log(r.Id);","title":"Add List"},{"location":"v2/sp/lists/#ensure-that-a-list-exists-by-title","text":"Ensures that the specified list exists in the collection (note: this method not supported for batching). Just like with the add-method (see examples above) you can provide only the title, or any or all of the optional parameters desc, template, enableContentTypes and additionalSettings. // ensure that a list exists. If it doesn't it will be created with the provided title (the rest of the settings will be default): const listEnsureResult = await sp.web.lists.ensure(\"My List\"); // check if the list was created, or if it already existed: if (listEnsureResult.created) { console.log(\"My List was created!\"); } else { console.log(\"My List already existed!\"); } // work on the created/updated list const r = await listEnsureResult.list.select(\"Id\")(); // log the Id console.log(r.Id); If the list already exists, the other settings you provide will be used to update the existing list. // add a new list to the lists collection of the web sp.web.lists.add(\"My List 2\").then(async () => { // then call ensure on the created list with an updated description const listEnsureResult = await sp.web.lists.ensure(\"My List 2\", \"Updated description\"); // get the updated description const r = await listEnsureResult.list.select(\"Description\")(); // log the updated description console.log(r.Description); });","title":"Ensure that a List exists (by title)"},{"location":"v2/sp/lists/#ensure-site-assets-library-exist","text":"Gets a list that is the default asset location for images or other files, which the users upload to their wiki pages. // get Site Assets library const siteAssetsList = await sp.web.lists.ensureSiteAssetsLibrary(); // get the Title const r = await siteAssetsList.select(\"Title\")(); // log Title console.log(r.Title);","title":"Ensure Site Assets Library exist"},{"location":"v2/sp/lists/#ensure-site-pages-library-exist","text":"Gets a list that is the default location for wiki pages. // get Site Pages library const siteAssetsList = await sp.web.lists.ensureSitePagesLibrary(); // get the Title const r = await siteAssetsList.select(\"Title\")(); // log Title console.log(r.Title);","title":"Ensure Site Pages Library exist"},{"location":"v2/sp/lists/#ilist","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { List, IList } from \"@pnp/sp/lists\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/lists\"; Preset: All import { sp, List, IList } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, List, IList } from \"@pnp/sp/presets/core\";","title":"IList"},{"location":"v2/sp/lists/#update-a-list","text":"Update an existing list with the provided properties. You can also provide an eTag value that will be used in the IF-Match header (default is \"*\") import { IListUpdateResult } from \"@pnp/sp/lists\"; // create a TypedHash object with the properties to update const updateProperties = { Description: \"This list title and description has been updated using PnPjs.\", Title: \"Updated title\", }; // update the list with the properties above list.update(updateProperties).then(async (l: IListUpdateResult) => { // get the updated title and description const r = await l.list.select(\"Title\", \"Description\")(); // log the updated properties to the console console.log(r.Title); console.log(r.Description); });","title":"Update a list"},{"location":"v2/sp/lists/#get-changes-on-a-list","text":"From the change log, you can get a collection of changes that have occurred within the list based on the specified query. import { sp, IChangeQuery } from \"@pnp/sp\"; // build the changeQuery object, here we look att changes regarding Add, DeleteObject and Restore const changeQuery: IChangeQuery = { Add: true, ChangeTokenEnd: null, ChangeTokenStart: null, DeleteObject: true, Rename: true, Restore: true, }; // get list changes const r = await list.getChanges(changeQuery); // log changes to console console.log(r);","title":"Get changes on a list"},{"location":"v2/sp/lists/#get-list-items-using-a-caml-query","text":"You can get items from SharePoint using a CAML Query. import { ICamlQuery } from \"@pnp/sp/lists\"; // build the caml query object (in this example, we include Title field and limit rows to 5) const caml: ICamlQuery = { ViewXml: \"5\", }; // get list items const r = await list.getItemsByCAMLQuery(caml); // log resulting array to console console.log(r); If you need to get and expand a lookup field, there is a spread array parameter on the getItemsByCAMLQuery. This means that you can provide multiple properties to this method depending on how many lookup fields you are working with on your list. Below is a minimal example showing how to expand one field (RoleAssignment) import { ICamlQuery } from \"@pnp/sp/lists\"; // build the caml query object (in this example, we include Title field and limit rows to 5) const caml: ICamlQuery = { ViewXml: \"5\", }; // get list items const r = await list.getItemsByCAMLQuery(caml, \"RoleAssignments\"); // log resulting item array to console console.log(r);","title":"Get list items using a CAML Query"},{"location":"v2/sp/lists/#get-list-items-changes-using-a-token","text":"import { IChangeLogItemQuery } from \"@pnp/sp/lists\"; // build the caml query object (in this example, we include Title field and limit rows to 5) const changeLogItemQuery: IChangeLogItemQuery = { Contains: `Item16`, QueryOptions: ` FALSE False TRUE FALSE My List`, }; // get list items const r = await list.getListItemChangesSinceToken(changeLogItemQuery); // log resulting XML to console console.log(r);","title":"Get list items changes using a Token"},{"location":"v2/sp/lists/#recycle-a-list","text":"Removes the list from the web's list collection and puts it in the recycle bin. await list.recycle();","title":"Recycle a list"},{"location":"v2/sp/lists/#render-list-data","text":"import { IRenderListData } from \"@pnp/sp/lists\"; // render list data, top 5 items const r: IRenderListData = await list.renderListData(\"5\"); // log array of items in response console.log(r.Row);","title":"Render list data"},{"location":"v2/sp/lists/#render-list-data-as-stream","text":"import { IRenderListDataParameters } from \"@pnp/sp/lists\"; // setup parameters object const renderListDataParams: IRenderListDataParameters = { ViewXml: \"5\", }; // render list data as stream const r = await list.renderListDataAsStream(renderListDataParams); // log array of items in response console.log(r.Row);","title":"Render list data as stream"},{"location":"v2/sp/lists/#reserve-list-item-id-for-idempotent-list-item-creation","text":"const listItemId = await list.reserveListItemId(); // log id to console console.log(listItemId);","title":"Reserve list item Id for idempotent list item creation"},{"location":"v2/sp/lists/#get-list-item-entity-type-name","text":"const entityTypeFullName = await list.getListItemEntityTypeFullName(); // log entity type name console.log(entityTypeFullName);","title":"Get list item entity type name"},{"location":"v2/sp/lists/#add-a-list-item-using-path-folder-validation-and-set-field-values","text":"const list = await sp.webs.lists.getByTitle(\"MyList\").select(\"Title\", \"ParentWebUrl\")(); const formValues: IListItemFormUpdateValue[] = [ { FieldName: \"Title\", FieldValue: title, }, ]; list.addValidateUpdateItemUsingPath(formValues,`${list.ParentWebUrl}/Lists/${list.Title}/MyFolder`)","title":"Add a list item using path (folder), validation and set field values"},{"location":"v2/sp/lists/#content-types-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/content-types\"; Selective 2 import \"@pnp/sp/content-types/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"content-types imports"},{"location":"v2/sp/lists/#contenttypes","text":"Get all content types for a list const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.contentTypes();","title":"contentTypes"},{"location":"v2/sp/lists/#fields-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/fields\"; Selective 2 import \"@pnp/sp/fields/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"fields imports"},{"location":"v2/sp/lists/#fields","text":"Get all the fields for a list const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.fields(); Add a field to the site, then add the site field to a list const fld = await sp.site.rootWeb.fields.addText(\"MyField\"); await sp.web.lists.getByTitle(\"MyList\").fields.createFieldAsXml(fld.data.SchemaXml);","title":"fields"},{"location":"v2/sp/lists/#folders-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/folders\"; Selective 2 import \"@pnp/sp/folders/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"folders imports"},{"location":"v2/sp/lists/#folders","text":"Get the root folder of a list. const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.rootFolder();","title":"folders"},{"location":"v2/sp/lists/#forms-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/forms\"; Selective 2 import \"@pnp/sp/forms/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"forms imports"},{"location":"v2/sp/lists/#forms","text":"const r = await list.forms();","title":"forms"},{"location":"v2/sp/lists/#items-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/items\"; Selective 2 import \"@pnp/sp/items/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"items imports"},{"location":"v2/sp/lists/#items","text":"Get a collection of list items. const r = await list.items();","title":"items"},{"location":"v2/sp/lists/#views-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/views\"; Selective 2 import \"@pnp/sp/views/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"views imports"},{"location":"v2/sp/lists/#views","text":"Get the default view of the list const list = sp.web.lists.getByTitle(\"Documents\"); const views = await list.views(); const defaultView = await list.defaultView(); Get a list view by Id const view = await list.getView(defaultView.Id).select(\"Title\")();","title":"views"},{"location":"v2/sp/lists/#security-imports","text":"To work with list security, you can import the list methods as follows: import \"@pnp/sp/security/list\"; For more information on how to call security methods for lists, please refer to the @pnp/sp/security documentation.","title":"security imports"},{"location":"v2/sp/lists/#subscriptions-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/subscriptions\"; Selective 2 import \"@pnp/sp/subscriptions/list\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"subscriptions imports"},{"location":"v2/sp/lists/#subscriptions","text":"Get all subscriptions on the list const list = sp.web.lists.getByTitle(\"Documents\"); const subscriptions = await list.subscriptions();","title":"subscriptions"},{"location":"v2/sp/lists/#user-custom-actions-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/user-custom-actions\"; Selective 2 import \"@pnp/sp/user-custom-actions/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"user-custom-actions imports"},{"location":"v2/sp/lists/#usercustomactions","text":"Get a collection of the list's user custom actions. const list = sp.web.lists.getByTitle(\"Documents\"); const r = await list.userCustomActions();","title":"userCustomActions"},{"location":"v2/sp/lists/#getparentinfos","text":"Added in 2.0.12 Gets information about an list, including details about the parent list root folder, and parent web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/items\"; const list = sp.web.lists.getByTitle(\"Documents\"); await list.getParentInfos();","title":"getParentInfos"},{"location":"v2/sp/navigation/","text":"@pnp/sp - navigation \u00b6 Navigation Service \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/navigation\"; getMenuState \u00b6 The MenuState service operation returns a Menu-State (dump) of a SiteMapProvider on a site. It will return an exception if the SiteMapProvider cannot be found on the site, the SiteMapProvider does not implement the IEditableSiteMapProvider interface or the SiteMapNode key cannot be found within the provider hierarchy. The IEditableSiteMapProvider also supports Custom Properties which is an optional feature. What will be return in the custom properties is up to the IEditableSiteMapProvider implementation and can differ for for each SiteMapProvider implementation. The custom properties can be requested by providing a comma separated string of property names like: property1,property2,property3\\,containingcomma NOTE: the , separator can be escaped using the \\ as escape character as done in the example above. The string above would split like: property1 property2 property3,containingcomma import { sp } from \"@pnp/sp\"; import \"@pnp/sp/navigation\"; // Will return a menu state of the default SiteMapProvider 'SPSiteMapProvider' where the dump starts a the RootNode (within the site) with a depth of 10 levels. const state = await sp.navigation.getMenuState(); // Will return the menu state of the 'SPSiteMapProvider', starting with the node with the key '1002' with a depth of 5 const state2 = await sp.navigation.getMenuState(\"1002\", 5); // Will return the menu state of the 'CurrentNavSiteMapProviderNoEncode' from the root node of the provider with a depth of 5 const state3 = await sp.navigation.getMenuState(null, 5, \"CurrentNavSiteMapProviderNoEncode\"); getMenuNodeKey \u00b6 Tries to get a SiteMapNode.Key for a given URL within a site collection. If the SiteMapNode cannot be found an Exception is returned. The method is using SiteMapProvider.FindSiteMapNodeFromKey(string rawUrl) to lookup the SiteMapNode. Depending on the actual implementation of FindSiteMapNodeFromKey the matching can differ for different SiteMapProviders. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/navigation\"; const key = await sp.navigation.getMenuNodeKey(\"/sites/dev/Lists/SPPnPJSExampleList/AllItems.aspx\"); Web Navigation \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; The navigation object contains two properties \"quicklaunch\" and \"topnavigationbar\". Both have the same set of methods so our examples below show use of only quicklaunch but apply equally to topnavigationbar. Get navigation \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const top = await sp.web.navigation.topNavigationBar(); const quick = await sp.web.navigation.quicklaunch(); For the following examples we will refer to a variable named \"nav\" that is understood to be one of topNavigationBar or quicklaunch. getById \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const node = await nav.getById(3)(); add \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const result = await nav.add(\"Node Title\", \"/sites/dev/pages/mypage.aspx\", true); const nodeDataRaw = result.data; // request the data from the created node const nodeData = result.node(); moveAfter \u00b6 Places a navigation node after another node in the tree import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const node1result = await nav.add(`Testing - ${getRandomString(4)} (1)`, url, true); const node2result = await nav.add(`Testing - ${getRandomString(4)} (2)`, url, true); const node1 = await node1result.node(); const node2 = await node2result.node(); await nav.moveAfter(node1.Id, node2.Id); Delete \u00b6 Deletes a given node import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const node1result = await nav.add(`Testing - ${getRandomString(4)}`, url, true); let nodes = await nav(); // check we added a node let index = nodes.findIndex(n => n.Id === node1result.data.Id) // index >= 0 // delete a node await nav.getById(node1result.data.Id).delete(); nodes = await nav(); index = nodes.findIndex(n => n.Id === node1result.data.Id) // index = -1 Update \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; await nav.getById(4).update({ Title: \"A new title\", }); Children \u00b6 The children property of a Navigation Node represents a collection with all the same properties and methods available on topNavigationBar or quicklaunch. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const childrenData = await nav.getById(1).children(); // add a child await nav.getById(1).children.add(\"Title\", \"Url\", true);","title":"@pnp/sp - navigation"},{"location":"v2/sp/navigation/#pnpsp-navigation","text":"","title":"@pnp/sp - navigation"},{"location":"v2/sp/navigation/#navigation-service","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/navigation\";","title":"Navigation Service"},{"location":"v2/sp/navigation/#getmenustate","text":"The MenuState service operation returns a Menu-State (dump) of a SiteMapProvider on a site. It will return an exception if the SiteMapProvider cannot be found on the site, the SiteMapProvider does not implement the IEditableSiteMapProvider interface or the SiteMapNode key cannot be found within the provider hierarchy. The IEditableSiteMapProvider also supports Custom Properties which is an optional feature. What will be return in the custom properties is up to the IEditableSiteMapProvider implementation and can differ for for each SiteMapProvider implementation. The custom properties can be requested by providing a comma separated string of property names like: property1,property2,property3\\,containingcomma NOTE: the , separator can be escaped using the \\ as escape character as done in the example above. The string above would split like: property1 property2 property3,containingcomma import { sp } from \"@pnp/sp\"; import \"@pnp/sp/navigation\"; // Will return a menu state of the default SiteMapProvider 'SPSiteMapProvider' where the dump starts a the RootNode (within the site) with a depth of 10 levels. const state = await sp.navigation.getMenuState(); // Will return the menu state of the 'SPSiteMapProvider', starting with the node with the key '1002' with a depth of 5 const state2 = await sp.navigation.getMenuState(\"1002\", 5); // Will return the menu state of the 'CurrentNavSiteMapProviderNoEncode' from the root node of the provider with a depth of 5 const state3 = await sp.navigation.getMenuState(null, 5, \"CurrentNavSiteMapProviderNoEncode\");","title":"getMenuState"},{"location":"v2/sp/navigation/#getmenunodekey","text":"Tries to get a SiteMapNode.Key for a given URL within a site collection. If the SiteMapNode cannot be found an Exception is returned. The method is using SiteMapProvider.FindSiteMapNodeFromKey(string rawUrl) to lookup the SiteMapNode. Depending on the actual implementation of FindSiteMapNodeFromKey the matching can differ for different SiteMapProviders. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/navigation\"; const key = await sp.navigation.getMenuNodeKey(\"/sites/dev/Lists/SPPnPJSExampleList/AllItems.aspx\");","title":"getMenuNodeKey"},{"location":"v2/sp/navigation/#web-navigation","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; The navigation object contains two properties \"quicklaunch\" and \"topnavigationbar\". Both have the same set of methods so our examples below show use of only quicklaunch but apply equally to topnavigationbar.","title":"Web Navigation"},{"location":"v2/sp/navigation/#get-navigation","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const top = await sp.web.navigation.topNavigationBar(); const quick = await sp.web.navigation.quicklaunch(); For the following examples we will refer to a variable named \"nav\" that is understood to be one of topNavigationBar or quicklaunch.","title":"Get navigation"},{"location":"v2/sp/navigation/#getbyid","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const node = await nav.getById(3)();","title":"getById"},{"location":"v2/sp/navigation/#add","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const result = await nav.add(\"Node Title\", \"/sites/dev/pages/mypage.aspx\", true); const nodeDataRaw = result.data; // request the data from the created node const nodeData = result.node();","title":"add"},{"location":"v2/sp/navigation/#moveafter","text":"Places a navigation node after another node in the tree import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const node1result = await nav.add(`Testing - ${getRandomString(4)} (1)`, url, true); const node2result = await nav.add(`Testing - ${getRandomString(4)} (2)`, url, true); const node1 = await node1result.node(); const node2 = await node2result.node(); await nav.moveAfter(node1.Id, node2.Id);","title":"moveAfter"},{"location":"v2/sp/navigation/#delete","text":"Deletes a given node import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const node1result = await nav.add(`Testing - ${getRandomString(4)}`, url, true); let nodes = await nav(); // check we added a node let index = nodes.findIndex(n => n.Id === node1result.data.Id) // index >= 0 // delete a node await nav.getById(node1result.data.Id).delete(); nodes = await nav(); index = nodes.findIndex(n => n.Id === node1result.data.Id) // index = -1","title":"Delete"},{"location":"v2/sp/navigation/#update","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; await nav.getById(4).update({ Title: \"A new title\", });","title":"Update"},{"location":"v2/sp/navigation/#children","text":"The children property of a Navigation Node represents a collection with all the same properties and methods available on topNavigationBar or quicklaunch. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/navigation\"; const childrenData = await nav.getById(1).children(); // add a child await nav.getById(1).children.add(\"Title\", \"Url\", true);","title":"Children"},{"location":"v2/sp/permissions/","text":"@pnp/sp - permissions \u00b6 A common task is to determine if a user or the current user has a certain permission level. It is a great idea to check before performing a task such as creating a list to ensure a user can without getting back an error. This allows you to provide a better experience to the user. Permissions in SharePoint are assigned to the set of securable objects which include Site, Web, List, and List Item. These are the four level to which unique permissions can be assigned. As such @pnp/sp provides a set of methods defined in the QueryableSecurable class to handle these permissions. These examples all use the Web to get the values, however the methods work identically on all securables. Get Role Assignments \u00b6 This gets a collection of all the role assignments on a given securable. The property returns a RoleAssignments collection which supports the OData collection operators. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/security\"; import { Logger } from \"@pnp/logging\"; const roles = await sp.web.roleAssignments(); Logger.writeJSON(roles); First Unique Ancestor Securable Object \u00b6 This method can be used to find the securable parent up the hierarchy that has unique permissions. If everything inherits permissions this will be the Site. If a sub web has unique permissions it will be the web, and so on. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/security\"; import { Logger } from \"@pnp/logging\"; const obj = await sp.web.firstUniqueAncestorSecurableObject(); Logger.writeJSON(obj); User Effective Permissions \u00b6 This method returns the BasePermissions for a given user or the current user. This value contains the High and Low values for a user on the securable you have queried. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/security\"; import { Logger } from \"@pnp/logging\"; const perms = await sp.web.getUserEffectivePermissions(\"i:0#.f|membership|user@site.com\"); Logger.writeJSON(perms); const perms2 = await sp.web.getCurrentUserEffectivePermissions(); Logger.writeJSON(perms2); User Has Permissions \u00b6 Because the High and Low values in the BasePermission don't obviously mean anything you can use these methods along with the PermissionKind enumeration to check actual rights on the securable. import { sp } from \"@pnp/sp\"; import { PermissionKind } from \"@pnp/sp/security\"; const perms = await sp.web.userHasPermissions(\"i:0#.f|membership|user@site.com\", PermissionKind.ApproveItems); console.log(perms); const perms2 = await sp.web.currentUserHasPermissions(PermissionKind.ApproveItems); console.log(perms2); Has Permissions \u00b6 If you need to check multiple permissions it can be more efficient to get the BasePermissions once and then use the hasPermissions method to check them as shown below. import { sp } from \"@pnp/sp\"; import { PermissionKind } from \"@pnp/sp/security\"; const perms = await sp.web.getCurrentUserEffectivePermissions(); if (sp.web.hasPermissions(perms, PermissionKind.AddListItems) && sp.web.hasPermissions(perms, PermissionKind.DeleteVersions)) { // ... }","title":"@pnp/sp - permissions"},{"location":"v2/sp/permissions/#pnpsp-permissions","text":"A common task is to determine if a user or the current user has a certain permission level. It is a great idea to check before performing a task such as creating a list to ensure a user can without getting back an error. This allows you to provide a better experience to the user. Permissions in SharePoint are assigned to the set of securable objects which include Site, Web, List, and List Item. These are the four level to which unique permissions can be assigned. As such @pnp/sp provides a set of methods defined in the QueryableSecurable class to handle these permissions. These examples all use the Web to get the values, however the methods work identically on all securables.","title":"@pnp/sp - permissions"},{"location":"v2/sp/permissions/#get-role-assignments","text":"This gets a collection of all the role assignments on a given securable. The property returns a RoleAssignments collection which supports the OData collection operators. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/security\"; import { Logger } from \"@pnp/logging\"; const roles = await sp.web.roleAssignments(); Logger.writeJSON(roles);","title":"Get Role Assignments"},{"location":"v2/sp/permissions/#first-unique-ancestor-securable-object","text":"This method can be used to find the securable parent up the hierarchy that has unique permissions. If everything inherits permissions this will be the Site. If a sub web has unique permissions it will be the web, and so on. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/security\"; import { Logger } from \"@pnp/logging\"; const obj = await sp.web.firstUniqueAncestorSecurableObject(); Logger.writeJSON(obj);","title":"First Unique Ancestor Securable Object"},{"location":"v2/sp/permissions/#user-effective-permissions","text":"This method returns the BasePermissions for a given user or the current user. This value contains the High and Low values for a user on the securable you have queried. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/security\"; import { Logger } from \"@pnp/logging\"; const perms = await sp.web.getUserEffectivePermissions(\"i:0#.f|membership|user@site.com\"); Logger.writeJSON(perms); const perms2 = await sp.web.getCurrentUserEffectivePermissions(); Logger.writeJSON(perms2);","title":"User Effective Permissions"},{"location":"v2/sp/permissions/#user-has-permissions","text":"Because the High and Low values in the BasePermission don't obviously mean anything you can use these methods along with the PermissionKind enumeration to check actual rights on the securable. import { sp } from \"@pnp/sp\"; import { PermissionKind } from \"@pnp/sp/security\"; const perms = await sp.web.userHasPermissions(\"i:0#.f|membership|user@site.com\", PermissionKind.ApproveItems); console.log(perms); const perms2 = await sp.web.currentUserHasPermissions(PermissionKind.ApproveItems); console.log(perms2);","title":"User Has Permissions"},{"location":"v2/sp/permissions/#has-permissions","text":"If you need to check multiple permissions it can be more efficient to get the BasePermissions once and then use the hasPermissions method to check them as shown below. import { sp } from \"@pnp/sp\"; import { PermissionKind } from \"@pnp/sp/security\"; const perms = await sp.web.getCurrentUserEffectivePermissions(); if (sp.web.hasPermissions(perms, PermissionKind.AddListItems) && sp.web.hasPermissions(perms, PermissionKind.DeleteVersions)) { // ... }","title":"Has Permissions"},{"location":"v2/sp/profiles/","text":"@pnp/sp/profiles \u00b6 The profile services allows you to work with the SharePoint User Profile Store. Profiles \u00b6 Profiles is accessed directly from the root sp object. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/profiles\"; Get edit profile link for the current user \u00b6 editProfileLink(): Promise const editProfileLink = await sp.profiles.editProfileLink(); console.log(\"My edit profile link =\" + editProfileLink); Is My People List Public \u00b6 Provides a boolean that indicates if the current users \"People I'm Following\" list is public or not isMyPeopleListPublic(): Promise const isPublic = await sp.profiles.isMyPeopleListPublic(); console.log(\"Is my Following list Public =\" + isPubic); Find out if the current user is followed by another user \u00b6 Provides a boolean that indicates if the current users is followed by a specific user. amIFollowedBy(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const isFollowedBy = await sp.profiles.amIFollowedBy(loginName); console.log(\"Is \" + loginName + \" following me? \" + isFollowedBy); Find out if I am following a specific user \u00b6 Provides a boolean that indicates if the current users is followed by a specific user. amIFollowing(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const following = await sp.profiles.amIFollowing(loginName); console.log(\"Am I following \" + loginName + \"? \" + following); Get the tags I follow \u00b6 Gets the tags the current user is following. Accepts max count, default is 20. getFollowedTags(maxCount = 20): Promise const tags = await sp.profiles.getFollowedTags(); console.log(tags); Get followers for a specific user \u00b6 Gets the people who are following the specified user. getFollowersFor(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const followers = await sp.profiles.getFollowersFor(loginName); followers.forEach((value) => { console.log(value); }); Get followers for the current \u00b6 Gets the people who are following the current user. myFollowers(): ISharePointQueryableCollection const folowers = await sp.profiles.myFollowers(); console.log(folowers); Get the properties for the current user \u00b6 Gets user properties for the current user. myProperties(): _SharePointQueryableInstance const profile = await sp.profiles.myProperties(); console.log(profile.DisplayName); console.log(profile.Email); console.log(profile.Title); console.log(profile.UserProfileProperties.length); // Properties are stored in Key/Value pairs, // so parse into an object called userProperties var props = {}; profile.UserProfileProperties.forEach((prop) => { props[prop.Key] = prop.Value; }); profile.userProperties = props; console.log(\"Account Name: \" + profile.userProperties.AccountName); Gets people specified user is following \u00b6 getPeopleFollowedBy(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const folowers = await sp.profiles.getFollowersFor(loginName); followers.forEach((value) => { console.log(value); }); Gets properties for a specified user \u00b6 getPropertiesFor(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const profile = await sp.profiles.getPropertiesFor(loginName); console.log(profile.DisplayName); console.log(profile.Email); console.log(profile.Title); console.log(profile.UserProfileProperties.length); // Properties are stored in inconvenient Key/Value pairs, // so parse into an object called userProperties var props = {}; profile.UserProfileProperties.forEach((prop) => { props[prop.Key] = prop.Value; }); profile.userProperties = props; console.log(\"Account Name: \" + profile.userProperties.AccountName); Gets most popular tags \u00b6 Gets the 20 most popular hash tags over the past week, sorted so that the most popular tag appears first trendingTags(): Promise const tags = await sp.profiles.trendingTags(); tags.Items.forEach((tag) => { console.log(tag); }); Gets specified user profile property for the specified user \u00b6 getUserProfilePropertyFor(loginName: string, propertyName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const propertyName = \"AccountName\"; const property = await sp.profiles.getUserProfilePropertyFor(loginName, propertyName); console.log(property); Hide specific user from list of suggested people \u00b6 Removes the specified user from the user's list of suggested people to follow. hideSuggestion(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; await sp.profiles.hideSuggestion(loginName); Is one user following another \u00b6 Indicates whether the first user is following the second user. First parameter is the account name of the user who might be following the followee. Second parameter is the account name of the user who might be followed by the follower. isFollowing(follower: string, followee: string): Promise const follower = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const followee = \"i:0#.f|membership|testuser2@mytenant.onmicrosoft.com\"; const isFollowing = await sp.profiles.isFollowing(follower, followee); console.log(isFollowing); Set User Profile Picture \u00b6 Uploads and sets the user profile picture (Users can upload a picture to their own profile only). Not supported for batching. Accepts the profilePicSource Blob data representing the user's picture in BMP, JPEG, or PNG format of up to 4.76MB. setMyProfilePic(profilePicSource: Blob): Promise import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/profiles\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/files\"; // get the blob object through a request or from a file input const blob = await sp.web.lists.getByTitle(\"Documents\").rootFolder.files.getByName(\"profile.jpg\").getBlob(); await sp.profiles.setMyProfilePic(blob); Sets single value User Profile property \u00b6 accountName The account name of the user propertyName Property name propertyValue Property value setSingleValueProfileProperty(accountName: string, propertyName: string, propertyValue: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; await sp.profiles.setSingleValueProfileProperty(loginName, \"CellPhone\", \"(123) 555-1212\"); Sets a mult-value User Profile property \u00b6 accountName The account name of the user propertyName Property name propertyValues Property values setMultiValuedProfileProperty(accountName: string, propertyName: string, propertyValues: string[]): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const propertyName = \"SPS-Skills\"; const propertyValues = [\"SharePoint\", \"Office 365\", \"Architecture\", \"Azure\"]; await sp.profiles.setMultiValuedProfileProperty(loginName, propertyName, propertyValues); const profile = await sp.profiles.getPropertiesFor(loginName); var props = {}; profile.UserProfileProperties.forEach((prop) => { props[prop.Key] = prop.Value; }); profile.userProperties = props; console.log(profile.userProperties[propertyName]); Create Personal Site for specified users \u00b6 Provisions one or more users' personal sites. (My Site administrator on SharePoint Online only) Emails The email addresses of the users to provision sites for createPersonalSiteEnqueueBulk(...emails: string[]): Promise let userEmails: string[] = [\"testuser1@mytenant.onmicrosoft.com\", \"testuser2@mytenant.onmicrosoft.com\"]; await sp.profiles.createPersonalSiteEnqueueBulk(userEmails); Get the user profile of the owner for the current site \u00b6 ownerUserProfile(): Promise const profile = await sp.profiles.ownerUserProfile(); console.log(profile); Get the user profile of the current user \u00b6 userProfile(): Promise const profile = await sp.profiles.userProfile(); console.log(profile); Create personal site for current user \u00b6 createPersonalSite(interactiveRequest = false): Promise await sp.profiles.createPersonalSite(); Make all profile data public or private \u00b6 Set the privacy settings for all social data. shareAllSocialData(share: boolean): Promise await sp.profiles.shareAllSocialData(true); Resolve a user or group \u00b6 Resolves user or group using specified query parameters clientPeoplePickerResolveUser(queryParams: IClientPeoplePickerQueryParameters): Promise const result = await sp.profiles.clientPeoplePickerSearchUser({ AllowEmailAddresses: true, AllowMultipleEntities: false, MaximumEntitySuggestions: 25, QueryString: 'John' }); console.log(result); Search a user or group \u00b6 Searches for users or groups using specified query parameters clientPeoplePickerSearchUser(queryParams: IClientPeoplePickerQueryParameters): Promise const result = await sp.profiles.clientPeoplePickerSearchUser({ AllowEmailAddresses: true, AllowMultipleEntities: false, MaximumEntitySuggestions: 25, QueryString: 'John' }); console.log(result);","title":"@pnp/sp/profiles"},{"location":"v2/sp/profiles/#pnpspprofiles","text":"The profile services allows you to work with the SharePoint User Profile Store.","title":"@pnp/sp/profiles"},{"location":"v2/sp/profiles/#profiles","text":"Profiles is accessed directly from the root sp object. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/profiles\";","title":"Profiles"},{"location":"v2/sp/profiles/#get-edit-profile-link-for-the-current-user","text":"editProfileLink(): Promise const editProfileLink = await sp.profiles.editProfileLink(); console.log(\"My edit profile link =\" + editProfileLink);","title":"Get edit profile link for the current user"},{"location":"v2/sp/profiles/#is-my-people-list-public","text":"Provides a boolean that indicates if the current users \"People I'm Following\" list is public or not isMyPeopleListPublic(): Promise const isPublic = await sp.profiles.isMyPeopleListPublic(); console.log(\"Is my Following list Public =\" + isPubic);","title":"Is My People List Public"},{"location":"v2/sp/profiles/#find-out-if-the-current-user-is-followed-by-another-user","text":"Provides a boolean that indicates if the current users is followed by a specific user. amIFollowedBy(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const isFollowedBy = await sp.profiles.amIFollowedBy(loginName); console.log(\"Is \" + loginName + \" following me? \" + isFollowedBy);","title":"Find out if the current user is followed by another user"},{"location":"v2/sp/profiles/#find-out-if-i-am-following-a-specific-user","text":"Provides a boolean that indicates if the current users is followed by a specific user. amIFollowing(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const following = await sp.profiles.amIFollowing(loginName); console.log(\"Am I following \" + loginName + \"? \" + following);","title":"Find out if I am following a specific user"},{"location":"v2/sp/profiles/#get-the-tags-i-follow","text":"Gets the tags the current user is following. Accepts max count, default is 20. getFollowedTags(maxCount = 20): Promise const tags = await sp.profiles.getFollowedTags(); console.log(tags);","title":"Get the tags I follow"},{"location":"v2/sp/profiles/#get-followers-for-a-specific-user","text":"Gets the people who are following the specified user. getFollowersFor(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const followers = await sp.profiles.getFollowersFor(loginName); followers.forEach((value) => { console.log(value); });","title":"Get followers for a specific user"},{"location":"v2/sp/profiles/#get-followers-for-the-current","text":"Gets the people who are following the current user. myFollowers(): ISharePointQueryableCollection const folowers = await sp.profiles.myFollowers(); console.log(folowers);","title":"Get followers for the current"},{"location":"v2/sp/profiles/#get-the-properties-for-the-current-user","text":"Gets user properties for the current user. myProperties(): _SharePointQueryableInstance const profile = await sp.profiles.myProperties(); console.log(profile.DisplayName); console.log(profile.Email); console.log(profile.Title); console.log(profile.UserProfileProperties.length); // Properties are stored in Key/Value pairs, // so parse into an object called userProperties var props = {}; profile.UserProfileProperties.forEach((prop) => { props[prop.Key] = prop.Value; }); profile.userProperties = props; console.log(\"Account Name: \" + profile.userProperties.AccountName);","title":"Get the properties for the current user"},{"location":"v2/sp/profiles/#gets-people-specified-user-is-following","text":"getPeopleFollowedBy(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const folowers = await sp.profiles.getFollowersFor(loginName); followers.forEach((value) => { console.log(value); });","title":"Gets people specified user is following"},{"location":"v2/sp/profiles/#gets-properties-for-a-specified-user","text":"getPropertiesFor(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const profile = await sp.profiles.getPropertiesFor(loginName); console.log(profile.DisplayName); console.log(profile.Email); console.log(profile.Title); console.log(profile.UserProfileProperties.length); // Properties are stored in inconvenient Key/Value pairs, // so parse into an object called userProperties var props = {}; profile.UserProfileProperties.forEach((prop) => { props[prop.Key] = prop.Value; }); profile.userProperties = props; console.log(\"Account Name: \" + profile.userProperties.AccountName);","title":"Gets properties for a specified user"},{"location":"v2/sp/profiles/#gets-most-popular-tags","text":"Gets the 20 most popular hash tags over the past week, sorted so that the most popular tag appears first trendingTags(): Promise const tags = await sp.profiles.trendingTags(); tags.Items.forEach((tag) => { console.log(tag); });","title":"Gets most popular tags"},{"location":"v2/sp/profiles/#gets-specified-user-profile-property-for-the-specified-user","text":"getUserProfilePropertyFor(loginName: string, propertyName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const propertyName = \"AccountName\"; const property = await sp.profiles.getUserProfilePropertyFor(loginName, propertyName); console.log(property);","title":"Gets specified user profile property for the specified user"},{"location":"v2/sp/profiles/#hide-specific-user-from-list-of-suggested-people","text":"Removes the specified user from the user's list of suggested people to follow. hideSuggestion(loginName: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; await sp.profiles.hideSuggestion(loginName);","title":"Hide specific user from list of suggested people"},{"location":"v2/sp/profiles/#is-one-user-following-another","text":"Indicates whether the first user is following the second user. First parameter is the account name of the user who might be following the followee. Second parameter is the account name of the user who might be followed by the follower. isFollowing(follower: string, followee: string): Promise const follower = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const followee = \"i:0#.f|membership|testuser2@mytenant.onmicrosoft.com\"; const isFollowing = await sp.profiles.isFollowing(follower, followee); console.log(isFollowing);","title":"Is one user following another"},{"location":"v2/sp/profiles/#set-user-profile-picture","text":"Uploads and sets the user profile picture (Users can upload a picture to their own profile only). Not supported for batching. Accepts the profilePicSource Blob data representing the user's picture in BMP, JPEG, or PNG format of up to 4.76MB. setMyProfilePic(profilePicSource: Blob): Promise import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/profiles\"; import \"@pnp/sp/folders\"; import \"@pnp/sp/files\"; // get the blob object through a request or from a file input const blob = await sp.web.lists.getByTitle(\"Documents\").rootFolder.files.getByName(\"profile.jpg\").getBlob(); await sp.profiles.setMyProfilePic(blob);","title":"Set User Profile Picture"},{"location":"v2/sp/profiles/#sets-single-value-user-profile-property","text":"accountName The account name of the user propertyName Property name propertyValue Property value setSingleValueProfileProperty(accountName: string, propertyName: string, propertyValue: string): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; await sp.profiles.setSingleValueProfileProperty(loginName, \"CellPhone\", \"(123) 555-1212\");","title":"Sets single value User Profile property"},{"location":"v2/sp/profiles/#sets-a-mult-value-user-profile-property","text":"accountName The account name of the user propertyName Property name propertyValues Property values setMultiValuedProfileProperty(accountName: string, propertyName: string, propertyValues: string[]): Promise const loginName = \"i:0#.f|membership|testuser@mytenant.onmicrosoft.com\"; const propertyName = \"SPS-Skills\"; const propertyValues = [\"SharePoint\", \"Office 365\", \"Architecture\", \"Azure\"]; await sp.profiles.setMultiValuedProfileProperty(loginName, propertyName, propertyValues); const profile = await sp.profiles.getPropertiesFor(loginName); var props = {}; profile.UserProfileProperties.forEach((prop) => { props[prop.Key] = prop.Value; }); profile.userProperties = props; console.log(profile.userProperties[propertyName]);","title":"Sets a mult-value User Profile property"},{"location":"v2/sp/profiles/#create-personal-site-for-specified-users","text":"Provisions one or more users' personal sites. (My Site administrator on SharePoint Online only) Emails The email addresses of the users to provision sites for createPersonalSiteEnqueueBulk(...emails: string[]): Promise let userEmails: string[] = [\"testuser1@mytenant.onmicrosoft.com\", \"testuser2@mytenant.onmicrosoft.com\"]; await sp.profiles.createPersonalSiteEnqueueBulk(userEmails);","title":"Create Personal Site for specified users"},{"location":"v2/sp/profiles/#get-the-user-profile-of-the-owner-for-the-current-site","text":"ownerUserProfile(): Promise const profile = await sp.profiles.ownerUserProfile(); console.log(profile);","title":"Get the user profile of the owner for the current site"},{"location":"v2/sp/profiles/#get-the-user-profile-of-the-current-user","text":"userProfile(): Promise const profile = await sp.profiles.userProfile(); console.log(profile);","title":"Get the user profile of the current user"},{"location":"v2/sp/profiles/#create-personal-site-for-current-user","text":"createPersonalSite(interactiveRequest = false): Promise await sp.profiles.createPersonalSite();","title":"Create personal site for current user"},{"location":"v2/sp/profiles/#make-all-profile-data-public-or-private","text":"Set the privacy settings for all social data. shareAllSocialData(share: boolean): Promise await sp.profiles.shareAllSocialData(true);","title":"Make all profile data public or private"},{"location":"v2/sp/profiles/#resolve-a-user-or-group","text":"Resolves user or group using specified query parameters clientPeoplePickerResolveUser(queryParams: IClientPeoplePickerQueryParameters): Promise const result = await sp.profiles.clientPeoplePickerSearchUser({ AllowEmailAddresses: true, AllowMultipleEntities: false, MaximumEntitySuggestions: 25, QueryString: 'John' }); console.log(result);","title":"Resolve a user or group"},{"location":"v2/sp/profiles/#search-a-user-or-group","text":"Searches for users or groups using specified query parameters clientPeoplePickerSearchUser(queryParams: IClientPeoplePickerQueryParameters): Promise const result = await sp.profiles.clientPeoplePickerSearchUser({ AllowEmailAddresses: true, AllowMultipleEntities: false, MaximumEntitySuggestions: 25, QueryString: 'John' }); console.log(result);","title":"Search a user or group"},{"location":"v2/sp/regional-settings/","text":"@pnp/sp/regional-settings \u00b6 The regional settings module helps with managing dates and times across various timezones. IRegionalSettings \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IRegionalSettings, ITimeZone, ITimeZones, RegionalSettings, TimeZone, TimeZones, } from \"@pnp/sp/regional-settings\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; Preset: All import { sp, Webs, IWebs } from \"@pnp/sp/presets/all\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; // get all the web's regional settings const s = await sp.web.regionalSettings(); // select only some settings to return const s2 = await sp.web.regionalSettings.select(\"DecimalSeparator\", \"ListSeparator\", \"IsUIRightToLeft\")(); Installed Languages \u00b6 You can get a list of the installed languages in the web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; const s = await sp.web.regionalSettings.getInstalledLanguages(); The installedLanguages property accessor is deprecated after 2.0.4 in favor of getInstalledLanguages and will be removed in future versions. TimeZones \u00b6 You can also get information about the selected timezone in the web and all of the defined timezones. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; // get the web's configured timezone const s = await sp.web.regionalSettings.timeZone(); // select just the Description and Id const s2 = await sp.web.regionalSettings.timeZone.select(\"Description\", \"Id\")(); // get all the timezones const s3 = await sp.web.regionalSettings.timeZones(); // get a specific timezone by id // list of ids: https://msdn.microsoft.com/en-us/library/office/jj247008.aspx const s4 = await sp.web.regionalSettings.timeZones.getById(23); const s5 = await s.localTimeToUTC(new Date()); // convert a given date from web's local time to UTC time const s6 = await sp.web.regionalSettings.timeZone.localTimeToUTC(new Date()); // convert a given date from UTC time to web's local time const s6 = await sp.web.regionalSettings.timeZone.utcToLocalTime(new Date()) const s7 = await sp.web.regionalSettings.timeZone.utcToLocalTime(new Date(2019, 6, 10, 10, 0, 0, 0)) Title and Description Resources \u00b6 Added in 2.0.4 Some objects allow you to read language specific title information as shown in the following sample. This applies to Web, List, Field, Content Type, and User Custom Actions. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings\"; // // The below methods appears on // - Web // - List // - Field // - ContentType // - User Custom Action // // after you import @pnp/sp/regional-settings // // you can also import just parts of the regional settings: // import \"@pnp/sp/regional-settings/web\"; // import \"@pnp/sp/regional-settings/list\"; // import \"@pnp/sp/regional-settings/content-type\"; // import \"@pnp/sp/regional-settings/field\"; // import \"@pnp/sp/regional-settings/user-custom-actions\"; const title = await sp.web.titleResource(\"en-us\"); const title2 = await sp.web.titleResource(\"de-de\"); const description = await sp.web.descriptionResource(\"en-us\"); const description2 = await sp.web.descriptionResource(\"de-de\"); You can only read the values through the REST API, not set the value.","title":"@pnp/sp/regional-settings"},{"location":"v2/sp/regional-settings/#pnpspregional-settings","text":"The regional settings module helps with managing dates and times across various timezones.","title":"@pnp/sp/regional-settings"},{"location":"v2/sp/regional-settings/#iregionalsettings","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import { IRegionalSettings, ITimeZone, ITimeZones, RegionalSettings, TimeZone, TimeZones, } from \"@pnp/sp/regional-settings\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; Preset: All import { sp, Webs, IWebs } from \"@pnp/sp/presets/all\"; import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; // get all the web's regional settings const s = await sp.web.regionalSettings(); // select only some settings to return const s2 = await sp.web.regionalSettings.select(\"DecimalSeparator\", \"ListSeparator\", \"IsUIRightToLeft\")();","title":"IRegionalSettings"},{"location":"v2/sp/regional-settings/#installed-languages","text":"You can get a list of the installed languages in the web. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; const s = await sp.web.regionalSettings.getInstalledLanguages(); The installedLanguages property accessor is deprecated after 2.0.4 in favor of getInstalledLanguages and will be removed in future versions.","title":"Installed Languages"},{"location":"v2/sp/regional-settings/#timezones","text":"You can also get information about the selected timezone in the web and all of the defined timezones. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings/web\"; // get the web's configured timezone const s = await sp.web.regionalSettings.timeZone(); // select just the Description and Id const s2 = await sp.web.regionalSettings.timeZone.select(\"Description\", \"Id\")(); // get all the timezones const s3 = await sp.web.regionalSettings.timeZones(); // get a specific timezone by id // list of ids: https://msdn.microsoft.com/en-us/library/office/jj247008.aspx const s4 = await sp.web.regionalSettings.timeZones.getById(23); const s5 = await s.localTimeToUTC(new Date()); // convert a given date from web's local time to UTC time const s6 = await sp.web.regionalSettings.timeZone.localTimeToUTC(new Date()); // convert a given date from UTC time to web's local time const s6 = await sp.web.regionalSettings.timeZone.utcToLocalTime(new Date()) const s7 = await sp.web.regionalSettings.timeZone.utcToLocalTime(new Date(2019, 6, 10, 10, 0, 0, 0))","title":"TimeZones"},{"location":"v2/sp/regional-settings/#title-and-description-resources","text":"Added in 2.0.4 Some objects allow you to read language specific title information as shown in the following sample. This applies to Web, List, Field, Content Type, and User Custom Actions. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/regional-settings\"; // // The below methods appears on // - Web // - List // - Field // - ContentType // - User Custom Action // // after you import @pnp/sp/regional-settings // // you can also import just parts of the regional settings: // import \"@pnp/sp/regional-settings/web\"; // import \"@pnp/sp/regional-settings/list\"; // import \"@pnp/sp/regional-settings/content-type\"; // import \"@pnp/sp/regional-settings/field\"; // import \"@pnp/sp/regional-settings/user-custom-actions\"; const title = await sp.web.titleResource(\"en-us\"); const title2 = await sp.web.titleResource(\"de-de\"); const description = await sp.web.descriptionResource(\"en-us\"); const description2 = await sp.web.descriptionResource(\"de-de\"); You can only read the values through the REST API, not set the value.","title":"Title and Description Resources"},{"location":"v2/sp/related-items/","text":"@pnp/sp/related-items \u00b6 The related items API allows you to add related items to items within a task or workflow list. Related items need to be in the same site collection. Setup \u00b6 Instead of copying this block of code into each sample, understand that each sample is meant to run with this supporting code to work. import { sp, extractWebUrl } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/related-items/web\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items/list\"; import \"@pnp/sp/files/list\"; import { IList } from \"@pnp/sp/lists\"; import { getRandomString } from \"@pnp/core\"; // setup some lists (or just use existing ones this is just to show the complete process) // we need two lists to use for creating related items, they need to use template 107 (task list) const ler1 = await sp.web.lists.ensure(\"RelatedItemsSourceList\", \"\", 107); const ler2 = await sp.web.lists.ensure(\"RelatedItemsTargetList\", \"\", 107); const sourceList = ler1.list; const targetList = ler2.list; const sourceListName = await sourceList.select(\"Id\")().then(r => r.Id); const targetListName = await targetList.select(\"Id\")().then(r => r.Id); // or whatever you need to get the web url, both our example lists are in the same web. const webUrl = sp.web.toUrl(); // ...individual samples start here addSingleLink \u00b6 const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); addSingleLinkToUrl \u00b6 This method adds a link to task item based on a url. The list name and item id are to the task item, the url is to the related item/document. // get a file's server relative url in some manner, here we add one const file = await sp.web.defaultDocumentLibrary.rootFolder.files.add(`file_${getRandomString(4)}.txt`, \"Content\", true).then(r => r.data); // add an item or get an item from the task list const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); await sp.web.relatedItems.addSingleLinkToUrl(targetListName, targetItem.Id, file.ServerRelativeUrl); addSingleLinkFromUrl \u00b6 This method adds a link to task item based on a url. The list name and item id are to related item, the url is to task item to which the related reference is being added. I haven't found a use case for this method. deleteSingleLink \u00b6 This method allows you to delete a link previously created. const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add the link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); // delete the link await sp.web.relatedItems.deleteSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); getRelatedItems \u00b6 Gets the related items for an item import { IRelatedItem } from \"@pnp/sp/related-items\"; const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); const targetItem2 = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem2.Id, webUrl); const items: IRelatedItem[] = await sp.web.relatedItems.getRelatedItems(sourceListName, sourceItem.Id); // items.length === 2 Related items are defined by the IRelatedItem interface export interface IRelatedItem { ListId: string; ItemId: number; Url: string; Title: string; WebId: string; IconUrl: string; } getPageOneRelatedItems \u00b6 Gets an abbreviated set of related items import { IRelatedItem } from \"@pnp/sp/related-items\"; const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); const targetItem2 = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem2.Id, webUrl); const items: IRelatedItem[] = await sp.web.relatedItems.getPageOneRelatedItems(sourceListName, sourceItem.Id); // items.length === 2","title":"@pnp/sp/related-items"},{"location":"v2/sp/related-items/#pnpsprelated-items","text":"The related items API allows you to add related items to items within a task or workflow list. Related items need to be in the same site collection.","title":"@pnp/sp/related-items"},{"location":"v2/sp/related-items/#setup","text":"Instead of copying this block of code into each sample, understand that each sample is meant to run with this supporting code to work. import { sp, extractWebUrl } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/related-items/web\"; import \"@pnp/sp/lists/web\"; import \"@pnp/sp/items/list\"; import \"@pnp/sp/files/list\"; import { IList } from \"@pnp/sp/lists\"; import { getRandomString } from \"@pnp/core\"; // setup some lists (or just use existing ones this is just to show the complete process) // we need two lists to use for creating related items, they need to use template 107 (task list) const ler1 = await sp.web.lists.ensure(\"RelatedItemsSourceList\", \"\", 107); const ler2 = await sp.web.lists.ensure(\"RelatedItemsTargetList\", \"\", 107); const sourceList = ler1.list; const targetList = ler2.list; const sourceListName = await sourceList.select(\"Id\")().then(r => r.Id); const targetListName = await targetList.select(\"Id\")().then(r => r.Id); // or whatever you need to get the web url, both our example lists are in the same web. const webUrl = sp.web.toUrl(); // ...individual samples start here","title":"Setup"},{"location":"v2/sp/related-items/#addsinglelink","text":"const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl);","title":"addSingleLink"},{"location":"v2/sp/related-items/#addsinglelinktourl","text":"This method adds a link to task item based on a url. The list name and item id are to the task item, the url is to the related item/document. // get a file's server relative url in some manner, here we add one const file = await sp.web.defaultDocumentLibrary.rootFolder.files.add(`file_${getRandomString(4)}.txt`, \"Content\", true).then(r => r.data); // add an item or get an item from the task list const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); await sp.web.relatedItems.addSingleLinkToUrl(targetListName, targetItem.Id, file.ServerRelativeUrl);","title":"addSingleLinkToUrl"},{"location":"v2/sp/related-items/#addsinglelinkfromurl","text":"This method adds a link to task item based on a url. The list name and item id are to related item, the url is to task item to which the related reference is being added. I haven't found a use case for this method.","title":"addSingleLinkFromUrl"},{"location":"v2/sp/related-items/#deletesinglelink","text":"This method allows you to delete a link previously created. const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add the link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); // delete the link await sp.web.relatedItems.deleteSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl);","title":"deleteSingleLink"},{"location":"v2/sp/related-items/#getrelateditems","text":"Gets the related items for an item import { IRelatedItem } from \"@pnp/sp/related-items\"; const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); const targetItem2 = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem2.Id, webUrl); const items: IRelatedItem[] = await sp.web.relatedItems.getRelatedItems(sourceListName, sourceItem.Id); // items.length === 2 Related items are defined by the IRelatedItem interface export interface IRelatedItem { ListId: string; ItemId: number; Url: string; Title: string; WebId: string; IconUrl: string; }","title":"getRelatedItems"},{"location":"v2/sp/related-items/#getpageonerelateditems","text":"Gets an abbreviated set of related items import { IRelatedItem } from \"@pnp/sp/related-items\"; const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl); const targetItem2 = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data); // add a link await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem2.Id, webUrl); const items: IRelatedItem[] = await sp.web.relatedItems.getPageOneRelatedItems(sourceListName, sourceItem.Id); // items.length === 2","title":"getPageOneRelatedItems"},{"location":"v2/sp/search/","text":"@pnp/sp/search \u00b6 Using search you can access content throughout your organization in a secure and consistent manner. The library provides support for searching and suggest - as well as some interfaces and helper classes to make building your queries and processing responses easier. Search \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISearchQuery, SearchResults } from \"@pnp/sp/search\"; Preset: All import { sp, ISearchQuery, SearchResults } from \"@pnp/sp/presets/all\"; Search is accessed directly from the root sp object and can take either a string representing the query text, a plain object matching the ISearchQuery interface, or a SearchQueryBuilder instance. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISearchQuery, SearchResults, SearchQueryBuilder } from \"@pnp/sp/search\"; // text search using SharePoint default values for other parameters const results: SearchResults = await sp.search(\"test\"); console.log(results.ElapsedTime); console.log(results.RowCount); console.log(results.PrimarySearchResults); // define a search query object matching the ISearchQuery interface const results2: SearchResults = await sp.search({ Querytext: \"test\", RowLimit: 10, EnableInterleaving: true, }); console.log(results2.ElapsedTime); console.log(results2.RowCount); console.log(results2.PrimarySearchResults); // define a query using a builder const builder = SearchQueryBuilder(\"test\").rowLimit(10).enableInterleaving.enableQueryRules.processPersonalFavorites; const results3 = await sp.search(builder); console.log(results3.ElapsedTime); console.log(results3.RowCount); console.log(results3.PrimarySearchResults); Search Result Caching \u00b6 You can use the searchWithCaching method to enable cache support for your search results this option works with any of the options for providing a query, just replace \"search\" with \"searchWithCaching\" in your method chain and gain all the benefits of caching. The second parameter is optional and allows you to specify the cache options import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISearchQuery, SearchResults, SearchQueryBuilder } from \"@pnp/sp/search\"; sp.searchWithCaching({ Querytext: \"test\", RowLimit: 10, EnableInterleaving: true, } as ISearchQuery).then((r: SearchResults) => { console.log(r.ElapsedTime); console.log(r.RowCount); console.log(r.PrimarySearchResults); }); // use a query builder const builder = SearchQueryBuilder(\"test\").rowLimit(3); // supply a search query builder and caching options const results2 = await sp.searchWithCaching(builder, { key: \"mykey\", expiration: dateAdd(new Date(), \"month\", 1) }); console.log(results2.TotalRows); Paging with SearchResults.getPage \u00b6 Paging is controlled by a start row and page size parameter. You can specify both arguments in your initial query however you can use the getPage method to jump to any page. The second parameter page size is optional and will use the previous RowLimit or default to 10. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { SearchResults, SearchQueryBuilder } from \"@pnp/sp/search\"; // this will hold our current results let currentResults: SearchResults = null; let page = 1; // triggered on page load or through some other means function onStart() { // construct our query that will be used throughout the paging process, likely from user input const q = SearchQueryBuilder(\"test\").rowLimit(5); const results = await sp.search(q); currentResults = results; // set the current results page = 1; // reset page counter // update UI... } // triggered by an event async function next() { currentResults = await currentResults.getPage(++page); // update UI... } // triggered by an event async function prev() { currentResults = await currentResults.getPage(--page); // update UI... } SearchQueryBuilder \u00b6 The SearchQueryBuilder allows you to build your queries in a fluent manner. It also accepts constructor arguments for query text and a base query plain object, should you have a shared configuration for queries in an application you can define them once. The methods and properties match those on the SearchQuery interface. Boolean properties add the flag to the query while methods require that you supply one or more arguments. Also arguments supplied later in the chain will overwrite previous values. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { SearchQueryBuilder, SearchResults, ISearchQuery } from \"@pnp/sp/search\"; // basic usage let q = SearchQueryBuilder().text(\"test\").rowLimit(4).enablePhonetic; sp.search(q).then(h => { /* ... */ }); // provide a default query text at creation let q2 = SearchQueryBuilder(\"text\").rowLimit(4).enablePhonetic; const results: SearchResults = await sp.search(q2); // provide query text and a template for // shared settings across queries that can // be overwritten by individual builders const appSearchSettings: ISearchQuery = { EnablePhonetic: true, HiddenConstraints: \"reports\" }; let q3 = SearchQueryBuilder(\"test\", appSearchSettings).enableQueryRules; let q4 = SearchQueryBuilder(\"financial data\", appSearchSettings).enableSorting.enableStemming; const results2 = await sp.search(q3); const results3 = sp.search(q4); Search Suggest \u00b6 Search suggest works in much the same way as search, except against the suggest end point. It takes a string or a plain object that matches ISuggestQuery. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISuggestQuery, ISuggestResult } from \"@pnp/sp/search\"; const results = await sp.searchSuggest(\"test\"); const results2 = await sp.searchSuggest({ querytext: \"test\", count: 5, } as ISuggestQuery); Search Factory \u00b6 You can also configure a search or suggest query against any valid SP url using the factory methods. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { Search, Suggest } from \"@pnp/sp/search\"; // set the url for search const searcher = Search(\"https://mytenant.sharepoint.com/sites/dev\"); // this can accept any of the query types (text, ISearchQuery, or SearchQueryBuilder) const results = await searcher(\"test\"); // you can reuse the ISearch instance const results2 = await searcher(\"another query\"); // same process works for Suggest const suggester = Suggest(\"https://mytenant.sharepoint.com/sites/dev\"); const suggestions = await suggester({ querytext: \"test\" });","title":"@pnp/sp/search"},{"location":"v2/sp/search/#pnpspsearch","text":"Using search you can access content throughout your organization in a secure and consistent manner. The library provides support for searching and suggest - as well as some interfaces and helper classes to make building your queries and processing responses easier.","title":"@pnp/sp/search"},{"location":"v2/sp/search/#search","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISearchQuery, SearchResults } from \"@pnp/sp/search\"; Preset: All import { sp, ISearchQuery, SearchResults } from \"@pnp/sp/presets/all\"; Search is accessed directly from the root sp object and can take either a string representing the query text, a plain object matching the ISearchQuery interface, or a SearchQueryBuilder instance. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISearchQuery, SearchResults, SearchQueryBuilder } from \"@pnp/sp/search\"; // text search using SharePoint default values for other parameters const results: SearchResults = await sp.search(\"test\"); console.log(results.ElapsedTime); console.log(results.RowCount); console.log(results.PrimarySearchResults); // define a search query object matching the ISearchQuery interface const results2: SearchResults = await sp.search({ Querytext: \"test\", RowLimit: 10, EnableInterleaving: true, }); console.log(results2.ElapsedTime); console.log(results2.RowCount); console.log(results2.PrimarySearchResults); // define a query using a builder const builder = SearchQueryBuilder(\"test\").rowLimit(10).enableInterleaving.enableQueryRules.processPersonalFavorites; const results3 = await sp.search(builder); console.log(results3.ElapsedTime); console.log(results3.RowCount); console.log(results3.PrimarySearchResults);","title":"Search"},{"location":"v2/sp/search/#search-result-caching","text":"You can use the searchWithCaching method to enable cache support for your search results this option works with any of the options for providing a query, just replace \"search\" with \"searchWithCaching\" in your method chain and gain all the benefits of caching. The second parameter is optional and allows you to specify the cache options import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISearchQuery, SearchResults, SearchQueryBuilder } from \"@pnp/sp/search\"; sp.searchWithCaching({ Querytext: \"test\", RowLimit: 10, EnableInterleaving: true, } as ISearchQuery).then((r: SearchResults) => { console.log(r.ElapsedTime); console.log(r.RowCount); console.log(r.PrimarySearchResults); }); // use a query builder const builder = SearchQueryBuilder(\"test\").rowLimit(3); // supply a search query builder and caching options const results2 = await sp.searchWithCaching(builder, { key: \"mykey\", expiration: dateAdd(new Date(), \"month\", 1) }); console.log(results2.TotalRows);","title":"Search Result Caching"},{"location":"v2/sp/search/#paging-with-searchresultsgetpage","text":"Paging is controlled by a start row and page size parameter. You can specify both arguments in your initial query however you can use the getPage method to jump to any page. The second parameter page size is optional and will use the previous RowLimit or default to 10. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { SearchResults, SearchQueryBuilder } from \"@pnp/sp/search\"; // this will hold our current results let currentResults: SearchResults = null; let page = 1; // triggered on page load or through some other means function onStart() { // construct our query that will be used throughout the paging process, likely from user input const q = SearchQueryBuilder(\"test\").rowLimit(5); const results = await sp.search(q); currentResults = results; // set the current results page = 1; // reset page counter // update UI... } // triggered by an event async function next() { currentResults = await currentResults.getPage(++page); // update UI... } // triggered by an event async function prev() { currentResults = await currentResults.getPage(--page); // update UI... }","title":"Paging with SearchResults.getPage"},{"location":"v2/sp/search/#searchquerybuilder","text":"The SearchQueryBuilder allows you to build your queries in a fluent manner. It also accepts constructor arguments for query text and a base query plain object, should you have a shared configuration for queries in an application you can define them once. The methods and properties match those on the SearchQuery interface. Boolean properties add the flag to the query while methods require that you supply one or more arguments. Also arguments supplied later in the chain will overwrite previous values. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { SearchQueryBuilder, SearchResults, ISearchQuery } from \"@pnp/sp/search\"; // basic usage let q = SearchQueryBuilder().text(\"test\").rowLimit(4).enablePhonetic; sp.search(q).then(h => { /* ... */ }); // provide a default query text at creation let q2 = SearchQueryBuilder(\"text\").rowLimit(4).enablePhonetic; const results: SearchResults = await sp.search(q2); // provide query text and a template for // shared settings across queries that can // be overwritten by individual builders const appSearchSettings: ISearchQuery = { EnablePhonetic: true, HiddenConstraints: \"reports\" }; let q3 = SearchQueryBuilder(\"test\", appSearchSettings).enableQueryRules; let q4 = SearchQueryBuilder(\"financial data\", appSearchSettings).enableSorting.enableStemming; const results2 = await sp.search(q3); const results3 = sp.search(q4);","title":"SearchQueryBuilder"},{"location":"v2/sp/search/#search-suggest","text":"Search suggest works in much the same way as search, except against the suggest end point. It takes a string or a plain object that matches ISuggestQuery. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { ISuggestQuery, ISuggestResult } from \"@pnp/sp/search\"; const results = await sp.searchSuggest(\"test\"); const results2 = await sp.searchSuggest({ querytext: \"test\", count: 5, } as ISuggestQuery);","title":"Search Suggest"},{"location":"v2/sp/search/#search-factory","text":"You can also configure a search or suggest query against any valid SP url using the factory methods. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/search\"; import { Search, Suggest } from \"@pnp/sp/search\"; // set the url for search const searcher = Search(\"https://mytenant.sharepoint.com/sites/dev\"); // this can accept any of the query types (text, ISearchQuery, or SearchQueryBuilder) const results = await searcher(\"test\"); // you can reuse the ISearch instance const results2 = await searcher(\"another query\"); // same process works for Suggest const suggester = Suggest(\"https://mytenant.sharepoint.com/sites/dev\"); const suggestions = await suggester({ querytext: \"test\" });","title":"Search Factory"},{"location":"v2/sp/security/","text":"@pnp/sp/security \u00b6 There are four levels where you can break inheritance and assign security: Site, Web, List, Item. All four of these objects share a common set of methods. Because of this we are showing in the examples below usage of these methods for an IList instance, but they apply across all four securable objects. In addition to the shared methods, some types have unique methods which are listed below. Site permissions are managed on the root web of the site collection. A Note on Selective Imports for Security \u00b6 Because the method are shared you can opt to import only the methods for one of the instances. import \"@pnp/sp/security/web\"; import \"@pnp/sp/security/list\"; import \"@pnp/sp/security/item\"; Possibly useful if you are trying to hyper-optimize for bundle size but it is just as easy to import the whole module: import \"@pnp/sp/security\"; Securable Methods \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/security/list\"; import \"@pnp/sp/site-users/web\"; import { IList } from \"@pnp/sp/lists\"; import { PermissionKind } from \"@pnp/sp/security\"; // ensure we have a list const ler = await sp.web.lists.ensure(\"SecurityTestingList\"); const list: IList = ler.list; // role assignments (see section below) await list.roleAssignments(); // data will represent one of the possible parents Site, Web, or List const data = await list.firstUniqueAncestorSecurableObject(); // getUserEffectivePermissions const users = await sp.web.siteUsers.top(1).select(\"LoginName\")(); const perms = await list.getUserEffectivePermissions(users[0].LoginName); // getCurrentUserEffectivePermissions const perms2 = list.getCurrentUserEffectivePermissions(); // userHasPermissions const v: boolean = list.userHasPermissions(users[0].LoginName, PermissionKind.AddListItems) // currentUserHasPermissions const v2: boolean = list.currentUserHasPermissions(PermissionKind.AddListItems) // breakRoleInheritance await list.breakRoleInheritance(); // copy existing permissions await list.breakRoleInheritance(true); // copy existing permissions and reset all child securables to the new permissions await list.breakRoleInheritance(true, true); // resetRoleInheritance await list.resetRoleInheritance(); Web Specific methods \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/security/web\"; // role definitions (see section below) const defs = await sp.web.roleDefinitions(); Role Assignments \u00b6 Allows you to list and manipulate the set of role assignments for the given securable. Again we show usage using list, but the examples apply to web and item as well. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/security/web\"; import \"@pnp/sp/site-users/web\"; import { IList } from \"@pnp/sp/lists\"; import { PermissionKind } from \"@pnp/sp/security\"; // ensure we have a list const ler = await sp.web.lists.ensure(\"SecurityTestingList\"); const list: IList = ler.list; // list role assignments const assignments = await list.roleAssignments(); // add a role assignment const defs = await sp.web.roleDefinitions(); const user = await sp.web.currentUser(); const r = await list.roleAssignments.add(user.Id, defs[0].Id); // remove a role assignment const ras = await list.roleAssignments(); // filter/find the role assignment you want to remove // here we just grab the first const ra = ras.find(v => true); const r = await list.roleAssignments.remove(ra.Id); // read role assignment info const info = await list.roleAssignments.getById(ra.Id)(); // get the groups const info2 = await list.roleAssignments.getById(ra.Id).groups(); // get the bindings const info3 = await list.roleAssignments.getById(ra.Id).bindings(); // delete a role assignment (same as remove) const ras = await list.roleAssignments(); // filter/find the role assignment you want to remove // here we just grab the first const ra = ras.find(v => true); // delete it await list.roleAssignments.getById(ra.Id).delete(); Role Definitions \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/security/web\"; // read role definitions const defs = await sp.web.roleDefinitions(); // get by id const def = await sp.web.roleDefinitions.getById(5)(); const def = await sp.web.roleDefinitions.getById(5).select(\"Name\", \"Order\")(); // get by name const def = await sp.web.roleDefinitions.getByName(\"Full Control\")(); const def = await sp.web.roleDefinitions.getByName(\"Full Control\").select(\"Name\", \"Order\")(); // get by type const def = await sp.web.roleDefinitions.getByName(5)(); const def = await sp.web.roleDefinitions.getByName(5).select(\"Name\", \"Order\")(); // add // name The new role definition's name // description The new role definition's description // order The order in which the role definition appears // basePermissions The permissions mask for this role definition const rdar = await sp.web.roleDefinitions.add(\"title\", \"description\", 99, { High: 1, Low: 2 }); // the following methods work on a single role def, you can use any of the three getBy methods, here we use getById as an example // delete await sp.web.roleDefinitions.getById(5).delete(); // update const res = sp.web.roleDefinitions.getById(5).update({ Name: \"New Name\" });","title":"@pnp/sp/security"},{"location":"v2/sp/security/#pnpspsecurity","text":"There are four levels where you can break inheritance and assign security: Site, Web, List, Item. All four of these objects share a common set of methods. Because of this we are showing in the examples below usage of these methods for an IList instance, but they apply across all four securable objects. In addition to the shared methods, some types have unique methods which are listed below. Site permissions are managed on the root web of the site collection.","title":"@pnp/sp/security"},{"location":"v2/sp/security/#a-note-on-selective-imports-for-security","text":"Because the method are shared you can opt to import only the methods for one of the instances. import \"@pnp/sp/security/web\"; import \"@pnp/sp/security/list\"; import \"@pnp/sp/security/item\"; Possibly useful if you are trying to hyper-optimize for bundle size but it is just as easy to import the whole module: import \"@pnp/sp/security\";","title":"A Note on Selective Imports for Security"},{"location":"v2/sp/security/#securable-methods","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/security/list\"; import \"@pnp/sp/site-users/web\"; import { IList } from \"@pnp/sp/lists\"; import { PermissionKind } from \"@pnp/sp/security\"; // ensure we have a list const ler = await sp.web.lists.ensure(\"SecurityTestingList\"); const list: IList = ler.list; // role assignments (see section below) await list.roleAssignments(); // data will represent one of the possible parents Site, Web, or List const data = await list.firstUniqueAncestorSecurableObject(); // getUserEffectivePermissions const users = await sp.web.siteUsers.top(1).select(\"LoginName\")(); const perms = await list.getUserEffectivePermissions(users[0].LoginName); // getCurrentUserEffectivePermissions const perms2 = list.getCurrentUserEffectivePermissions(); // userHasPermissions const v: boolean = list.userHasPermissions(users[0].LoginName, PermissionKind.AddListItems) // currentUserHasPermissions const v2: boolean = list.currentUserHasPermissions(PermissionKind.AddListItems) // breakRoleInheritance await list.breakRoleInheritance(); // copy existing permissions await list.breakRoleInheritance(true); // copy existing permissions and reset all child securables to the new permissions await list.breakRoleInheritance(true, true); // resetRoleInheritance await list.resetRoleInheritance();","title":"Securable Methods"},{"location":"v2/sp/security/#web-specific-methods","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/security/web\"; // role definitions (see section below) const defs = await sp.web.roleDefinitions();","title":"Web Specific methods"},{"location":"v2/sp/security/#role-assignments","text":"Allows you to list and manipulate the set of role assignments for the given securable. Again we show usage using list, but the examples apply to web and item as well. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/security/web\"; import \"@pnp/sp/site-users/web\"; import { IList } from \"@pnp/sp/lists\"; import { PermissionKind } from \"@pnp/sp/security\"; // ensure we have a list const ler = await sp.web.lists.ensure(\"SecurityTestingList\"); const list: IList = ler.list; // list role assignments const assignments = await list.roleAssignments(); // add a role assignment const defs = await sp.web.roleDefinitions(); const user = await sp.web.currentUser(); const r = await list.roleAssignments.add(user.Id, defs[0].Id); // remove a role assignment const ras = await list.roleAssignments(); // filter/find the role assignment you want to remove // here we just grab the first const ra = ras.find(v => true); const r = await list.roleAssignments.remove(ra.Id); // read role assignment info const info = await list.roleAssignments.getById(ra.Id)(); // get the groups const info2 = await list.roleAssignments.getById(ra.Id).groups(); // get the bindings const info3 = await list.roleAssignments.getById(ra.Id).bindings(); // delete a role assignment (same as remove) const ras = await list.roleAssignments(); // filter/find the role assignment you want to remove // here we just grab the first const ra = ras.find(v => true); // delete it await list.roleAssignments.getById(ra.Id).delete();","title":"Role Assignments"},{"location":"v2/sp/security/#role-definitions","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/security/web\"; // read role definitions const defs = await sp.web.roleDefinitions(); // get by id const def = await sp.web.roleDefinitions.getById(5)(); const def = await sp.web.roleDefinitions.getById(5).select(\"Name\", \"Order\")(); // get by name const def = await sp.web.roleDefinitions.getByName(\"Full Control\")(); const def = await sp.web.roleDefinitions.getByName(\"Full Control\").select(\"Name\", \"Order\")(); // get by type const def = await sp.web.roleDefinitions.getByName(5)(); const def = await sp.web.roleDefinitions.getByName(5).select(\"Name\", \"Order\")(); // add // name The new role definition's name // description The new role definition's description // order The order in which the role definition appears // basePermissions The permissions mask for this role definition const rdar = await sp.web.roleDefinitions.add(\"title\", \"description\", 99, { High: 1, Low: 2 }); // the following methods work on a single role def, you can use any of the three getBy methods, here we use getById as an example // delete await sp.web.roleDefinitions.getById(5).delete(); // update const res = sp.web.roleDefinitions.getById(5).update({ Name: \"New Name\" });","title":"Role Definitions"},{"location":"v2/sp/sharing/","text":"@pnp/sp/sharing \u00b6 Note: This API is still considered \"beta\" meaning it may change and some behaviors may differ across tenants by version. It is also supported only in SharePoint Online. One of the newer abilities in SharePoint is the ability to share webs, files, or folders with both internal and external folks. It is important to remember that these settings are managed at the tenant level and ? override anything you may supply as an argument to these methods. If you receive an InvalidOperationException when using these methods please check your tenant sharing settings to ensure sharing is not blocked before ?submitting an issue. Imports \u00b6 In previous versions of this library the sharing methods were part of the inheritance stack for SharePointQueryable objects. Starting with v2 this is no longer the case and they are now selectively importable. There are four objects within the SharePoint hierarchy that support sharing: Item, File, Folder, and Web. You can import the sharing methods for all of them, or for individual objects. Import All \u00b6 To import and attach the sharing methods to all four of the sharable types include all of the sharing sub module: import \"@pnp/sp/sharing\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; import { sp } from \"@pnp/sp\"; const user = await sp.web.siteUsers.getByEmail(\"user@site.com\")(); const r = await sp.web.shareWith(user.LoginName); Selective Import \u00b6 Import only the web's sharing methods into the library import \"@pnp/sp/sharing/web\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; import { sp } from \"@pnp/sp\"; const user = await sp.web.siteUsers.getByEmail(\"user@site.com\")(); const r = await sp.web.shareWith(user.LoginName); getShareLink \u00b6 Applies to: Item, Folder, File Creates a sharing link for the given resource with an optional expiration. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import { SharingLinkKind, IShareLinkResponse } from \"@pnp/sp/sharing\"; import { dateAdd } from \"@pnp/core\"; const result = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/folder1\").getShareLink(SharingLinkKind.AnonymousView); console.log(JSON.stringify(result, null, 2)); const result2 = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/folder1\").getShareLink(SharingLinkKind.AnonymousView, dateAdd(new Date(), \"day\", 5)); console.log(JSON.stringify(result2, null, 2)); shareWith \u00b6 Applies to: Item, Folder, File, Web Shares the given resource with the specified permissions (View or Edit) and optionally sends an email to the users. You can supply a single string for the loginnames parameter or an array of loginnames . The folder method takes an optional parameter \"shareEverything\" which determines if the shared permissions are pushed down to all items in the folder, even those with unique permissions. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/files/web\"; import { ISharingResult, SharingRole } from \"@pnp/sp/sharing\"; const result = await sp.web.shareWith(\"i:0#.f|membership|user@site.com\"); console.log(JSON.stringify(result, null, 2)); // Share and allow editing const result2 = await sp.web.shareWith(\"i:0#.f|membership|user@site.com\", SharingRole.Edit); console.log(JSON.stringify(result2, null, 2)); // share folder const result3 = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/folder1\").shareWith(\"i:0#.f|membership|user@site.com\"); // Share folder with edit permissions, and provide params for requireSignin and propagateAcl (apply to all children) await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").shareWith(\"i:0#.f|membership|user@site.com\", SharingRole.Edit, true, true); // Share a file await sp.web.getFileByServerRelativeUrl(\"/sites/dev/Shared Documents/test.txt\").shareWith(\"i:0#.f|membership|user@site.com\"); // Share a file with edit permissions await sp.web.getFileByServerRelativeUrl(\"/sites/dev/Shared Documents/test.txt\").shareWith(\"i:0#.f|membership|user@site.com\", SharingRole.Edit); shareObject & shareObjectRaw \u00b6 Applies to: Web Allows you to share any shareable object in a web by providing the appropriate parameters. These two methods differ in that shareObject will try and fix up your query based on the supplied parameters where shareObjectRaw will send your supplied json object directly to the server. The later method is provided for the greatest amount of flexibility. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import { ISharingResult, SharingRole } from \"@pnp/sp/sharing\"; // Share an object in this web const result = await sp.web.shareObject(\"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\", \"i:0#.f|membership|user@site.com\", SharingRole.View); // Share an object with all settings available await sp.web.shareObjectRaw({ url: \"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\", peoplePickerInput: [{ Key: \"i:0#.f|membership|user@site.com\" }], roleValue: \"role: 1973741327\", groupId: 0, propagateAcl: false, sendEmail: true, includeAnonymousLinkInEmail: false, emailSubject: \"subject\", emailBody: \"body\", useSimplifiedRoles: true, }); unshareObject \u00b6 Applies to: Web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import { ISharingResult } from \"@pnp/sp/sharing\"; const result = await sp.web.unshareObject(\"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\"); checkSharingPermissions \u00b6 Applies to: Item, Folder, File Checks Permissions on the list of Users and returns back role the users have on the Item. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing/folders\"; import \"@pnp/sp/folders/web\"; import { SharingEntityPermission } from \"@pnp/sp/sharing\"; // check the sharing permissions for a folder const perms = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").checkSharingPermissions([{ alias: \"i:0#.f|membership|user@site.com\" }]); getSharingInformation \u00b6 Applies to: Item, Folder, File Get Sharing Information. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { ISharingInformation } from \"@pnp/sp/sharing\"; // Get the sharing information for a folder const info = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").getSharingInformation(); getObjectSharingSettings \u00b6 Applies to: Item, Folder, File Gets the sharing settings import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { IObjectSharingSettings } from \"@pnp/sp/sharing\"; // Gets the sharing object settings const settings: IObjectSharingSettings = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").getObjectSharingSettings(); unshare \u00b6 Applies to: Item, Folder, File Unshares a given resource import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { ISharingResult } from \"@pnp/sp/sharing\"; const result: ISharingResult = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").unshare(); deleteSharingLinkByKind \u00b6 Applies to: Item, Folder, File import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { ISharingResult, SharingLinkKind } from \"@pnp/sp/sharing\"; const result: ISharingResult = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").deleteSharingLinkByKind(SharingLinkKind.AnonymousEdit); unshareLink \u00b6 Applies to: Item, Folder, File import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { SharingLinkKind } from \"@pnp/sp/sharing\"; await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").unshareLink(SharingLinkKind.AnonymousEdit); // specify the sharing link id if available await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").unshareLink(SharingLinkKind.AnonymousEdit, \"12345\");","title":"@pnp/sp/sharing"},{"location":"v2/sp/sharing/#pnpspsharing","text":"Note: This API is still considered \"beta\" meaning it may change and some behaviors may differ across tenants by version. It is also supported only in SharePoint Online. One of the newer abilities in SharePoint is the ability to share webs, files, or folders with both internal and external folks. It is important to remember that these settings are managed at the tenant level and ? override anything you may supply as an argument to these methods. If you receive an InvalidOperationException when using these methods please check your tenant sharing settings to ensure sharing is not blocked before ?submitting an issue.","title":"@pnp/sp/sharing"},{"location":"v2/sp/sharing/#imports","text":"In previous versions of this library the sharing methods were part of the inheritance stack for SharePointQueryable objects. Starting with v2 this is no longer the case and they are now selectively importable. There are four objects within the SharePoint hierarchy that support sharing: Item, File, Folder, and Web. You can import the sharing methods for all of them, or for individual objects.","title":"Imports"},{"location":"v2/sp/sharing/#import-all","text":"To import and attach the sharing methods to all four of the sharable types include all of the sharing sub module: import \"@pnp/sp/sharing\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; import { sp } from \"@pnp/sp\"; const user = await sp.web.siteUsers.getByEmail(\"user@site.com\")(); const r = await sp.web.shareWith(user.LoginName);","title":"Import All"},{"location":"v2/sp/sharing/#selective-import","text":"Import only the web's sharing methods into the library import \"@pnp/sp/sharing/web\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; import { sp } from \"@pnp/sp\"; const user = await sp.web.siteUsers.getByEmail(\"user@site.com\")(); const r = await sp.web.shareWith(user.LoginName);","title":"Selective Import"},{"location":"v2/sp/sharing/#getsharelink","text":"Applies to: Item, Folder, File Creates a sharing link for the given resource with an optional expiration. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import { SharingLinkKind, IShareLinkResponse } from \"@pnp/sp/sharing\"; import { dateAdd } from \"@pnp/core\"; const result = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/folder1\").getShareLink(SharingLinkKind.AnonymousView); console.log(JSON.stringify(result, null, 2)); const result2 = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/folder1\").getShareLink(SharingLinkKind.AnonymousView, dateAdd(new Date(), \"day\", 5)); console.log(JSON.stringify(result2, null, 2));","title":"getShareLink"},{"location":"v2/sp/sharing/#sharewith","text":"Applies to: Item, Folder, File, Web Shares the given resource with the specified permissions (View or Edit) and optionally sends an email to the users. You can supply a single string for the loginnames parameter or an array of loginnames . The folder method takes an optional parameter \"shareEverything\" which determines if the shared permissions are pushed down to all items in the folder, even those with unique permissions. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders/web\"; import \"@pnp/sp/files/web\"; import { ISharingResult, SharingRole } from \"@pnp/sp/sharing\"; const result = await sp.web.shareWith(\"i:0#.f|membership|user@site.com\"); console.log(JSON.stringify(result, null, 2)); // Share and allow editing const result2 = await sp.web.shareWith(\"i:0#.f|membership|user@site.com\", SharingRole.Edit); console.log(JSON.stringify(result2, null, 2)); // share folder const result3 = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/folder1\").shareWith(\"i:0#.f|membership|user@site.com\"); // Share folder with edit permissions, and provide params for requireSignin and propagateAcl (apply to all children) await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").shareWith(\"i:0#.f|membership|user@site.com\", SharingRole.Edit, true, true); // Share a file await sp.web.getFileByServerRelativeUrl(\"/sites/dev/Shared Documents/test.txt\").shareWith(\"i:0#.f|membership|user@site.com\"); // Share a file with edit permissions await sp.web.getFileByServerRelativeUrl(\"/sites/dev/Shared Documents/test.txt\").shareWith(\"i:0#.f|membership|user@site.com\", SharingRole.Edit);","title":"shareWith"},{"location":"v2/sp/sharing/#shareobject-shareobjectraw","text":"Applies to: Web Allows you to share any shareable object in a web by providing the appropriate parameters. These two methods differ in that shareObject will try and fix up your query based on the supplied parameters where shareObjectRaw will send your supplied json object directly to the server. The later method is provided for the greatest amount of flexibility. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import { ISharingResult, SharingRole } from \"@pnp/sp/sharing\"; // Share an object in this web const result = await sp.web.shareObject(\"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\", \"i:0#.f|membership|user@site.com\", SharingRole.View); // Share an object with all settings available await sp.web.shareObjectRaw({ url: \"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\", peoplePickerInput: [{ Key: \"i:0#.f|membership|user@site.com\" }], roleValue: \"role: 1973741327\", groupId: 0, propagateAcl: false, sendEmail: true, includeAnonymousLinkInEmail: false, emailSubject: \"subject\", emailBody: \"body\", useSimplifiedRoles: true, });","title":"shareObject & shareObjectRaw"},{"location":"v2/sp/sharing/#unshareobject","text":"Applies to: Web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import { ISharingResult } from \"@pnp/sp/sharing\"; const result = await sp.web.unshareObject(\"https://mysite.sharepoint.com/sites/dev/Docs/test.txt\");","title":"unshareObject"},{"location":"v2/sp/sharing/#checksharingpermissions","text":"Applies to: Item, Folder, File Checks Permissions on the list of Users and returns back role the users have on the Item. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing/folders\"; import \"@pnp/sp/folders/web\"; import { SharingEntityPermission } from \"@pnp/sp/sharing\"; // check the sharing permissions for a folder const perms = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").checkSharingPermissions([{ alias: \"i:0#.f|membership|user@site.com\" }]);","title":"checkSharingPermissions"},{"location":"v2/sp/sharing/#getsharinginformation","text":"Applies to: Item, Folder, File Get Sharing Information. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { ISharingInformation } from \"@pnp/sp/sharing\"; // Get the sharing information for a folder const info = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").getSharingInformation();","title":"getSharingInformation"},{"location":"v2/sp/sharing/#getobjectsharingsettings","text":"Applies to: Item, Folder, File Gets the sharing settings import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { IObjectSharingSettings } from \"@pnp/sp/sharing\"; // Gets the sharing object settings const settings: IObjectSharingSettings = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").getObjectSharingSettings();","title":"getObjectSharingSettings"},{"location":"v2/sp/sharing/#unshare","text":"Applies to: Item, Folder, File Unshares a given resource import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { ISharingResult } from \"@pnp/sp/sharing\"; const result: ISharingResult = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").unshare();","title":"unshare"},{"location":"v2/sp/sharing/#deletesharinglinkbykind","text":"Applies to: Item, Folder, File import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { ISharingResult, SharingLinkKind } from \"@pnp/sp/sharing\"; const result: ISharingResult = await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").deleteSharingLinkByKind(SharingLinkKind.AnonymousEdit);","title":"deleteSharingLinkByKind"},{"location":"v2/sp/sharing/#unsharelink","text":"Applies to: Item, Folder, File import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sharing\"; import \"@pnp/sp/folders\"; import { SharingLinkKind } from \"@pnp/sp/sharing\"; await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").unshareLink(SharingLinkKind.AnonymousEdit); // specify the sharing link id if available await sp.web.getFolderByServerRelativeUrl(\"/sites/dev/Shared Documents/test\").unshareLink(SharingLinkKind.AnonymousEdit, \"12345\");","title":"unshareLink"},{"location":"v2/sp/site-designs/","text":"@pnp/sp/site-designs \u00b6 You can create site designs to provide reusable lists, themes, layouts, pages, or custom actions so that your users can quickly build new SharePoint sites with the features they need. Check out SharePoint site design and site script overview for more information. Site Designs \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; Create a new site design \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // WebTemplate: 64 Team site template, 68 Communication site template const siteDesign = await sp.siteDesigns.createSiteDesign({ SiteScriptIds: [\"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\"], Title: \"SiteDesign001\", WebTemplate: \"64\", }); console.log(siteDesign.Title); Applying a site design to a site \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Limited to 30 actions in a site script, but runs synchronously await sp.siteDesigns.applySiteDesign(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\",\"https://contoso.sharepoint.com/sites/teamsite-pnpjs001\"); // Better use the following method for 300 actions in a site script const task = await sp.web.addSiteDesignTask(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); Retrieval \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Retrieving all site designs const allSiteDesigns = await sp.siteDesigns.getSiteDesigns(); console.log(`Total site designs: ${allSiteDesigns.length}`); // Retrieving a single site design by Id const siteDesign = await sp.siteDesigns.getSiteDesignMetadata(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); console.log(siteDesign.Title); Update and delete \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Update const updatedSiteDesign = await sp.siteDesigns.updateSiteDesign({ Id: \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", Title: \"SiteDesignUpdatedTitle001\" }); // Delete await sp.siteDesigns.deleteSiteDesign(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); Setting Rights/Permissions \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Get const rights = await sp.siteDesigns.getSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); console.log(rights.length > 0 ? rights[0].PrincipalName : \"\"); // Grant await sp.siteDesigns.grantSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", [\"user@contoso.onmicrosoft.com\"]); // Revoke await sp.siteDesigns.revokeSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", [\"user@contoso.onmicrosoft.com\"]); // Reset all view rights const rights = await sp.siteDesigns.getSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); await sp.siteDesigns.revokeSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", rights.map(u => u.PrincipalName)); Get a history of site designs that have run on a web \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; const runs = await sp.web.getSiteDesignRuns(); const runs2 = await sp.siteDesigns.getSiteDesignRun(\"https://TENANT.sharepoint.com/sites/mysite\"); // Get runs specific to a site design const runs3 = await sp.web.getSiteDesignRuns(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); const runs4 = await sp.siteDesigns.getSiteDesignRun(\"https://TENANT.sharepoint.com/sites/mysite\", \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); // For more information about the site script actions const runStatus = await sp.web.getSiteDesignRunStatus(runs[0].ID); const runStatus2 = await sp.siteDesigns.getSiteDesignRunStatus(\"https://TENANT.sharepoint.com/sites/mysite\", runs[0].ID);","title":"@pnp/sp/site-designs"},{"location":"v2/sp/site-designs/#pnpspsite-designs","text":"You can create site designs to provide reusable lists, themes, layouts, pages, or custom actions so that your users can quickly build new SharePoint sites with the features they need. Check out SharePoint site design and site script overview for more information.","title":"@pnp/sp/site-designs"},{"location":"v2/sp/site-designs/#site-designs","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"Site Designs"},{"location":"v2/sp/site-designs/#create-a-new-site-design","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // WebTemplate: 64 Team site template, 68 Communication site template const siteDesign = await sp.siteDesigns.createSiteDesign({ SiteScriptIds: [\"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\"], Title: \"SiteDesign001\", WebTemplate: \"64\", }); console.log(siteDesign.Title);","title":"Create a new site design"},{"location":"v2/sp/site-designs/#applying-a-site-design-to-a-site","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Limited to 30 actions in a site script, but runs synchronously await sp.siteDesigns.applySiteDesign(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\",\"https://contoso.sharepoint.com/sites/teamsite-pnpjs001\"); // Better use the following method for 300 actions in a site script const task = await sp.web.addSiteDesignTask(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\");","title":"Applying a site design to a site"},{"location":"v2/sp/site-designs/#retrieval","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Retrieving all site designs const allSiteDesigns = await sp.siteDesigns.getSiteDesigns(); console.log(`Total site designs: ${allSiteDesigns.length}`); // Retrieving a single site design by Id const siteDesign = await sp.siteDesigns.getSiteDesignMetadata(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); console.log(siteDesign.Title);","title":"Retrieval"},{"location":"v2/sp/site-designs/#update-and-delete","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Update const updatedSiteDesign = await sp.siteDesigns.updateSiteDesign({ Id: \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", Title: \"SiteDesignUpdatedTitle001\" }); // Delete await sp.siteDesigns.deleteSiteDesign(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\");","title":"Update and delete"},{"location":"v2/sp/site-designs/#setting-rightspermissions","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; // Get const rights = await sp.siteDesigns.getSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); console.log(rights.length > 0 ? rights[0].PrincipalName : \"\"); // Grant await sp.siteDesigns.grantSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", [\"user@contoso.onmicrosoft.com\"]); // Revoke await sp.siteDesigns.revokeSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", [\"user@contoso.onmicrosoft.com\"]); // Reset all view rights const rights = await sp.siteDesigns.getSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); await sp.siteDesigns.revokeSiteDesignRights(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\", rights.map(u => u.PrincipalName));","title":"Setting Rights/Permissions"},{"location":"v2/sp/site-designs/#get-a-history-of-site-designs-that-have-run-on-a-web","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-designs\"; const runs = await sp.web.getSiteDesignRuns(); const runs2 = await sp.siteDesigns.getSiteDesignRun(\"https://TENANT.sharepoint.com/sites/mysite\"); // Get runs specific to a site design const runs3 = await sp.web.getSiteDesignRuns(\"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); const runs4 = await sp.siteDesigns.getSiteDesignRun(\"https://TENANT.sharepoint.com/sites/mysite\", \"75b9d8fe-4381-45d9-88c6-b03f483ae6a8\"); // For more information about the site script actions const runStatus = await sp.web.getSiteDesignRunStatus(runs[0].ID); const runStatus2 = await sp.siteDesigns.getSiteDesignRunStatus(\"https://TENANT.sharepoint.com/sites/mysite\", runs[0].ID);","title":"Get a history of site designs that have run on a web"},{"location":"v2/sp/site-groups/","text":"@pnp/sp/site-groups \u00b6 The site groups module provides methods to manage groups for a sharepoint site. ISiteGroups \u00b6 Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; Preset: All import {sp, SiteGroups } from \"@pnp/sp/presets/all\"; Get all site groups \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // gets all site groups of the web const groups = await sp.web.siteGroups(); Get the associated groups of a web \u00b6 You can get the associated Owner, Member and Visitor groups of a web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // Gets the associated visitors group of a web const visitorGroup = await sp.web.associatedVisitorGroup(); // Gets the associated members group of a web const memberGroup = await sp.web.associatedMemberGroup(); // Gets the associated owners group of a web const ownerGroup = await sp.web.associatedOwnerGroup(); Create the default associated groups for a web \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // Breaks permission inheritance and creates the default associated groups for the web // Login name of the owner const owner1 = \"owner@example.onmicrosoft.com\"; // Specify true, the permissions should be copied from the current parent scope, else false const copyRoleAssignments = false; // Specify true to make all child securable objects inherit role assignments from the current object const clearSubScopes = true; await sp.web.createDefaultAssociatedGroups(\"PnP Site\", owner1, copyRoleAssignments, clearSubScopes); Create a new site group \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // Creates a new site group with the specified title await sp.web.siteGroups.add({\"Title\":\"new group name\"}); ISiteGroup \u00b6 Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; Preset: All import {sp, SiteGroups, SiteGroup } from \"@pnp/sp/presets/all\"; Getting and updating the groups of a sharepoint web \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; // get the group using a group id const groupID = 33; let grp = await sp.web.siteGroups.getById(groupID)(); // get the group using the group's name const groupName = \"ClassicTeam Visitors\"; grp = await sp.web.siteGroups.getByName(groupName)(); // update a group await sp.web.siteGroups.getById(groupID).update({\"Title\": \"New Group Title\"}); // delete a group from the site using group id await sp.web.siteGroups.removeById(groupID); // delete a group from the site using group name await sp.web.siteGroups.removeByLoginName(groupName); Getting all users of a group \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; // get all users of group const groupID = 7; const users = await sp.web.siteGroups.getById(groupID).users(); Updating the owner of a site group \u00b6 Unfortunately for now setting the owner of a group as another or same SharePoint group is currently unsupported in REST. Setting the owner as a user is supported. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; // Update the owner with a user id await sp.web.siteGroups.getById(7).setUserAsOwner(4);","title":"@pnp/sp/site-groups"},{"location":"v2/sp/site-groups/#pnpspsite-groups","text":"The site groups module provides methods to manage groups for a sharepoint site.","title":"@pnp/sp/site-groups"},{"location":"v2/sp/site-groups/#isitegroups","text":"Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; Preset: All import {sp, SiteGroups } from \"@pnp/sp/presets/all\";","title":"ISiteGroups"},{"location":"v2/sp/site-groups/#get-all-site-groups","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // gets all site groups of the web const groups = await sp.web.siteGroups();","title":"Get all site groups"},{"location":"v2/sp/site-groups/#get-the-associated-groups-of-a-web","text":"You can get the associated Owner, Member and Visitor groups of a web import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // Gets the associated visitors group of a web const visitorGroup = await sp.web.associatedVisitorGroup(); // Gets the associated members group of a web const memberGroup = await sp.web.associatedMemberGroup(); // Gets the associated owners group of a web const ownerGroup = await sp.web.associatedOwnerGroup();","title":"Get the associated groups of a web"},{"location":"v2/sp/site-groups/#create-the-default-associated-groups-for-a-web","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // Breaks permission inheritance and creates the default associated groups for the web // Login name of the owner const owner1 = \"owner@example.onmicrosoft.com\"; // Specify true, the permissions should be copied from the current parent scope, else false const copyRoleAssignments = false; // Specify true to make all child securable objects inherit role assignments from the current object const clearSubScopes = true; await sp.web.createDefaultAssociatedGroups(\"PnP Site\", owner1, copyRoleAssignments, clearSubScopes);","title":"Create the default associated groups for a web"},{"location":"v2/sp/site-groups/#create-a-new-site-group","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; // Creates a new site group with the specified title await sp.web.siteGroups.add({\"Title\":\"new group name\"});","title":"Create a new site group"},{"location":"v2/sp/site-groups/#isitegroup","text":"Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups/web\"; Preset: All import {sp, SiteGroups, SiteGroup } from \"@pnp/sp/presets/all\";","title":"ISiteGroup"},{"location":"v2/sp/site-groups/#getting-and-updating-the-groups-of-a-sharepoint-web","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; // get the group using a group id const groupID = 33; let grp = await sp.web.siteGroups.getById(groupID)(); // get the group using the group's name const groupName = \"ClassicTeam Visitors\"; grp = await sp.web.siteGroups.getByName(groupName)(); // update a group await sp.web.siteGroups.getById(groupID).update({\"Title\": \"New Group Title\"}); // delete a group from the site using group id await sp.web.siteGroups.removeById(groupID); // delete a group from the site using group name await sp.web.siteGroups.removeByLoginName(groupName);","title":"Getting and updating the groups of a sharepoint web"},{"location":"v2/sp/site-groups/#getting-all-users-of-a-group","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; // get all users of group const groupID = 7; const users = await sp.web.siteGroups.getById(groupID).users();","title":"Getting all users of a group"},{"location":"v2/sp/site-groups/#updating-the-owner-of-a-site-group","text":"Unfortunately for now setting the owner of a group as another or same SharePoint group is currently unsupported in REST. Setting the owner as a user is supported. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-groups\"; // Update the owner with a user id await sp.web.siteGroups.getById(7).setUserAsOwner(4);","title":"Updating the owner of a site group"},{"location":"v2/sp/site-scripts/","text":"@pnp/sp/site-scripts \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; Create a new site script \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; const sitescriptContent = { \"$schema\": \"schema.json\", \"actions\": [ { \"themeName\": \"Theme Name 123\", \"verb\": \"applyTheme\", }, ], \"bindata\": {}, \"version\": 1, }; const siteScript = await sp.siteScripts.createSiteScript(\"Title\", \"description\", sitescriptContent); console.log(siteScript.Title); Retrieval \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; // Retrieving all site scripts const allSiteScripts = await sp.siteScripts.getSiteScripts(); console.log(allSiteScripts.length > 0 ? allSiteScripts[0].Title : \"\"); // Retrieving a single site script by Id const siteScript = await sp.siteScripts.getSiteScriptMetadata(\"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\"); console.log(siteScript.Title); Update and delete \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; // Update const updatedSiteScript = await sp.siteScripts.updateSiteScript({ Id: \"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\", Title: \"New Title\" }); console.log(updatedSiteScript.Title); // Delete await sp.siteScripts.deleteSiteScript(\"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\"); Get site script from a list \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; // Using the absolute URL of the list const ss = await sp.siteScripts.getSiteScriptFromList(\"https://TENANT.sharepoint.com/Lists/mylist\"); // Using the PnPjs web object to fetch the site script from a specific list const ss2 = await sp.web.lists.getByTitle(\"mylist\").getSiteScript(); Get site script from a web \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; const extractInfo = { IncludeBranding: true, IncludeLinksToExportedItems: true, IncludeRegionalSettings: true, IncludeSiteExternalSharingCapability: true, IncludeTheme: true, IncludedLists: [\"Lists/MyList\"] }; const ss = await sp.siteScripts.getSiteScriptFromWeb(\"https://TENANT.sharepoint.com/sites/mysite\", extractInfo); // Using the PnPjs web object to fetch the site script from a specific web const ss2 = await sp.web.getSiteScript(extractInfo); Execute Site Script Action \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; const siteScript = \"your site script action...\"; const ss = await sp.siteScripts.executeSiteScriptAction(siteScript); Execute site script for a specific web \u00b6 import { sp } from \"@pnp/sp\"; import { SiteScripts } \"@pnp/sp/site-scripts\"; const siteScript = \"your site script action...\"; const scriptService = SiteScripts(\"https://absolute/url/to/web\"); const ss = await scriptService.executeSiteScriptAction(siteScript);","title":"@pnp/sp/site-scripts"},{"location":"v2/sp/site-scripts/#pnpspsite-scripts","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"@pnp/sp/site-scripts"},{"location":"v2/sp/site-scripts/#create-a-new-site-script","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; const sitescriptContent = { \"$schema\": \"schema.json\", \"actions\": [ { \"themeName\": \"Theme Name 123\", \"verb\": \"applyTheme\", }, ], \"bindata\": {}, \"version\": 1, }; const siteScript = await sp.siteScripts.createSiteScript(\"Title\", \"description\", sitescriptContent); console.log(siteScript.Title);","title":"Create a new site script"},{"location":"v2/sp/site-scripts/#retrieval","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; // Retrieving all site scripts const allSiteScripts = await sp.siteScripts.getSiteScripts(); console.log(allSiteScripts.length > 0 ? allSiteScripts[0].Title : \"\"); // Retrieving a single site script by Id const siteScript = await sp.siteScripts.getSiteScriptMetadata(\"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\"); console.log(siteScript.Title);","title":"Retrieval"},{"location":"v2/sp/site-scripts/#update-and-delete","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; // Update const updatedSiteScript = await sp.siteScripts.updateSiteScript({ Id: \"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\", Title: \"New Title\" }); console.log(updatedSiteScript.Title); // Delete await sp.siteScripts.deleteSiteScript(\"884ed56b-1aab-4653-95cf-4be0bfa5ef0a\");","title":"Update and delete"},{"location":"v2/sp/site-scripts/#get-site-script-from-a-list","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; // Using the absolute URL of the list const ss = await sp.siteScripts.getSiteScriptFromList(\"https://TENANT.sharepoint.com/Lists/mylist\"); // Using the PnPjs web object to fetch the site script from a specific list const ss2 = await sp.web.lists.getByTitle(\"mylist\").getSiteScript();","title":"Get site script from a list"},{"location":"v2/sp/site-scripts/#get-site-script-from-a-web","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; const extractInfo = { IncludeBranding: true, IncludeLinksToExportedItems: true, IncludeRegionalSettings: true, IncludeSiteExternalSharingCapability: true, IncludeTheme: true, IncludedLists: [\"Lists/MyList\"] }; const ss = await sp.siteScripts.getSiteScriptFromWeb(\"https://TENANT.sharepoint.com/sites/mysite\", extractInfo); // Using the PnPjs web object to fetch the site script from a specific web const ss2 = await sp.web.getSiteScript(extractInfo);","title":"Get site script from a web"},{"location":"v2/sp/site-scripts/#execute-site-script-action","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/site-scripts\"; const siteScript = \"your site script action...\"; const ss = await sp.siteScripts.executeSiteScriptAction(siteScript);","title":"Execute Site Script Action"},{"location":"v2/sp/site-scripts/#execute-site-script-for-a-specific-web","text":"import { sp } from \"@pnp/sp\"; import { SiteScripts } \"@pnp/sp/site-scripts\"; const siteScript = \"your site script action...\"; const scriptService = SiteScripts(\"https://absolute/url/to/web\"); const ss = await scriptService.executeSiteScriptAction(siteScript);","title":"Execute site script for a specific web"},{"location":"v2/sp/site-users/","text":"@pnp/sp/site-users \u00b6 The site users module provides methods to manage users for a sharepoint site. ISiteUsers \u00b6 Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; Preset: All import {sp, SiteUsers } from \"@pnp/sp/presets/all\"; Get all site user \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const users = await sp.web.siteUsers(); Get Current user \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; let user = await sp.web.currentUser(); Get user by id \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const id = 6; user = await sp.web.getUserById(id); Ensure user \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const username = \"usernames@microsoft.com\"; result = await sp.web.ensureUser(username); ISiteUser \u00b6 Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; Preset: All import {sp, SiteUsers, SiteUser } from \"@pnp/sp/presets/all\"; Get user Groups \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; let groups = await sp.web.currentUser.groups(); Add user to Site collection \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const user = await sp.web.ensureUser(\"userLoginname\") const users = await sp.web.siteUsers; await users.add(user.data.LoginName); Get user \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; // get user object by id const user = await sp.web.siteUsers.getById(6); //get user object by Email const user = await sp.web.siteUsers.getByEmail(\"user@mail.com\"); //get user object by LoginName const user = await sp.web.siteUsers.getByLoginName(\"userLoginName\"); Update user \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; let userProps = await sp.web.currentUser(); userProps.Title = \"New title\"; await sp.web.currentUser.update(userProps); Remove user \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; // remove user by id await sp.web.siteUsers.removeById(6); // remove user by LoginName await sp.web.siteUsers.removeByLoginName(6); ISiteUserProps \u00b6 User properties: Property Name Type Description Email string Contains Site user email Id Number Contains Site user Id IsHiddenInUI Boolean Site user IsHiddenInUI IsShareByEmailGuestUser boolean Site user is external user IsSiteAdmin Boolean Describes if Site user Is Site Admin LoginName string Site user LoginName PrincipalType number Site user Principal type Title string Site user Title interface ISiteUserProps { /** * Contains Site user email * */ Email: string; /** * Contains Site user Id * */ Id: number; /** * Site user IsHiddenInUI * */ IsHiddenInUI: boolean; /** * Site user IsShareByEmailGuestUser * */ IsShareByEmailGuestUser: boolean; /** * Describes if Site user Is Site Admin * */ IsSiteAdmin: boolean; /** * Site user LoginName * */ LoginName: string; /** * Site user Principal type * */ PrincipalType: number | PrincipalType; /** * Site user Title * */ Title: string; }","title":"@pnp/sp/site-users"},{"location":"v2/sp/site-users/#pnpspsite-users","text":"The site users module provides methods to manage users for a sharepoint site.","title":"@pnp/sp/site-users"},{"location":"v2/sp/site-users/#isiteusers","text":"Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; Preset: All import {sp, SiteUsers } from \"@pnp/sp/presets/all\";","title":"ISiteUsers"},{"location":"v2/sp/site-users/#get-all-site-user","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const users = await sp.web.siteUsers();","title":"Get all site user"},{"location":"v2/sp/site-users/#get-current-user","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; let user = await sp.web.currentUser();","title":"Get Current user"},{"location":"v2/sp/site-users/#get-user-by-id","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const id = 6; user = await sp.web.getUserById(id);","title":"Get user by id"},{"location":"v2/sp/site-users/#ensure-user","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const username = \"usernames@microsoft.com\"; result = await sp.web.ensureUser(username);","title":"Ensure user"},{"location":"v2/sp/site-users/#isiteuser","text":"Scenario Import Statement Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users\"; Selective 3 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; Preset: All import {sp, SiteUsers, SiteUser } from \"@pnp/sp/presets/all\";","title":"ISiteUser"},{"location":"v2/sp/site-users/#get-user-groups","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; let groups = await sp.web.currentUser.groups();","title":"Get user Groups"},{"location":"v2/sp/site-users/#add-user-to-site-collection","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; const user = await sp.web.ensureUser(\"userLoginname\") const users = await sp.web.siteUsers; await users.add(user.data.LoginName);","title":"Add user to Site collection"},{"location":"v2/sp/site-users/#get-user","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; // get user object by id const user = await sp.web.siteUsers.getById(6); //get user object by Email const user = await sp.web.siteUsers.getByEmail(\"user@mail.com\"); //get user object by LoginName const user = await sp.web.siteUsers.getByLoginName(\"userLoginName\");","title":"Get user"},{"location":"v2/sp/site-users/#update-user","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; let userProps = await sp.web.currentUser(); userProps.Title = \"New title\"; await sp.web.currentUser.update(userProps);","title":"Update user"},{"location":"v2/sp/site-users/#remove-user","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/site-users/web\"; // remove user by id await sp.web.siteUsers.removeById(6); // remove user by LoginName await sp.web.siteUsers.removeByLoginName(6);","title":"Remove user"},{"location":"v2/sp/site-users/#isiteuserprops","text":"User properties: Property Name Type Description Email string Contains Site user email Id Number Contains Site user Id IsHiddenInUI Boolean Site user IsHiddenInUI IsShareByEmailGuestUser boolean Site user is external user IsSiteAdmin Boolean Describes if Site user Is Site Admin LoginName string Site user LoginName PrincipalType number Site user Principal type Title string Site user Title interface ISiteUserProps { /** * Contains Site user email * */ Email: string; /** * Contains Site user Id * */ Id: number; /** * Site user IsHiddenInUI * */ IsHiddenInUI: boolean; /** * Site user IsShareByEmailGuestUser * */ IsShareByEmailGuestUser: boolean; /** * Describes if Site user Is Site Admin * */ IsSiteAdmin: boolean; /** * Site user LoginName * */ LoginName: string; /** * Site user Principal type * */ PrincipalType: number | PrincipalType; /** * Site user Title * */ Title: string; }","title":"ISiteUserProps"},{"location":"v2/sp/sites/","text":"@pnp/sp/site - Site properties \u00b6 Site collection are one of the fundamental entry points while working with SharePoint. Sites serve as container for webs, lists, features and other entity types. Get context information for the current site collection \u00b6 Using the library, you can get the context information of the current site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import { IContextInfo } from \"@pnp/sp/sites\"; const oContext: IContextInfo = await sp.site.getContextInfo(); console.log(oContext.FormDigestValue); Get document libraries of a web \u00b6 Using the library, you can get a list of the document libraries present in the a given web. Note: Works only in SharePoint online import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import { IDocumentLibraryInformation } from \"@pnp/sp/sites\"; const docLibs: IDocumentLibraryInformation[] = await sp.site.getDocumentLibraries(\"https://tenant.sharepoint.com/sites/test/subsite\"); //we got the array of document library information docLibs.forEach((docLib: IDocumentLibraryInformation) => { // do something with each library }); Open Web By Id \u00b6 Because this method is a POST request you can chain off it directly. You will get back the full web properties in the data property of the return object. You can also chain directly off the returned Web instance on the web property. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const w = await sp.site.openWebById(\"111ca453-90f5-482e-a381-cee1ff383c9e\"); //we got all the data from the web as well console.log(w.data); // we can chain const w2 = await w.web.select(\"Title\")(); Get site collection url from page \u00b6 Using the library, you can get the site collection url by providing a page url import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const d: string = await sp.site.getWebUrlFromPageUrl(\"https://tenant.sharepoint.com/sites/test/Pages/test.aspx\"); console.log(d); //https://tenant.sharepoint.com/sites/test Access the root web \u00b6 There are two methods to access the root web. The first, using the rootWeb property, is best for directly accessing information about that web. If you want to chain multiple operations off of the web, better to use the getRootWeb method that will ensure the web instance is created using its own Url vs. \"_api/sites/rootweb\" which does not work for all operations. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; // use for rootweb information access const rootwebData = await sp.site.rootWeb(); // use for chaining const rootweb = await sp.site.getRootWeb(); const listData = await rootWeb.lists.getByTitle(\"MyList\")(); Create a modern communication site \u00b6 Note: Works only in SharePoint online Creates a modern communication site. Property Type Required Description Title string yes The title of the site to create. lcid number yes The default language to use for the site. shareByEmailEnabled boolean yes If set to true, it will enable sharing files via Email. By default it is set to false url string yes The fully qualified URL (e.g. https://yourtenant.sharepoint.com/sites/mysitecollection ) of the site. description string no The description of the communication site. classification string no The Site classification to use. For instance \"Contoso Classified\". See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information siteDesignId string no The Guid of the site design to be used. You can use the below default OOTB GUIDs: Topic: null Showcase: 6142d2a0-63a5-4ba0-aede-d9fefca2c767 Blank: f6cc5403-0d63-442e-96c0-285923709ffc hubSiteId string no The Guid of the already existing Hub site Owner string no Required when using app-only context. Owner principal name e.g. user@tenant.onmicrosoft.com import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const result = await sp.site.createCommunicationSite( \"Title\", 1033, true, \"https://tenant.sharepoint.com/sites/commSite\", \"Description\", \"HBI\", \"f6cc5403-0d63-442e-96c0-285923709ffc\", \"a00ec589-ea9f-4dba-a34e-67e78d41e509\", \"user@TENANT.onmicrosoft.com\"); Create from Props \u00b6 You may need to supply additional parameters such as WebTemplate, to do so please use the createCommunicationSiteFromProps method. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sites\"; // in this case you supply a single struct deinfing the creation props const result = await sp.site.createCommunicationSiteFromProps({ Owner: \"patrick@three18studios.com\", Title: \"A Test Site\", Url: \"https://{tenant}.sharepoint.com/sites/commsite2\", WebTemplate: \"STS#3\", }); Create a modern team site \u00b6 Note: Works only in SharePoint online. It wont work with App only tokens Creates a modern team site backed by O365 group. Property Type Required Description displayName string yes The title/displayName of the site to be created. alias string yes Alias of the underlying Office 365 Group. isPublic boolean yes Defines whether the Office 365 Group will be public (default), or private. lcid number yes The language to use for the site. If not specified will default to English (1033). description string no The description of the modern team site. classification string no The Site classification to use. For instance \"Contoso Classified\". See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information owners string array (string[]) no The Owners of the site to be created hubSiteId string no The Guid of the already existing Hub site siteDesignId string no The Guid of the site design to be used. You can use the below default OOTB GUIDs: Topic: null Showcase: 6142d2a0-63a5-4ba0-aede-d9fefca2c767 Blank: f6cc5403-0d63-442e-96c0-285923709ffc import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const result = await sp.site.createModernTeamSite( \"displayName\", \"alias\", true, 1033, \"description\", \"HBI\", [\"user1@tenant.onmicrosoft.com\",\"user2@tenant.onmicrosoft.com\",\"user3@tenant.onmicrosoft.com\"], \"a00ec589-ea9f-4dba-a34e-67e78d41e509\", \"f6cc5403-0d63-442e-96c0-285923709ffc\" ); console.log(d); Create from Props \u00b6 You may need to supply additional parameters, to do so please use the createModernTeamSiteFromProps method. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sites\"; // in this case you supply a single struct deinfing the creation props const result = await sp.site.createModernTeamSiteFromProps({ alias: \"JenniferGarner\", displayName: \"A Test Site\", owners: [\"patrick@three18studios.com\"], }); Delete a site collection \u00b6 Using the library, you can delete a specific site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import { Site } from \"@pnp/sp/sites\"; // Delete the current site await sp.site.delete(); // Specify which site to delete const siteUrl = \"https://tenant.sharepoint.com/sites/subsite\"; const site2 = Site(siteUrl); await site2.delete(); Check if a Site Collection Exists \u00b6 Using the library, you can check if a specific site collection exist or not on your tenant import { sp } from \"@pnp/sp\"; // Specify which site to verify const siteUrl = \"https://tenant.sharepoint.com/sites/subsite\"; const exists = sp.site.exists(siteUrl); console.log(exists);","title":"@pnp/sp/site - Site properties"},{"location":"v2/sp/sites/#pnpspsite-site-properties","text":"Site collection are one of the fundamental entry points while working with SharePoint. Sites serve as container for webs, lists, features and other entity types.","title":"@pnp/sp/site - Site properties"},{"location":"v2/sp/sites/#get-context-information-for-the-current-site-collection","text":"Using the library, you can get the context information of the current site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import { IContextInfo } from \"@pnp/sp/sites\"; const oContext: IContextInfo = await sp.site.getContextInfo(); console.log(oContext.FormDigestValue);","title":"Get context information for the current site collection"},{"location":"v2/sp/sites/#get-document-libraries-of-a-web","text":"Using the library, you can get a list of the document libraries present in the a given web. Note: Works only in SharePoint online import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import { IDocumentLibraryInformation } from \"@pnp/sp/sites\"; const docLibs: IDocumentLibraryInformation[] = await sp.site.getDocumentLibraries(\"https://tenant.sharepoint.com/sites/test/subsite\"); //we got the array of document library information docLibs.forEach((docLib: IDocumentLibraryInformation) => { // do something with each library });","title":"Get document libraries of a web"},{"location":"v2/sp/sites/#open-web-by-id","text":"Because this method is a POST request you can chain off it directly. You will get back the full web properties in the data property of the return object. You can also chain directly off the returned Web instance on the web property. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const w = await sp.site.openWebById(\"111ca453-90f5-482e-a381-cee1ff383c9e\"); //we got all the data from the web as well console.log(w.data); // we can chain const w2 = await w.web.select(\"Title\")();","title":"Open Web By Id"},{"location":"v2/sp/sites/#get-site-collection-url-from-page","text":"Using the library, you can get the site collection url by providing a page url import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const d: string = await sp.site.getWebUrlFromPageUrl(\"https://tenant.sharepoint.com/sites/test/Pages/test.aspx\"); console.log(d); //https://tenant.sharepoint.com/sites/test","title":"Get site collection url from page"},{"location":"v2/sp/sites/#access-the-root-web","text":"There are two methods to access the root web. The first, using the rootWeb property, is best for directly accessing information about that web. If you want to chain multiple operations off of the web, better to use the getRootWeb method that will ensure the web instance is created using its own Url vs. \"_api/sites/rootweb\" which does not work for all operations. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; // use for rootweb information access const rootwebData = await sp.site.rootWeb(); // use for chaining const rootweb = await sp.site.getRootWeb(); const listData = await rootWeb.lists.getByTitle(\"MyList\")();","title":"Access the root web"},{"location":"v2/sp/sites/#create-a-modern-communication-site","text":"Note: Works only in SharePoint online Creates a modern communication site. Property Type Required Description Title string yes The title of the site to create. lcid number yes The default language to use for the site. shareByEmailEnabled boolean yes If set to true, it will enable sharing files via Email. By default it is set to false url string yes The fully qualified URL (e.g. https://yourtenant.sharepoint.com/sites/mysitecollection ) of the site. description string no The description of the communication site. classification string no The Site classification to use. For instance \"Contoso Classified\". See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information siteDesignId string no The Guid of the site design to be used. You can use the below default OOTB GUIDs: Topic: null Showcase: 6142d2a0-63a5-4ba0-aede-d9fefca2c767 Blank: f6cc5403-0d63-442e-96c0-285923709ffc hubSiteId string no The Guid of the already existing Hub site Owner string no Required when using app-only context. Owner principal name e.g. user@tenant.onmicrosoft.com import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const result = await sp.site.createCommunicationSite( \"Title\", 1033, true, \"https://tenant.sharepoint.com/sites/commSite\", \"Description\", \"HBI\", \"f6cc5403-0d63-442e-96c0-285923709ffc\", \"a00ec589-ea9f-4dba-a34e-67e78d41e509\", \"user@TENANT.onmicrosoft.com\");","title":"Create a modern communication site"},{"location":"v2/sp/sites/#create-from-props","text":"You may need to supply additional parameters such as WebTemplate, to do so please use the createCommunicationSiteFromProps method. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sites\"; // in this case you supply a single struct deinfing the creation props const result = await sp.site.createCommunicationSiteFromProps({ Owner: \"patrick@three18studios.com\", Title: \"A Test Site\", Url: \"https://{tenant}.sharepoint.com/sites/commsite2\", WebTemplate: \"STS#3\", });","title":"Create from Props"},{"location":"v2/sp/sites/#create-a-modern-team-site","text":"Note: Works only in SharePoint online. It wont work with App only tokens Creates a modern team site backed by O365 group. Property Type Required Description displayName string yes The title/displayName of the site to be created. alias string yes Alias of the underlying Office 365 Group. isPublic boolean yes Defines whether the Office 365 Group will be public (default), or private. lcid number yes The language to use for the site. If not specified will default to English (1033). description string no The description of the modern team site. classification string no The Site classification to use. For instance \"Contoso Classified\". See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information owners string array (string[]) no The Owners of the site to be created hubSiteId string no The Guid of the already existing Hub site siteDesignId string no The Guid of the site design to be used. You can use the below default OOTB GUIDs: Topic: null Showcase: 6142d2a0-63a5-4ba0-aede-d9fefca2c767 Blank: f6cc5403-0d63-442e-96c0-285923709ffc import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; const result = await sp.site.createModernTeamSite( \"displayName\", \"alias\", true, 1033, \"description\", \"HBI\", [\"user1@tenant.onmicrosoft.com\",\"user2@tenant.onmicrosoft.com\",\"user3@tenant.onmicrosoft.com\"], \"a00ec589-ea9f-4dba-a34e-67e78d41e509\", \"f6cc5403-0d63-442e-96c0-285923709ffc\" ); console.log(d);","title":"Create a modern team site"},{"location":"v2/sp/sites/#create-from-props_1","text":"You may need to supply additional parameters, to do so please use the createModernTeamSiteFromProps method. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/sites\"; // in this case you supply a single struct deinfing the creation props const result = await sp.site.createModernTeamSiteFromProps({ alias: \"JenniferGarner\", displayName: \"A Test Site\", owners: [\"patrick@three18studios.com\"], });","title":"Create from Props"},{"location":"v2/sp/sites/#delete-a-site-collection","text":"Using the library, you can delete a specific site collection import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sites\"; import { Site } from \"@pnp/sp/sites\"; // Delete the current site await sp.site.delete(); // Specify which site to delete const siteUrl = \"https://tenant.sharepoint.com/sites/subsite\"; const site2 = Site(siteUrl); await site2.delete();","title":"Delete a site collection"},{"location":"v2/sp/sites/#check-if-a-site-collection-exists","text":"Using the library, you can check if a specific site collection exist or not on your tenant import { sp } from \"@pnp/sp\"; // Specify which site to verify const siteUrl = \"https://tenant.sharepoint.com/sites/subsite\"; const exists = sp.site.exists(siteUrl); console.log(exists);","title":"Check if a Site Collection Exists"},{"location":"v2/sp/social/","text":"@pnp/sp/ - social \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; The social API allows you to track followed sites, people, and docs. Note, many of these methods only work with the context of a logged in user, and not with app-only permissions. getFollowedSitesUri \u00b6 Gets a URI to a site that lists the current user's followed sites. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; const uri = await sp.social.getFollowedSitesUri(); getFollowedDocumentsUri \u00b6 Gets a URI to a site that lists the current user's followed documents. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; const uri = await sp.social.getFollowedDocumentsUri(); follow \u00b6 Makes the current user start following a user, document, site, or tag import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // follow a site const r1 = await sp.social.follow({ ActorType: SocialActorType.Site, ContentUri: \"htts://tenant.sharepoint.com/sites/site\", }); // follow a person const r2 = await sp.social.follow({ AccountName: \"i:0#.f|membership|person@tenant.com\", ActorType: SocialActorType.User, }); // follow a doc const r3 = await sp.social.follow({ ActorType: SocialActorType.Document, ContentUri: \"https://tenant.sharepoint.com/sites/dev/SitePages/Test.aspx\", }); // follow a tag // You need the tag GUID to start following a tag. // You can't get the GUID by using the REST service, but you can use the .NET client object model or the JavaScript object model. // See How to get a tag's GUID based on the tag's name by using the JavaScript object model. // https://docs.microsoft.com/en-us/sharepoint/dev/general-development/follow-content-in-sharepoint#bk_getTagGuid const r4 = await sp.social.follow({ ActorType: SocialActorType.Tag, TagGuid: \"19a4a484-c1dc-4bc5-8c93-bb96245ce928\", }); isFollowed \u00b6 Indicates whether the current user is following a specified user, document, site, or tag import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // pass the same social actor struct as shown in follow example for each type const r = await sp.social.isFollowed({ AccountName: \"i:0#.f|membership|person@tenant.com\", ActorType: SocialActorType.User, }); stopFollowing \u00b6 Makes the current user stop following a user, document, site, or tag import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // pass the same social actor struct as shown in follow example for each type const r = await sp.social.stopFollowing({ AccountName: \"i:0#.f|membership|person@tenant.com\", ActorType: SocialActorType.User, }); my \u00b6 get \u00b6 Gets this user's social information import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; const r = await sp.social.my(); followed \u00b6 Gets users, documents, sites, and tags that the current user is following based on the supplied flags. import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // get all the followed documents const r1 = await sp.social.my.followed(SocialActorTypes.Document); // get all the followed documents and sites const r2 = await sp.social.my.followed(SocialActorTypes.Document | SocialActorTypes.Site); // get all the followed sites updated in the last 24 hours const r3 = await sp.social.my.followed(SocialActorTypes.Site | SocialActorTypes.WithinLast24Hours); followedCount \u00b6 Works as followed but returns on the count of actors specified by the query import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // get the followed documents count const r = await sp.social.my.followedCount(SocialActorTypes.Document); followers \u00b6 Gets the users who are following the current user. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; // get the followed documents count const r = await sp.social.my.followers(); suggestions \u00b6 Gets users who the current user might want to follow. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; // get the followed documents count const r = await sp.social.my.suggestions();","title":"@pnp/sp/ - social"},{"location":"v2/sp/social/#pnpsp-social","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; The social API allows you to track followed sites, people, and docs. Note, many of these methods only work with the context of a logged in user, and not with app-only permissions.","title":"@pnp/sp/ - social"},{"location":"v2/sp/social/#getfollowedsitesuri","text":"Gets a URI to a site that lists the current user's followed sites. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; const uri = await sp.social.getFollowedSitesUri();","title":"getFollowedSitesUri"},{"location":"v2/sp/social/#getfolloweddocumentsuri","text":"Gets a URI to a site that lists the current user's followed documents. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; const uri = await sp.social.getFollowedDocumentsUri();","title":"getFollowedDocumentsUri"},{"location":"v2/sp/social/#follow","text":"Makes the current user start following a user, document, site, or tag import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // follow a site const r1 = await sp.social.follow({ ActorType: SocialActorType.Site, ContentUri: \"htts://tenant.sharepoint.com/sites/site\", }); // follow a person const r2 = await sp.social.follow({ AccountName: \"i:0#.f|membership|person@tenant.com\", ActorType: SocialActorType.User, }); // follow a doc const r3 = await sp.social.follow({ ActorType: SocialActorType.Document, ContentUri: \"https://tenant.sharepoint.com/sites/dev/SitePages/Test.aspx\", }); // follow a tag // You need the tag GUID to start following a tag. // You can't get the GUID by using the REST service, but you can use the .NET client object model or the JavaScript object model. // See How to get a tag's GUID based on the tag's name by using the JavaScript object model. // https://docs.microsoft.com/en-us/sharepoint/dev/general-development/follow-content-in-sharepoint#bk_getTagGuid const r4 = await sp.social.follow({ ActorType: SocialActorType.Tag, TagGuid: \"19a4a484-c1dc-4bc5-8c93-bb96245ce928\", });","title":"follow"},{"location":"v2/sp/social/#isfollowed","text":"Indicates whether the current user is following a specified user, document, site, or tag import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // pass the same social actor struct as shown in follow example for each type const r = await sp.social.isFollowed({ AccountName: \"i:0#.f|membership|person@tenant.com\", ActorType: SocialActorType.User, });","title":"isFollowed"},{"location":"v2/sp/social/#stopfollowing","text":"Makes the current user stop following a user, document, site, or tag import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // pass the same social actor struct as shown in follow example for each type const r = await sp.social.stopFollowing({ AccountName: \"i:0#.f|membership|person@tenant.com\", ActorType: SocialActorType.User, });","title":"stopFollowing"},{"location":"v2/sp/social/#my","text":"","title":"my"},{"location":"v2/sp/social/#get","text":"Gets this user's social information import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; const r = await sp.social.my();","title":"get"},{"location":"v2/sp/social/#followed","text":"Gets users, documents, sites, and tags that the current user is following based on the supplied flags. import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // get all the followed documents const r1 = await sp.social.my.followed(SocialActorTypes.Document); // get all the followed documents and sites const r2 = await sp.social.my.followed(SocialActorTypes.Document | SocialActorTypes.Site); // get all the followed sites updated in the last 24 hours const r3 = await sp.social.my.followed(SocialActorTypes.Site | SocialActorTypes.WithinLast24Hours);","title":"followed"},{"location":"v2/sp/social/#followedcount","text":"Works as followed but returns on the count of actors specified by the query import { sp } from \"@pnp/sp\"; import { SocialActorType } from \"@pnp/sp/social\"; // get the followed documents count const r = await sp.social.my.followedCount(SocialActorTypes.Document);","title":"followedCount"},{"location":"v2/sp/social/#followers","text":"Gets the users who are following the current user. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; // get the followed documents count const r = await sp.social.my.followers();","title":"followers"},{"location":"v2/sp/social/#suggestions","text":"Gets users who the current user might want to follow. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/social\"; // get the followed documents count const r = await sp.social.my.suggestions();","title":"suggestions"},{"location":"v2/sp/sp-utilities-utility/","text":"@pnp/sp/utilities \u00b6 Through the REST api you are able to call a subset of the SP.Utilities.Utility methods. We have explicitly defined some of these methods and provided a method to call any others in a generic manner. These methods are exposed on pnp.sp.utility and support batching and caching. sendEmail \u00b6 This methods allows you to send an email based on the supplied arguments. The method takes a single argument, a plain object defined by the EmailProperties interface (shown below). EmailProperties \u00b6 export interface EmailProperties { To: string[]; CC?: string[]; BCC?: string[]; Subject: string; Body: string; AdditionalHeaders?: TypedHash; From?: string; } Usage \u00b6 You must define the To, Subject, and Body values - the remaining are optional. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; import { IEmailProperties } from \"@pnp/sp/sputilities\"; const emailProps: IEmailProperties = { To: [\"user@site.com\"], CC: [\"user2@site.com\", \"user3@site.com\"], BCC: [\"user4@site.com\", \"user5@site.com\"], Subject: \"This email is about...\", Body: \"Here is the body. It supports html\", AdditionalHeaders: { \"content-type\": \"text/html\" } }; await sp.utility.sendEmail(emailProps); console.log(\"Email Sent!\"); getCurrentUserEmailAddresses \u00b6 This method returns the current user's email addresses known to SharePoint. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let addressString: string = await sp.utility.getCurrentUserEmailAddresses(); // and use it with sendEmail await sp.utility.sendEmail({ To: [addressString], Subject: \"This email is about...\", Body: \"Here is the body. It supports html\", AdditionalHeaders: { \"content-type\": \"text/html\" }, }); resolvePrincipal \u00b6 Gets information about a principal that matches the specified Search criteria import { sp, IPrincipalInfo, PrincipalType, PrincipalSource } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let principal : IPrincipalInfo = await sp.utility.resolvePrincipal(\"user@site.com\", PrincipalType.User, PrincipalSource.All, true, false, true); console.log(principal); searchPrincipals \u00b6 Gets information about the principals that match the specified Search criteria. import { sp, IPrincipalInfo, PrincipalType, PrincipalSource } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let principals : IPrincipalInfo[] = await sp.utility.searchPrincipals(\"john\", PrincipalType.User, PrincipalSource.All,\"\", 10); console.log(principals); createEmailBodyForInvitation \u00b6 Gets the external (outside the firewall) URL to a document or resource in a site. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let url : string = await sp.utility.createEmailBodyForInvitation(\"https://contoso.sharepoint.com/sites/dev/SitePages/DevHome.aspx\"); console.log(url); expandGroupsToPrincipals \u00b6 Resolves the principals contained within the supplied groups import { sp, IPrincipalInfo } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let principals : IPrincipalInfo[] = await sp.utility.expandGroupsToPrincipals([\"Dev Owners\", \"Dev Members\"]); console.log(principals); // optionally supply a max results count. Default is 30. let principals : IPrincipalInfo[] = await sp.utility.expandGroupsToPrincipals([\"Dev Owners\", \"Dev Members\"], 10); console.log(principals); createWikiPage \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; import { ICreateWikiPageResult } from \"@pnp/sp/sputilities\"; let newPage : ICreateWikiPageResult = await sp.utility.createWikiPage({ ServerRelativeUrl: \"/sites/dev/SitePages/mynewpage.aspx\", WikiHtmlContent: \"This is my page content. It supports rich html.\", }); // newPage contains the raw data returned by the service console.log(newPage.data); // newPage contains a File instance you can use to further update the new page let file = await newPage.file(); console.log(file);","title":"@pnp/sp/utilities"},{"location":"v2/sp/sp-utilities-utility/#pnpsputilities","text":"Through the REST api you are able to call a subset of the SP.Utilities.Utility methods. We have explicitly defined some of these methods and provided a method to call any others in a generic manner. These methods are exposed on pnp.sp.utility and support batching and caching.","title":"@pnp/sp/utilities"},{"location":"v2/sp/sp-utilities-utility/#sendemail","text":"This methods allows you to send an email based on the supplied arguments. The method takes a single argument, a plain object defined by the EmailProperties interface (shown below).","title":"sendEmail"},{"location":"v2/sp/sp-utilities-utility/#emailproperties","text":"export interface EmailProperties { To: string[]; CC?: string[]; BCC?: string[]; Subject: string; Body: string; AdditionalHeaders?: TypedHash; From?: string; }","title":"EmailProperties"},{"location":"v2/sp/sp-utilities-utility/#usage","text":"You must define the To, Subject, and Body values - the remaining are optional. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; import { IEmailProperties } from \"@pnp/sp/sputilities\"; const emailProps: IEmailProperties = { To: [\"user@site.com\"], CC: [\"user2@site.com\", \"user3@site.com\"], BCC: [\"user4@site.com\", \"user5@site.com\"], Subject: \"This email is about...\", Body: \"Here is the body. It supports html\", AdditionalHeaders: { \"content-type\": \"text/html\" } }; await sp.utility.sendEmail(emailProps); console.log(\"Email Sent!\");","title":"Usage"},{"location":"v2/sp/sp-utilities-utility/#getcurrentuseremailaddresses","text":"This method returns the current user's email addresses known to SharePoint. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let addressString: string = await sp.utility.getCurrentUserEmailAddresses(); // and use it with sendEmail await sp.utility.sendEmail({ To: [addressString], Subject: \"This email is about...\", Body: \"Here is the body. It supports html\", AdditionalHeaders: { \"content-type\": \"text/html\" }, });","title":"getCurrentUserEmailAddresses"},{"location":"v2/sp/sp-utilities-utility/#resolveprincipal","text":"Gets information about a principal that matches the specified Search criteria import { sp, IPrincipalInfo, PrincipalType, PrincipalSource } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let principal : IPrincipalInfo = await sp.utility.resolvePrincipal(\"user@site.com\", PrincipalType.User, PrincipalSource.All, true, false, true); console.log(principal);","title":"resolvePrincipal"},{"location":"v2/sp/sp-utilities-utility/#searchprincipals","text":"Gets information about the principals that match the specified Search criteria. import { sp, IPrincipalInfo, PrincipalType, PrincipalSource } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let principals : IPrincipalInfo[] = await sp.utility.searchPrincipals(\"john\", PrincipalType.User, PrincipalSource.All,\"\", 10); console.log(principals);","title":"searchPrincipals"},{"location":"v2/sp/sp-utilities-utility/#createemailbodyforinvitation","text":"Gets the external (outside the firewall) URL to a document or resource in a site. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let url : string = await sp.utility.createEmailBodyForInvitation(\"https://contoso.sharepoint.com/sites/dev/SitePages/DevHome.aspx\"); console.log(url);","title":"createEmailBodyForInvitation"},{"location":"v2/sp/sp-utilities-utility/#expandgroupstoprincipals","text":"Resolves the principals contained within the supplied groups import { sp, IPrincipalInfo } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; let principals : IPrincipalInfo[] = await sp.utility.expandGroupsToPrincipals([\"Dev Owners\", \"Dev Members\"]); console.log(principals); // optionally supply a max results count. Default is 30. let principals : IPrincipalInfo[] = await sp.utility.expandGroupsToPrincipals([\"Dev Owners\", \"Dev Members\"], 10); console.log(principals);","title":"expandGroupsToPrincipals"},{"location":"v2/sp/sp-utilities-utility/#createwikipage","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/sputilities\"; import { ICreateWikiPageResult } from \"@pnp/sp/sputilities\"; let newPage : ICreateWikiPageResult = await sp.utility.createWikiPage({ ServerRelativeUrl: \"/sites/dev/SitePages/mynewpage.aspx\", WikiHtmlContent: \"This is my page content. It supports rich html.\", }); // newPage contains the raw data returned by the service console.log(newPage.data); // newPage contains a File instance you can use to further update the new page let file = await newPage.file(); console.log(file);","title":"createWikiPage"},{"location":"v2/sp/subscriptions/","text":"@pnp/sp/subscriptions \u00b6 Webhooks on a SharePoint list are used to notify any change in the list, to other applications using a push model. This module provides methods to add, update or delete webhooks on a particular SharePoint list or library. ISubscriptions \u00b6 Scenario Import Statement Selective import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { Subscriptions, ISubscriptions} from \"@pnp/sp/subscriptions\"; import \"@pnp/sp/subscriptions/list\" Preset: All import {sp, Webs, IWebs, Lists, ILists, Subscriptions, ISubscriptions, Subscription, ISubscription} from \"@pnp/sp/presets/all\"; Add a webhook \u00b6 Using this library, you can add a webhook to a specified list within the SharePoint site. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { Subscriptions, ISubscriptions} from \"@pnp/sp/subscriptions\"; import \"@pnp/sp/subscriptions/list\"; // This is the URL which will be called by SharePoint when there is a change in the list const notificationUrl = \"\"; // Set the expiry date to 180 days from now, which is the maximum allowed for the webhook expiry date. const expiryDate = dateAdd(new Date(), \"day\" , 180).toISOString(); // Adds a webhook to the Documents library var res = await sp.web.lists.getByTitle(\"Documents\").subscriptions.add(notificationUrl,expiryDate); Get all webhooks added to a list \u00b6 Read all the webhooks' details which are associated to the list import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/subscriptions\"; const res = await sp.web.lists.getByTitle(\"Documents\").subscriptions(); ISubscription \u00b6 This interface provides the methods for managing a particular webhook. Scenario Import Statement Selective import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { Subscriptions, ISubscriptions, Subscription, ISubscription} from \"@pnp/sp/subscriptions\"; import \"@pnp/sp/subscriptions/list\" Preset: All import { sp, Webs, IWebs, Lists, ILists, Subscriptions, ISubscriptions, Subscription, ISubscription } from \"@pnp/sp/presets/all\"; Managing a webhook \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/subscriptions\"; // Get details of a webhook based on its ID const webhookId = \"1f029e5c-16e4-4941-b46f-67895118763f\"; const webhook = await sp.web.lists.getByTitle(\"Documents\").subscriptions.getById(webhookId)(); // Update a webhook const newDate = dateAdd(new Date(), \"day\" , 150).toISOString(); const updatedWebhook = await sp.web.lists.getByTitle(\"Documents\").subscriptions.getById(webhookId).update(newDate); // Delete a webhook await sp.web.lists.getByTitle(\"Documents\").subscriptions.getById(webhookId).delete();","title":"@pnp/sp/subscriptions"},{"location":"v2/sp/subscriptions/#pnpspsubscriptions","text":"Webhooks on a SharePoint list are used to notify any change in the list, to other applications using a push model. This module provides methods to add, update or delete webhooks on a particular SharePoint list or library.","title":"@pnp/sp/subscriptions"},{"location":"v2/sp/subscriptions/#isubscriptions","text":"Scenario Import Statement Selective import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { Subscriptions, ISubscriptions} from \"@pnp/sp/subscriptions\"; import \"@pnp/sp/subscriptions/list\" Preset: All import {sp, Webs, IWebs, Lists, ILists, Subscriptions, ISubscriptions, Subscription, ISubscription} from \"@pnp/sp/presets/all\";","title":"ISubscriptions"},{"location":"v2/sp/subscriptions/#add-a-webhook","text":"Using this library, you can add a webhook to a specified list within the SharePoint site. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { Subscriptions, ISubscriptions} from \"@pnp/sp/subscriptions\"; import \"@pnp/sp/subscriptions/list\"; // This is the URL which will be called by SharePoint when there is a change in the list const notificationUrl = \"\"; // Set the expiry date to 180 days from now, which is the maximum allowed for the webhook expiry date. const expiryDate = dateAdd(new Date(), \"day\" , 180).toISOString(); // Adds a webhook to the Documents library var res = await sp.web.lists.getByTitle(\"Documents\").subscriptions.add(notificationUrl,expiryDate);","title":"Add a webhook"},{"location":"v2/sp/subscriptions/#get-all-webhooks-added-to-a-list","text":"Read all the webhooks' details which are associated to the list import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/subscriptions\"; const res = await sp.web.lists.getByTitle(\"Documents\").subscriptions();","title":"Get all webhooks added to a list"},{"location":"v2/sp/subscriptions/#isubscription","text":"This interface provides the methods for managing a particular webhook. Scenario Import Statement Selective import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import { Subscriptions, ISubscriptions, Subscription, ISubscription} from \"@pnp/sp/subscriptions\"; import \"@pnp/sp/subscriptions/list\" Preset: All import { sp, Webs, IWebs, Lists, ILists, Subscriptions, ISubscriptions, Subscription, ISubscription } from \"@pnp/sp/presets/all\";","title":"ISubscription"},{"location":"v2/sp/subscriptions/#managing-a-webhook","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/subscriptions\"; // Get details of a webhook based on its ID const webhookId = \"1f029e5c-16e4-4941-b46f-67895118763f\"; const webhook = await sp.web.lists.getByTitle(\"Documents\").subscriptions.getById(webhookId)(); // Update a webhook const newDate = dateAdd(new Date(), \"day\" , 150).toISOString(); const updatedWebhook = await sp.web.lists.getByTitle(\"Documents\").subscriptions.getById(webhookId).update(newDate); // Delete a webhook await sp.web.lists.getByTitle(\"Documents\").subscriptions.getById(webhookId).delete();","title":"Managing a webhook"},{"location":"v2/sp/taxonomy/","text":"@pnp/sp/taxonomy \u00b6 Provides access to the v2.1 api term store Docs updated with v2.0.9 release as the underlying API changed. \u00b6 NOTE: This API may change so please be aware updates to the taxonomy module will not trigger a major version bump in PnPjs even if they are breaking. Once things stabalize this note will be removed. Term Store \u00b6 Access term store data from the root sp object as shown below. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermStoreInfo } from \"@pnp/sp/taxonomy\"; // get term store data const info: ITermStoreInfo = await sp.termStore(); Term Groups \u00b6 Access term group information List \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermGroupInfo } from \"@pnp/sp/taxonomy\"; // get term groups const info: ITermGroupInfo[] = await sp.termStore.groups(); Get By Id \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermGroupInfo } from \"@pnp/sp/taxonomy\"; // get term groups data const info: ITermGroupInfo = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\")(); Term Sets \u00b6 Access term set information List \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermSetInfo } from \"@pnp/sp/taxonomy\"; // get get set info const info: ITermSetInfo[] = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets(); Get By Id \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermSetInfo } from \"@pnp/sp/taxonomy\"; // get term set data const info: ITermSetInfo = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\")(); getAllChildrenAsOrderedTree \u00b6 Added in 2.0.13 This method will get all of a set's child terms in an ordered array. It is a costly method in terms of requests so we suggest you cache the results as taxonomy trees seldom change. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; import { dateAdd, PnPClientStorage } from \"@pnp/core\"; // here we get all the children of a given set const childTree = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").getAllChildrenAsOrderedTree(); // here we show caching the results using the PnPClientStorage class, there are many caching libraries and options available const store = new PnPClientStorage(); // our tree likely doesn't change much in 30 minutes for most applications // adjust to be longer or shorter as needed const cachedTree = await store.local.getOrPut(\"myKey\", () => { return sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").getAllChildrenAsOrderedTree(); }, dateAdd(new Date(), \"minute\", 30)); Terms \u00b6 Access term set information List \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; // list all the terms that are direct children of this set const infos: ITermInfo[] = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").children(); List (terms) \u00b6 Added in 2.0.13 You can use the terms property to get a flat list of all terms in the set. These terms do not contain parent/child relationship information. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; // list all the terms that are direct children of this set const infos: ITermInfo[] = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").terms(); Get By Id \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; // get term set data const info: ITermInfo = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").getTermById(\"338666a8-1111-2222-3333-f72471314e72\")(); Get Term Parent \u00b6 Behavior Change in 2.1.0 The server API changed again, resulting in the removal of the \"parent\" property from ITerm as it is not longer supported as a path property. You now must use \"expand\" to load a term's parent information. The side affect of this is that the parent is no longer chainable, meaning you need to load a new term instance to work with the parent term. An approach for this is shown below. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; // get a ref to the set const set = sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\"); // get a term's information and expand parent to get the parent info as well const w = await set.getTermById(\"338666a8-1111-2222-3333-f72471314e72\").expand(\"parent\")(); // get a ref to the parent term const parent = set.getTermById(w.parent.id); // make a request for the parent term's info - this data currently match the results in the expand call above, but this // is to demonstrate how to gain a ref to the parent and select its data const parentInfo = await parent.select(\"Id\", \"Descriptions\")();","title":"@pnp/sp/taxonomy"},{"location":"v2/sp/taxonomy/#pnpsptaxonomy","text":"Provides access to the v2.1 api term store","title":"@pnp/sp/taxonomy"},{"location":"v2/sp/taxonomy/#docs-updated-with-v209-release-as-the-underlying-api-changed","text":"NOTE: This API may change so please be aware updates to the taxonomy module will not trigger a major version bump in PnPjs even if they are breaking. Once things stabalize this note will be removed.","title":"Docs updated with v2.0.9 release as the underlying API changed."},{"location":"v2/sp/taxonomy/#term-store","text":"Access term store data from the root sp object as shown below. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermStoreInfo } from \"@pnp/sp/taxonomy\"; // get term store data const info: ITermStoreInfo = await sp.termStore();","title":"Term Store"},{"location":"v2/sp/taxonomy/#term-groups","text":"Access term group information","title":"Term Groups"},{"location":"v2/sp/taxonomy/#list","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermGroupInfo } from \"@pnp/sp/taxonomy\"; // get term groups const info: ITermGroupInfo[] = await sp.termStore.groups();","title":"List"},{"location":"v2/sp/taxonomy/#get-by-id","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermGroupInfo } from \"@pnp/sp/taxonomy\"; // get term groups data const info: ITermGroupInfo = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\")();","title":"Get By Id"},{"location":"v2/sp/taxonomy/#term-sets","text":"Access term set information","title":"Term Sets"},{"location":"v2/sp/taxonomy/#list_1","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermSetInfo } from \"@pnp/sp/taxonomy\"; // get get set info const info: ITermSetInfo[] = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets();","title":"List"},{"location":"v2/sp/taxonomy/#get-by-id_1","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermSetInfo } from \"@pnp/sp/taxonomy\"; // get term set data const info: ITermSetInfo = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\")();","title":"Get By Id"},{"location":"v2/sp/taxonomy/#getallchildrenasorderedtree","text":"Added in 2.0.13 This method will get all of a set's child terms in an ordered array. It is a costly method in terms of requests so we suggest you cache the results as taxonomy trees seldom change. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; import { dateAdd, PnPClientStorage } from \"@pnp/core\"; // here we get all the children of a given set const childTree = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").getAllChildrenAsOrderedTree(); // here we show caching the results using the PnPClientStorage class, there are many caching libraries and options available const store = new PnPClientStorage(); // our tree likely doesn't change much in 30 minutes for most applications // adjust to be longer or shorter as needed const cachedTree = await store.local.getOrPut(\"myKey\", () => { return sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").getAllChildrenAsOrderedTree(); }, dateAdd(new Date(), \"minute\", 30));","title":"getAllChildrenAsOrderedTree"},{"location":"v2/sp/taxonomy/#terms","text":"Access term set information","title":"Terms"},{"location":"v2/sp/taxonomy/#list_2","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; // list all the terms that are direct children of this set const infos: ITermInfo[] = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").children();","title":"List"},{"location":"v2/sp/taxonomy/#list-terms","text":"Added in 2.0.13 You can use the terms property to get a flat list of all terms in the set. These terms do not contain parent/child relationship information. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; // list all the terms that are direct children of this set const infos: ITermInfo[] = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").terms();","title":"List (terms)"},{"location":"v2/sp/taxonomy/#get-by-id_2","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; import { ITermInfo } from \"@pnp/sp/taxonomy\"; // get term set data const info: ITermInfo = await sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\").getTermById(\"338666a8-1111-2222-3333-f72471314e72\")();","title":"Get By Id"},{"location":"v2/sp/taxonomy/#get-term-parent","text":"Behavior Change in 2.1.0 The server API changed again, resulting in the removal of the \"parent\" property from ITerm as it is not longer supported as a path property. You now must use \"expand\" to load a term's parent information. The side affect of this is that the parent is no longer chainable, meaning you need to load a new term instance to work with the parent term. An approach for this is shown below. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/taxonomy\"; // get a ref to the set const set = sp.termStore.groups.getById(\"338666a8-1111-2222-3333-f72471314e72\").sets.getById(\"338666a8-1111-2222-3333-f72471314e72\"); // get a term's information and expand parent to get the parent info as well const w = await set.getTermById(\"338666a8-1111-2222-3333-f72471314e72\").expand(\"parent\")(); // get a ref to the parent term const parent = set.getTermById(w.parent.id); // make a request for the parent term's info - this data currently match the results in the expand call above, but this // is to demonstrate how to gain a ref to the parent and select its data const parentInfo = await parent.select(\"Id\", \"Descriptions\")();","title":"Get Term Parent"},{"location":"v2/sp/tenant-properties/","text":"@pnp/sp/web - tenant properties \u00b6 You can set, read, and remove tenant properties using the methods shown below: setStorageEntity \u00b6 This method MUST be called in the context of the app catalog web or you will get an access denied message. import { Web } from \"@pnp/sp/webs\"; const w = Web(\"https://tenant.sharepoint.com/sites/appcatalog/\"); // specify required key and value await w.setStorageEntity(\"Test1\", \"Value 1\"); // specify optional description and comments await w.setStorageEntity(\"Test2\", \"Value 2\", \"description\", \"comments\"); getStorageEntity \u00b6 This method can be used from any web to retrieve values previously set. import { sp, IStorageEntity } from \"@pnp/sp/presets/all\"; const prop: IStorageEntity = await sp.web.getStorageEntity(\"Test1\"); console.log(prop.Value); removeStorageEntity \u00b6 This method MUST be called in the context of the app catalog web or you will get an access denied message. import { Web } from \"@pnp/sp/webs\"; const w = Web(\"https://tenant.sharepoint.com/sites/appcatalog/\"); await w.removeStorageEntity(\"Test1\");","title":"@pnp/sp/web - tenant properties"},{"location":"v2/sp/tenant-properties/#pnpspweb-tenant-properties","text":"You can set, read, and remove tenant properties using the methods shown below:","title":"@pnp/sp/web - tenant properties"},{"location":"v2/sp/tenant-properties/#setstorageentity","text":"This method MUST be called in the context of the app catalog web or you will get an access denied message. import { Web } from \"@pnp/sp/webs\"; const w = Web(\"https://tenant.sharepoint.com/sites/appcatalog/\"); // specify required key and value await w.setStorageEntity(\"Test1\", \"Value 1\"); // specify optional description and comments await w.setStorageEntity(\"Test2\", \"Value 2\", \"description\", \"comments\");","title":"setStorageEntity"},{"location":"v2/sp/tenant-properties/#getstorageentity","text":"This method can be used from any web to retrieve values previously set. import { sp, IStorageEntity } from \"@pnp/sp/presets/all\"; const prop: IStorageEntity = await sp.web.getStorageEntity(\"Test1\"); console.log(prop.Value);","title":"getStorageEntity"},{"location":"v2/sp/tenant-properties/#removestorageentity","text":"This method MUST be called in the context of the app catalog web or you will get an access denied message. import { Web } from \"@pnp/sp/webs\"; const w = Web(\"https://tenant.sharepoint.com/sites/appcatalog/\"); await w.removeStorageEntity(\"Test1\");","title":"removeStorageEntity"},{"location":"v2/sp/user-custom-actions/","text":"@pnp/sp/user-custom-actions \u00b6 Represents a custom action associated with a SharePoint list, web or site collection. IUserCustomActions \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { IUserCustomActions, IUserCustomAction } from \"@pnp/sp/user-custom-actions\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/user-custom-actions\"; Preset: All import { sp, IUserCustomActions, IUserCustomAction } from \"@pnp/sp/presents/all\"; Get a collection of User Custom Actions from a web \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; const userCustomActions = sp.web.userCustomActions(); Add a new User Custom Action \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; import { IUserCustomActionAddResult } from '@pnp/sp/user-custom-actions'; const newValues: TypedHash = { \"Title\": \"New Title\", \"Description\": \"New Description\", \"Location\": \"ScriptLink\", \"ScriptSrc\": \"https://...\" }; const response : IUserCustomActionAddResult = await sp.web.userCustomActions.add(newValues); Get a User Custom Action by ID \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; const uca: IUserCustomAction = sp.web.userCustomActions.getById(\"00000000-0000-0000-0000-000000000000\"); const ucaData = await uca(); Clear the User Custom Action collection \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; // Site collection level await sp.site.userCustomActions.clear(); // Site (web) level await sp.web.userCustomActions.clear(); // List level await sp.web.lists.getByTitle(\"Documents\").userCustomActions.clear(); IUserCustomAction \u00b6 Update an existing User Custom Action \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; import { IUserCustomActionUpdateResult } from '@pnp/sp/user-custom-actions'; const uca = sp.web.userCustomActions.getById(\"00000000-0000-0000-0000-000000000000\"); const newValues: TypedHash = { \"Title\": \"New Title\", \"Description\": \"New Description\", \"ScriptSrc\": \"https://...\" }; const response: IUserCustomActionUpdateResult = uca.update(newValues);","title":"@pnp/sp/user-custom-actions"},{"location":"v2/sp/user-custom-actions/#pnpspuser-custom-actions","text":"Represents a custom action associated with a SharePoint list, web or site collection.","title":"@pnp/sp/user-custom-actions"},{"location":"v2/sp/user-custom-actions/#iusercustomactions","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { IUserCustomActions, IUserCustomAction } from \"@pnp/sp/user-custom-actions\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/user-custom-actions\"; Preset: All import { sp, IUserCustomActions, IUserCustomAction } from \"@pnp/sp/presents/all\";","title":"IUserCustomActions"},{"location":"v2/sp/user-custom-actions/#get-a-collection-of-user-custom-actions-from-a-web","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; const userCustomActions = sp.web.userCustomActions();","title":"Get a collection of User Custom Actions from a web"},{"location":"v2/sp/user-custom-actions/#add-a-new-user-custom-action","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; import { IUserCustomActionAddResult } from '@pnp/sp/user-custom-actions'; const newValues: TypedHash = { \"Title\": \"New Title\", \"Description\": \"New Description\", \"Location\": \"ScriptLink\", \"ScriptSrc\": \"https://...\" }; const response : IUserCustomActionAddResult = await sp.web.userCustomActions.add(newValues);","title":"Add a new User Custom Action"},{"location":"v2/sp/user-custom-actions/#get-a-user-custom-action-by-id","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; const uca: IUserCustomAction = sp.web.userCustomActions.getById(\"00000000-0000-0000-0000-000000000000\"); const ucaData = await uca();","title":"Get a User Custom Action by ID"},{"location":"v2/sp/user-custom-actions/#clear-the-user-custom-action-collection","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; // Site collection level await sp.site.userCustomActions.clear(); // Site (web) level await sp.web.userCustomActions.clear(); // List level await sp.web.lists.getByTitle(\"Documents\").userCustomActions.clear();","title":"Clear the User Custom Action collection"},{"location":"v2/sp/user-custom-actions/#iusercustomaction","text":"","title":"IUserCustomAction"},{"location":"v2/sp/user-custom-actions/#update-an-existing-user-custom-action","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/user-custom-actions\"; import { IUserCustomActionUpdateResult } from '@pnp/sp/user-custom-actions'; const uca = sp.web.userCustomActions.getById(\"00000000-0000-0000-0000-000000000000\"); const newValues: TypedHash = { \"Title\": \"New Title\", \"Description\": \"New Description\", \"ScriptSrc\": \"https://...\" }; const response: IUserCustomActionUpdateResult = uca.update(newValues);","title":"Update an existing User Custom Action"},{"location":"v2/sp/views/","text":"@pnp/sp/views \u00b6 Views define the columns, ordering, and other details we see when we look at a list. You can have multiple views for a list, including private views - and one default view. IViews \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Views, IViews } from \"@pnp/sp/views\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/views\"; Preset: All import { sp, Views, IViews } from \"@pnp/sp/presets/all\"; Get views in a list \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); // get all the views and their properties const views1 = await list.views(); // you can use odata select operations to get just a set a fields const views2 = await list.views.select(\"Id\", \"Title\")(); // get the top three views const views3 = await list.views.top(3)(); Add a View \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); // create a new view with default fields and properties const result = await list.views.add(\"My New View\"); // create a new view with specific properties const result2 = await list.views.add(\"My New View 2\", false, { RowLimit: 10, ViewQuery: \"\", }); // manipulate the view's fields await result2.view.fields.removeAll(); await Promise.all([ result2.view.fields.add(\"Title\"), result2.view.fields.add(\"Modified\"), ]); IView \u00b6 Get a View's Information \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); const result = await list.views.getById(\"{GUID view id}\")(); const result2 = await list.views.getByTitle(\"My View\")(); const result3 = await list.views.getByTitle(\"My View\").select(\"Id\", \"Title\")(); const result4 = await list.defaultView(); const result5 = await list.getView(\"{GUID view id}\")(); fields \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); const result = await list.views.getById(\"{GUID view id}\").fields(); update \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); const result = await list.views.getById(\"{GUID view id}\").update({ RowLimit: 20, }); renderAsHtml \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const result = await sp.web.lists.getByTitle(\"My List\").views.getById(\"{GUID view id}\").renderAsHtml(); setViewXml \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const viewXml = \"...\"; await sp.web.lists.getByTitle(\"My List\").views.getById(\"{GUID view id}\").setViewXml(viewXml); delete \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const viewXml = \"...\"; await sp.web.lists.getByTitle(\"My List\").views.getById(\"{GUID view id}\").delete(); ViewFields \u00b6 getSchemaXml \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const xml = await sp.web.lists.getByTitle(\"My List\").defaultView.fields.getSchemaXml(); add \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.add(\"Created\"); move \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.move(\"Created\", 0); remove \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.remove(\"Created\"); removeAll \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.removeAll();","title":"@pnp/sp/views"},{"location":"v2/sp/views/#pnpspviews","text":"Views define the columns, ordering, and other details we see when we look at a list. You can have multiple views for a list, including private views - and one default view.","title":"@pnp/sp/views"},{"location":"v2/sp/views/#iviews","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Views, IViews } from \"@pnp/sp/views\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/views\"; Preset: All import { sp, Views, IViews } from \"@pnp/sp/presets/all\";","title":"IViews"},{"location":"v2/sp/views/#get-views-in-a-list","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); // get all the views and their properties const views1 = await list.views(); // you can use odata select operations to get just a set a fields const views2 = await list.views.select(\"Id\", \"Title\")(); // get the top three views const views3 = await list.views.top(3)();","title":"Get views in a list"},{"location":"v2/sp/views/#add-a-view","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); // create a new view with default fields and properties const result = await list.views.add(\"My New View\"); // create a new view with specific properties const result2 = await list.views.add(\"My New View 2\", false, { RowLimit: 10, ViewQuery: \"\", }); // manipulate the view's fields await result2.view.fields.removeAll(); await Promise.all([ result2.view.fields.add(\"Title\"), result2.view.fields.add(\"Modified\"), ]);","title":"Add a View"},{"location":"v2/sp/views/#iview","text":"","title":"IView"},{"location":"v2/sp/views/#get-a-views-information","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); const result = await list.views.getById(\"{GUID view id}\")(); const result2 = await list.views.getByTitle(\"My View\")(); const result3 = await list.views.getByTitle(\"My View\").select(\"Id\", \"Title\")(); const result4 = await list.defaultView(); const result5 = await list.getView(\"{GUID view id}\")();","title":"Get a View's Information"},{"location":"v2/sp/views/#fields","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); const result = await list.views.getById(\"{GUID view id}\").fields();","title":"fields"},{"location":"v2/sp/views/#update","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const list = sp.web.lists.getByTitle(\"My List\"); const result = await list.views.getById(\"{GUID view id}\").update({ RowLimit: 20, });","title":"update"},{"location":"v2/sp/views/#renderashtml","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const result = await sp.web.lists.getByTitle(\"My List\").views.getById(\"{GUID view id}\").renderAsHtml();","title":"renderAsHtml"},{"location":"v2/sp/views/#setviewxml","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const viewXml = \"...\"; await sp.web.lists.getByTitle(\"My List\").views.getById(\"{GUID view id}\").setViewXml(viewXml);","title":"setViewXml"},{"location":"v2/sp/views/#delete","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const viewXml = \"...\"; await sp.web.lists.getByTitle(\"My List\").views.getById(\"{GUID view id}\").delete();","title":"delete"},{"location":"v2/sp/views/#viewfields","text":"","title":"ViewFields"},{"location":"v2/sp/views/#getschemaxml","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; const xml = await sp.web.lists.getByTitle(\"My List\").defaultView.fields.getSchemaXml();","title":"getSchemaXml"},{"location":"v2/sp/views/#add","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.add(\"Created\");","title":"add"},{"location":"v2/sp/views/#move","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.move(\"Created\", 0);","title":"move"},{"location":"v2/sp/views/#remove","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.remove(\"Created\");","title":"remove"},{"location":"v2/sp/views/#removeall","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/lists\"; import \"@pnp/sp/views\"; await sp.web.lists.getByTitle(\"My List\").defaultView.fields.removeAll();","title":"removeAll"},{"location":"v2/sp/webs/","text":"@pnp/sp/webs \u00b6 Webs are one of the fundamental entry points when working with SharePoint. Webs serve as a container for lists, features, sub-webs, and all of the entity types. IWebs \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; Preset: All import { sp, Webs, IWebs } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Webs, IWebs } from \"@pnp/sp/presets/core\"; Add Web \u00b6 Using the library you can add a web to another web's collection of subwebs. The simplest usage requires only a title and url. This will result in a team site with all of the default settings. You can also provide other settings such as description, template, language, and inherit permissions. import { sp } from \"@pnp/sp\"; import { IWebAddResult } from \"@pnp/sp/webs\"; const result = await sp.web.webs.add(\"title\", \"subweb1\"); // show the response from the server when adding the web console.log(result.data); // we can immediately operate on the new web result.web.select(\"Title\")().then((w: IWebAddResult) => { // show our title console.log(w.Title); }); import { sp } from \"@pnp/sp\"; import { IWebAddResult } from \"@pnp/sp/webs\"; // create a German language wiki site with title, url, description, which does not inherit permissions sp.web.webs.add(\"wiki\", \"subweb2\", \"a wiki web\", \"WIKI#0\", 1031, false).then((w: IWebAddResult) => { // ... }); IWeb \u00b6 Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Web, IWeb } from \"@pnp/sp/webs\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; Preset: All import { sp, Web, IWeb } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Web, IWeb } from \"@pnp/sp/presets/core\"; Access a Web \u00b6 There are several ways to access a web instance, each of these methods is equivalent in that you will have an IWeb instance to work with. All of the examples below use a variable named \"web\" which represents an IWeb instance - regardless of how it was initially accessed. Access the web from the imported \"sp\" object using selective import: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; const r = await sp.web(); Access the web from the imported \"sp\" using the 'all' preset import { sp } from \"@pnp/sp/presets/all\"; const r = await sp.web(); Access the web from the imported \"sp\" using the 'core' preset import { sp } from \"@pnp/sp/presets/core\"; const r = await sp.web(); Create a web instance using the factory function import { Web } from \"@pnp/sp/webs\"; const web = Web(\"https://something.sharepoint.com/sites/dev\"); const r = await web(); webs \u00b6 Access the child webs collection of this web const webs = web.webs(); Get A Web's properties \u00b6 // basic get of the webs properties const props = await web(); // use odata operators to get specific fields const props2 = await web.select(\"Title\")(); // type the result to match what you are requesting const props3 = await web.select(\"Title\")<{ Title: string }>(); getParentWeb \u00b6 Get the data and IWeb instance for the parent web for the given web instance import { IOpenWebByIdResult } from \"@pnp/sp/sites\"; const web: IOpenWebByIdResult = web.getParentWeb(); getSubwebsFilteredForCurrentUser \u00b6 Returns a collection of objects that contain metadata about subsites of the current site in which the current user is a member. const subWebs = await web.getSubwebsFilteredForCurrentUser()(); // apply odata operations to the collection const subWebs2 = await sp.web.getSubwebsFilteredForCurrentUser().select(\"Title\", \"Language\").orderBy(\"Created\", true)(); Note: getSubwebsFilteredForCurrentUser returns IWebInfosData which is a subset of all the available fields on IWebInfo. allProperties \u00b6 Allows access to the web's all properties collection. This is readonly in REST. const props = await web.allProperties(); // select certain props const props2 = await web.allProperties.select(\"prop1\", \"prop2\")(); webinfos \u00b6 Gets a collection of WebInfos for this web's subwebs const infos = await web.webinfos(); // or select certain fields const infos2 = await web.webinfos.select(\"Title\", \"Description\")(); // or filter const infos3 = await web.webinfos.filter(\"Title eq 'MyWebTitle'\")(); // or both const infos4 = await web.webinfos.select(\"Title\", \"Description\").filter(\"Title eq 'MyWebTitle'\")(); // get the top 4 ordered by Title const infos5 = await web.webinfos.top(4).orderBy(\"Title\")(); Note: webinfos returns IWebInfosData which is a subset of all the available fields on IWebInfo. update \u00b6 Updates this web instance with the supplied properties // update the web's title and description const result = await web.update({ Title: \"New Title\", Description: \"My new description\", }); // a project implementation could wrap the update to provide type information for your expected fields: import { IWebUpdateResult } from \"@pnp/sp/webs\"; interface IWebUpdateProps { Title: string; Description: string; } function updateWeb(props: IWebUpdateProps): Promise { web.update(props); } Delete a Web \u00b6 await web.delete(); applyTheme \u00b6 Applies the theme specified by the contents of each of the files specified in the arguments to the site import { combine } from \"@pnp/core\"; // we are going to apply the theme to this sub web as an example const web = Web(\"https://{tenant}.sharepoint.com/sites/dev/subweb\"); // the urls to the color and font need to both be from the catalog at the root // these urls can be constants or calculated from existing urls const colorUrl = combine(\"/\", \"sites/dev\", \"_catalogs/theme/15/palette011.spcolor\"); // this gives us the same result const fontUrl = \"/sites/dev/_catalogs/theme/15/fontscheme007.spfont\"; // apply the font and color, no background image, and don't share this theme await web.applyTheme(colorUrl, fontUrl, \"\", false); applyWebTemplate & availableWebTemplates \u00b6 Applies the specified site definition or site template to the Web site that has no template applied to it. This is seldom used outside provisioning scenarios. const templates = (await web.availableWebTemplates().select(\"Name\")<{ Name: string }[]>()).filter(t => /ENTERWIKI#0/i.test(t.Name)); // apply the wiki template const template = templates.length > 0 ? templates[0].Name : \"STS#0\"; await web.applyWebTemplate(template); getChanges \u00b6 Returns the collection of changes from the change log that have occurred within the web, based on the specified query. // get the web changes including add, update, and delete const changes = await web.getChanges({ Add: true, ChangeTokenEnd: null, ChangeTokenStart: null, DeleteObject: true, Update: true, Web: true, }); mapToIcon \u00b6 Returns the name of the image file for the icon that is used to represent the specified file import { combine } from \"@pnp/core\"; const iconFileName = await web.mapToIcon(\"test.docx\"); // iconPath === \"icdocx.png\" // which you can need to map to a real url const iconFullPath = `https://{tenant}.sharepoint.com/sites/dev/_layouts/images/${iconFileName}`; // OR dynamically const webData = await sp.web.select(\"Url\")(); const iconFullPath2 = combine(webData.Url, \"_layouts\", \"images\", iconFileName); // OR within SPFx using the context const iconFullPath3 = combine(this.context.pageContext.web.absoluteUrl, \"_layouts\", \"images\", iconFileName); // You can also set size // 16x16 pixels = 0, 32x32 pixels = 1 const icon32FileName = await web.mapToIcon(\"test.docx\", 1); storage entities \u00b6 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/appcatalog\"; import { IStorageEntity } from \"@pnp/sp/webs\"; // needs to be unique, GUIDs are great const key = \"my-storage-key\"; // read an existing entity const entity: IStorageEntity = await web.getStorageEntity(key); // setStorageEntity and removeStorageEntity must be called in the context of the tenant app catalog site // you can get the tenant app catalog using the getTenantAppCatalogWeb const tenantAppCatalogWeb = await sp.getTenantAppCatalogWeb(); tenantAppCatalogWeb.setStorageEntity(key, \"new value\"); // set other properties tenantAppCatalogWeb.setStorageEntity(key, \"another value\", \"description\", \"comments\"); const entity2: IStorageEntity = await web.getStorageEntity(key); /* entity2 === { Value: \"another value\", Comment: \"comments\"; Description: \"description\", }; */ // you can also remove a storage entity await tenantAppCatalogWeb.removeStorageEntity(key); appcatalog imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/appcatalog\"; Selective 2 import \"@pnp/sp/appcatalog/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; getAppCatalog \u00b6 Returns this web as an IAppCatalog instance or creates a new IAppCatalog instance from the provided url. import { IApp } from \"@pnp/sp/appcatalog\"; const appWeb = web.getAppCatalog(); // appWeb url === web url const app: IApp = appWeb.getAppById(\"{your app id}\"); const appWeb2 = web.getAppCatalog(\"https://tenant.sharepoing.com/sites/someappcatalog\"); // appWeb2 url === \"https://tenant.sharepoing.com/sites/someappcatalog\" client-side-pages imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/client-side-pages\"; Selective 2 import \"@pnp/sp/client-side-pages/web\"; Preset: All import { sp, Web, IWeb } from \"@pnp/sp/presets/all\"; You can create and load clientside page instances directly from a web. More details on working with clientside pages are available in the dedicated article. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; // simplest add a page example const page = await sp.web.addClientsidePage(\"mypage1\"); // simplest load a page example const page = await sp.web.loadClientsidePage(\"/sites/dev/sitepages/mypage3.aspx\"); content-type imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/content-types\"; Selective 2 import \"@pnp/sp/content-types/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; contentTypes \u00b6 Allows access to the collection of content types in this web. const cts = await web.contentTypes(); // you can also select fields and use other odata operators const cts2 = await web.contentTypes.select(\"Name\")(); features imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/features\"; Selective 2 import \"@pnp/sp/features/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; features \u00b6 Allows access to the collection of content types in this web. const features = await web.features(); fields imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/fields\"; Selective 2 import \"@pnp/sp/fields/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; fields \u00b6 Allows access to the collection of fields in this web. const fields = await web.fields(); files imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/files\"; Selective 2 import \"@pnp/sp/files/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; getFileByServerRelativeUrl \u00b6 Gets a file by server relative url import { IFile } from \"@pnp/sp/files\"; const file: IFile = web.getFileByServerRelativeUrl(\"/sites/dev/library/myfile.docx\"); getFileByServerRelativePath \u00b6 Gets a file by server relative url if your file name contains # and % characters import { IFile } from \"@pnp/sp/files\"; const file: IFile = web.getFileByServerRelativePath(\"/sites/dev/library/my # file%.docx\"); folders imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/folders\"; Selective 2 import \"@pnp/sp/folders/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; folders \u00b6 Gets the collection of folders in this web const folders = await web.folders(); // you can also filter and select as with any collection const folders2 = await web.folders.select(\"ServerRelativeUrl\", \"TimeLastModified\").filter(\"ItemCount gt 0\")(); // or get the most recently modified folder const folders2 = await web.folders.orderBy(\"TimeLastModified\").top(1)(); rootFolder \u00b6 Gets the root folder of the web const folder = await web.rootFolder(); getFolderByServerRelativeUrl \u00b6 Gets a folder by server relative url import { IFolder } from \"@pnp/sp/folders\"; const folder: IFolder = web.getFolderByServerRelativeUrl(\"/sites/dev/library\"); getFolderByServerRelativePath \u00b6 Gets a folder by server relative url if your folder name contains # and % characters import { IFolder } from \"@pnp/sp/folders\"; const folder: IFolder = web.getFolderByServerRelativePath(\"/sites/dev/library/my # folder%/\"); hubsites imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/hubsites\"; Selective 2 import \"@pnp/sp/hubsites/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; hubSiteData \u00b6 Gets hub site data for the current web import { IHubSiteWebData } from \"@pnp/sp/hubsites\"; // get the data and force a refresh const data: IHubSiteWebData = await web.hubSiteData(true); syncHubSiteTheme \u00b6 Applies theme updates from the parent hub site collection await web.syncHubSiteTheme(); lists imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/lists\"; Selective 2 import \"@pnp/sp/lists/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; Preset: Core import { sp } from \"@pnp/sp/presets/core\"; lists \u00b6 Gets the collection of all lists that are contained in the Web site import { ILists } from \"@pnp/sp/lists\"; const lists: ILists = web.lists; // you can always order the lists and select properties const data = await lists.select(\"Title\").orderBy(\"Title\")(); // and use other odata operators as well const data2 = await web.lists.top(3).orderBy(\"LastItemModifiedDate\")(); siteUserInfoList \u00b6 Gets the UserInfo list of the site collection that contains the Web site import { IList } from \"@pnp/sp/lists\"; const list: IList = web.siteUserInfoList; const data = await list(); // or chain off that list to get additional details const items = await list.items.top(2)(); defaultDocumentLibrary \u00b6 Get a reference the default documents library of a web import { IList } from \"@pnp/sp/lists\"; const list: IList = web.defaultDocumentLibrary; customListTemplates \u00b6 Gets the collection of all list definitions and list templates that are available import { IList } from \"@pnp/sp/lists\"; const templates = await web.customListTemplates(); // odata operators chain off the collection as expected const templates2 = await web.customListTemplates.select(\"Title\")(); getList \u00b6 Gets a list by server relative url (list's root folder) import { IList } from \"@pnp/sp/lists\"; const list: IList = web.getList(\"/sites/dev/lists/test\"); const listData = list(); getCatalog \u00b6 Returns the list gallery on the site Name Value WebTemplateCatalog 111 WebPartCatalog 113 ListTemplateCatalog 114 MasterPageCatalog 116 SolutionCatalog 121 ThemeCatalog 123 DesignCatalog 124 AppDataCatalog 125 import { IList } from \"@pnp/sp/lists\"; const templateCatalog: IList = await web.getCatalog(111); const themeCatalog: IList = await web.getCatalog(123); navigation imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/navigation\"; Selective 2 import \"@pnp/sp/navigation/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; navigation \u00b6 Gets a navigation object that represents navigation on the Web site, including the Quick Launch area and the top navigation bar import { INavigation } from \"@pnp/sp/navigation\"; const nav: INavigation = web.navigation; const navData = await nav(); regional-settings imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/regional-settings\"; Selective 2 import \"@pnp/sp/regional-settings/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; import { IRegionalSettings } from \"@pnp/sp/navigation\"; const settings: IRegionalSettings = web.regionalSettings; const settingsData = await settings(); related-items imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/related-items\"; Selective 2 import \"@pnp/sp/related-items/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; import { IRelatedItemManager, IRelatedItem } from \"@pnp/sp/related-items\"; const manager: IRelatedItemManager = web.relatedItems; const data: IRelatedItem[] = await manager.getRelatedItems(\"{list name}\", 4); security imports \u00b6 Please see information around the available security methods in the security article . sharing imports \u00b6 Please see information around the available sharing methods in the sharing article . site-groups imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/site-groups\"; Selective 2 import \"@pnp/sp/site-groups/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; siteGroups \u00b6 The site groups const groups = await web.siteGroups(); const groups2 = await web.siteGroups.top(2)(); associatedOwnerGroup \u00b6 The web's owner group const group = await web.associatedOwnerGroup(); const users = await web.associatedOwnerGroup.users(); associatedMemberGroup \u00b6 The web's member group const group = await web.associatedMemberGroup(); const users = await web.associatedMemberGroup.users(); associatedVisitorGroup \u00b6 The web's visitor group const group = await web.associatedVisitorGroup(); const users = await web.associatedVisitorGroup.users(); createDefaultAssociatedGroups \u00b6 Creates the default associated groups (Members, Owners, Visitors) and gives them the default permissions on the site. The target site must have unique permissions and no associated members / owners / visitors groups await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\"); // copy the role assignments await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\", true); // don't clear sub assignments await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\", false, false); // specify secondary owner, don't copy permissions, clear sub scopes await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\", false, true, \"{second owner login}\"); site-users imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/site-users\"; Selective 2 import \"@pnp/sp/site-users/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; siteUsers \u00b6 The site users const users = await web.siteUsers(); const users2 = await web.siteUsers.top(5)(); const users3 = await web.siteUsers.filter(`startswith(LoginName, '${encodeURIComponent(\"i:0#.f|m\")}')`)(); currentUser \u00b6 Information on the current user const user = await web.currentUser(); // check the login name of the current user const user2 = await web.currentUser.select(\"LoginName\")(); ensureUser \u00b6 Checks whether the specified login name belongs to a valid user in the web. If the user doesn't exist, adds the user to the web import { IWebEnsureUserResult } from \"@pnp/sp/site-users/\"; const result: IWebEnsureUserResult = await web.ensureUser(\"i:0#.f|membership|user@domain.onmicrosoft.com\"); getUserById \u00b6 Returns the user corresponding to the specified member identifier for the current web import { ISiteUser } from \"@pnp/sp/site-users/\"; const user: ISiteUser = web.getUserById(23); const userData = await user(); const userData2 = await user.select(\"LoginName\")(); user-custom-actions imports \u00b6 Scenario Import Statement Selective 1 import \"@pnp/sp/user-custom-actions\"; Selective 2 import \"@pnp/sp/user-custom-actions/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; userCustomActions \u00b6 Gets a newly refreshed collection of the SPWeb's SPUserCustomActionCollection import { IUserCustomActions } from \"@pnp/sp/user-custom-actions\"; const actions: IUserCustomActions = web.userCustomActions; const actionsData = await actions(); IWebInfosData \u00b6 Some web operations return a subset of web information defined by the IWebInfosData interface, shown below. In those cases only these fields are available for select, orderby, and other odata operations. interface IWebInfosData { Configuration: number; Created: string; Description: string; Id: string; Language: number; LastItemModifiedDate: string; LastItemUserModifiedDate: string; ServerRelativeUrl: string; Title: string; WebTemplate: string; WebTemplateId: number; }","title":"@pnp/sp/webs"},{"location":"v2/sp/webs/#pnpspwebs","text":"Webs are one of the fundamental entry points when working with SharePoint. Webs serve as a container for lists, features, sub-webs, and all of the entity types.","title":"@pnp/sp/webs"},{"location":"v2/sp/webs/#iwebs","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Webs, IWebs } from \"@pnp/sp/webs\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; Preset: All import { sp, Webs, IWebs } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Webs, IWebs } from \"@pnp/sp/presets/core\";","title":"IWebs"},{"location":"v2/sp/webs/#add-web","text":"Using the library you can add a web to another web's collection of subwebs. The simplest usage requires only a title and url. This will result in a team site with all of the default settings. You can also provide other settings such as description, template, language, and inherit permissions. import { sp } from \"@pnp/sp\"; import { IWebAddResult } from \"@pnp/sp/webs\"; const result = await sp.web.webs.add(\"title\", \"subweb1\"); // show the response from the server when adding the web console.log(result.data); // we can immediately operate on the new web result.web.select(\"Title\")().then((w: IWebAddResult) => { // show our title console.log(w.Title); }); import { sp } from \"@pnp/sp\"; import { IWebAddResult } from \"@pnp/sp/webs\"; // create a German language wiki site with title, url, description, which does not inherit permissions sp.web.webs.add(\"wiki\", \"subweb2\", \"a wiki web\", \"WIKI#0\", 1031, false).then((w: IWebAddResult) => { // ... });","title":"Add Web"},{"location":"v2/sp/webs/#iweb","text":"Scenario Import Statement Selective 1 import { sp } from \"@pnp/sp\"; import { Web, IWeb } from \"@pnp/sp/webs\"; Selective 2 import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; Preset: All import { sp, Web, IWeb } from \"@pnp/sp/presets/all\"; Preset: Core import { sp, Web, IWeb } from \"@pnp/sp/presets/core\";","title":"IWeb"},{"location":"v2/sp/webs/#access-a-web","text":"There are several ways to access a web instance, each of these methods is equivalent in that you will have an IWeb instance to work with. All of the examples below use a variable named \"web\" which represents an IWeb instance - regardless of how it was initially accessed. Access the web from the imported \"sp\" object using selective import: import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; const r = await sp.web(); Access the web from the imported \"sp\" using the 'all' preset import { sp } from \"@pnp/sp/presets/all\"; const r = await sp.web(); Access the web from the imported \"sp\" using the 'core' preset import { sp } from \"@pnp/sp/presets/core\"; const r = await sp.web(); Create a web instance using the factory function import { Web } from \"@pnp/sp/webs\"; const web = Web(\"https://something.sharepoint.com/sites/dev\"); const r = await web();","title":"Access a Web"},{"location":"v2/sp/webs/#webs","text":"Access the child webs collection of this web const webs = web.webs();","title":"webs"},{"location":"v2/sp/webs/#get-a-webs-properties","text":"// basic get of the webs properties const props = await web(); // use odata operators to get specific fields const props2 = await web.select(\"Title\")(); // type the result to match what you are requesting const props3 = await web.select(\"Title\")<{ Title: string }>();","title":"Get A Web's properties"},{"location":"v2/sp/webs/#getparentweb","text":"Get the data and IWeb instance for the parent web for the given web instance import { IOpenWebByIdResult } from \"@pnp/sp/sites\"; const web: IOpenWebByIdResult = web.getParentWeb();","title":"getParentWeb"},{"location":"v2/sp/webs/#getsubwebsfilteredforcurrentuser","text":"Returns a collection of objects that contain metadata about subsites of the current site in which the current user is a member. const subWebs = await web.getSubwebsFilteredForCurrentUser()(); // apply odata operations to the collection const subWebs2 = await sp.web.getSubwebsFilteredForCurrentUser().select(\"Title\", \"Language\").orderBy(\"Created\", true)(); Note: getSubwebsFilteredForCurrentUser returns IWebInfosData which is a subset of all the available fields on IWebInfo.","title":"getSubwebsFilteredForCurrentUser"},{"location":"v2/sp/webs/#allproperties","text":"Allows access to the web's all properties collection. This is readonly in REST. const props = await web.allProperties(); // select certain props const props2 = await web.allProperties.select(\"prop1\", \"prop2\")();","title":"allProperties"},{"location":"v2/sp/webs/#webinfos","text":"Gets a collection of WebInfos for this web's subwebs const infos = await web.webinfos(); // or select certain fields const infos2 = await web.webinfos.select(\"Title\", \"Description\")(); // or filter const infos3 = await web.webinfos.filter(\"Title eq 'MyWebTitle'\")(); // or both const infos4 = await web.webinfos.select(\"Title\", \"Description\").filter(\"Title eq 'MyWebTitle'\")(); // get the top 4 ordered by Title const infos5 = await web.webinfos.top(4).orderBy(\"Title\")(); Note: webinfos returns IWebInfosData which is a subset of all the available fields on IWebInfo.","title":"webinfos"},{"location":"v2/sp/webs/#update","text":"Updates this web instance with the supplied properties // update the web's title and description const result = await web.update({ Title: \"New Title\", Description: \"My new description\", }); // a project implementation could wrap the update to provide type information for your expected fields: import { IWebUpdateResult } from \"@pnp/sp/webs\"; interface IWebUpdateProps { Title: string; Description: string; } function updateWeb(props: IWebUpdateProps): Promise { web.update(props); }","title":"update"},{"location":"v2/sp/webs/#delete-a-web","text":"await web.delete();","title":"Delete a Web"},{"location":"v2/sp/webs/#applytheme","text":"Applies the theme specified by the contents of each of the files specified in the arguments to the site import { combine } from \"@pnp/core\"; // we are going to apply the theme to this sub web as an example const web = Web(\"https://{tenant}.sharepoint.com/sites/dev/subweb\"); // the urls to the color and font need to both be from the catalog at the root // these urls can be constants or calculated from existing urls const colorUrl = combine(\"/\", \"sites/dev\", \"_catalogs/theme/15/palette011.spcolor\"); // this gives us the same result const fontUrl = \"/sites/dev/_catalogs/theme/15/fontscheme007.spfont\"; // apply the font and color, no background image, and don't share this theme await web.applyTheme(colorUrl, fontUrl, \"\", false);","title":"applyTheme"},{"location":"v2/sp/webs/#applywebtemplate-availablewebtemplates","text":"Applies the specified site definition or site template to the Web site that has no template applied to it. This is seldom used outside provisioning scenarios. const templates = (await web.availableWebTemplates().select(\"Name\")<{ Name: string }[]>()).filter(t => /ENTERWIKI#0/i.test(t.Name)); // apply the wiki template const template = templates.length > 0 ? templates[0].Name : \"STS#0\"; await web.applyWebTemplate(template);","title":"applyWebTemplate & availableWebTemplates"},{"location":"v2/sp/webs/#getchanges","text":"Returns the collection of changes from the change log that have occurred within the web, based on the specified query. // get the web changes including add, update, and delete const changes = await web.getChanges({ Add: true, ChangeTokenEnd: null, ChangeTokenStart: null, DeleteObject: true, Update: true, Web: true, });","title":"getChanges"},{"location":"v2/sp/webs/#maptoicon","text":"Returns the name of the image file for the icon that is used to represent the specified file import { combine } from \"@pnp/core\"; const iconFileName = await web.mapToIcon(\"test.docx\"); // iconPath === \"icdocx.png\" // which you can need to map to a real url const iconFullPath = `https://{tenant}.sharepoint.com/sites/dev/_layouts/images/${iconFileName}`; // OR dynamically const webData = await sp.web.select(\"Url\")(); const iconFullPath2 = combine(webData.Url, \"_layouts\", \"images\", iconFileName); // OR within SPFx using the context const iconFullPath3 = combine(this.context.pageContext.web.absoluteUrl, \"_layouts\", \"images\", iconFileName); // You can also set size // 16x16 pixels = 0, 32x32 pixels = 1 const icon32FileName = await web.mapToIcon(\"test.docx\", 1);","title":"mapToIcon"},{"location":"v2/sp/webs/#storage-entities","text":"import { sp } from \"@pnp/sp\"; import \"@pnp/sp/appcatalog\"; import { IStorageEntity } from \"@pnp/sp/webs\"; // needs to be unique, GUIDs are great const key = \"my-storage-key\"; // read an existing entity const entity: IStorageEntity = await web.getStorageEntity(key); // setStorageEntity and removeStorageEntity must be called in the context of the tenant app catalog site // you can get the tenant app catalog using the getTenantAppCatalogWeb const tenantAppCatalogWeb = await sp.getTenantAppCatalogWeb(); tenantAppCatalogWeb.setStorageEntity(key, \"new value\"); // set other properties tenantAppCatalogWeb.setStorageEntity(key, \"another value\", \"description\", \"comments\"); const entity2: IStorageEntity = await web.getStorageEntity(key); /* entity2 === { Value: \"another value\", Comment: \"comments\"; Description: \"description\", }; */ // you can also remove a storage entity await tenantAppCatalogWeb.removeStorageEntity(key);","title":"storage entities"},{"location":"v2/sp/webs/#appcatalog-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/appcatalog\"; Selective 2 import \"@pnp/sp/appcatalog/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"appcatalog imports"},{"location":"v2/sp/webs/#getappcatalog","text":"Returns this web as an IAppCatalog instance or creates a new IAppCatalog instance from the provided url. import { IApp } from \"@pnp/sp/appcatalog\"; const appWeb = web.getAppCatalog(); // appWeb url === web url const app: IApp = appWeb.getAppById(\"{your app id}\"); const appWeb2 = web.getAppCatalog(\"https://tenant.sharepoing.com/sites/someappcatalog\"); // appWeb2 url === \"https://tenant.sharepoing.com/sites/someappcatalog\"","title":"getAppCatalog"},{"location":"v2/sp/webs/#client-side-pages-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/client-side-pages\"; Selective 2 import \"@pnp/sp/client-side-pages/web\"; Preset: All import { sp, Web, IWeb } from \"@pnp/sp/presets/all\"; You can create and load clientside page instances directly from a web. More details on working with clientside pages are available in the dedicated article. import { sp } from \"@pnp/sp\"; import \"@pnp/sp/webs\"; import \"@pnp/sp/clientside-pages/web\"; // simplest add a page example const page = await sp.web.addClientsidePage(\"mypage1\"); // simplest load a page example const page = await sp.web.loadClientsidePage(\"/sites/dev/sitepages/mypage3.aspx\");","title":"client-side-pages imports"},{"location":"v2/sp/webs/#content-type-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/content-types\"; Selective 2 import \"@pnp/sp/content-types/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"content-type imports"},{"location":"v2/sp/webs/#contenttypes","text":"Allows access to the collection of content types in this web. const cts = await web.contentTypes(); // you can also select fields and use other odata operators const cts2 = await web.contentTypes.select(\"Name\")();","title":"contentTypes"},{"location":"v2/sp/webs/#features-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/features\"; Selective 2 import \"@pnp/sp/features/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"features imports"},{"location":"v2/sp/webs/#features","text":"Allows access to the collection of content types in this web. const features = await web.features();","title":"features"},{"location":"v2/sp/webs/#fields-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/fields\"; Selective 2 import \"@pnp/sp/fields/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"fields imports"},{"location":"v2/sp/webs/#fields","text":"Allows access to the collection of fields in this web. const fields = await web.fields();","title":"fields"},{"location":"v2/sp/webs/#files-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/files\"; Selective 2 import \"@pnp/sp/files/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"files imports"},{"location":"v2/sp/webs/#getfilebyserverrelativeurl","text":"Gets a file by server relative url import { IFile } from \"@pnp/sp/files\"; const file: IFile = web.getFileByServerRelativeUrl(\"/sites/dev/library/myfile.docx\");","title":"getFileByServerRelativeUrl"},{"location":"v2/sp/webs/#getfilebyserverrelativepath","text":"Gets a file by server relative url if your file name contains # and % characters import { IFile } from \"@pnp/sp/files\"; const file: IFile = web.getFileByServerRelativePath(\"/sites/dev/library/my # file%.docx\");","title":"getFileByServerRelativePath"},{"location":"v2/sp/webs/#folders-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/folders\"; Selective 2 import \"@pnp/sp/folders/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"folders imports"},{"location":"v2/sp/webs/#folders","text":"Gets the collection of folders in this web const folders = await web.folders(); // you can also filter and select as with any collection const folders2 = await web.folders.select(\"ServerRelativeUrl\", \"TimeLastModified\").filter(\"ItemCount gt 0\")(); // or get the most recently modified folder const folders2 = await web.folders.orderBy(\"TimeLastModified\").top(1)();","title":"folders"},{"location":"v2/sp/webs/#rootfolder","text":"Gets the root folder of the web const folder = await web.rootFolder();","title":"rootFolder"},{"location":"v2/sp/webs/#getfolderbyserverrelativeurl","text":"Gets a folder by server relative url import { IFolder } from \"@pnp/sp/folders\"; const folder: IFolder = web.getFolderByServerRelativeUrl(\"/sites/dev/library\");","title":"getFolderByServerRelativeUrl"},{"location":"v2/sp/webs/#getfolderbyserverrelativepath","text":"Gets a folder by server relative url if your folder name contains # and % characters import { IFolder } from \"@pnp/sp/folders\"; const folder: IFolder = web.getFolderByServerRelativePath(\"/sites/dev/library/my # folder%/\");","title":"getFolderByServerRelativePath"},{"location":"v2/sp/webs/#hubsites-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/hubsites\"; Selective 2 import \"@pnp/sp/hubsites/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"hubsites imports"},{"location":"v2/sp/webs/#hubsitedata","text":"Gets hub site data for the current web import { IHubSiteWebData } from \"@pnp/sp/hubsites\"; // get the data and force a refresh const data: IHubSiteWebData = await web.hubSiteData(true);","title":"hubSiteData"},{"location":"v2/sp/webs/#synchubsitetheme","text":"Applies theme updates from the parent hub site collection await web.syncHubSiteTheme();","title":"syncHubSiteTheme"},{"location":"v2/sp/webs/#lists-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/lists\"; Selective 2 import \"@pnp/sp/lists/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; Preset: Core import { sp } from \"@pnp/sp/presets/core\";","title":"lists imports"},{"location":"v2/sp/webs/#lists","text":"Gets the collection of all lists that are contained in the Web site import { ILists } from \"@pnp/sp/lists\"; const lists: ILists = web.lists; // you can always order the lists and select properties const data = await lists.select(\"Title\").orderBy(\"Title\")(); // and use other odata operators as well const data2 = await web.lists.top(3).orderBy(\"LastItemModifiedDate\")();","title":"lists"},{"location":"v2/sp/webs/#siteuserinfolist","text":"Gets the UserInfo list of the site collection that contains the Web site import { IList } from \"@pnp/sp/lists\"; const list: IList = web.siteUserInfoList; const data = await list(); // or chain off that list to get additional details const items = await list.items.top(2)();","title":"siteUserInfoList"},{"location":"v2/sp/webs/#defaultdocumentlibrary","text":"Get a reference the default documents library of a web import { IList } from \"@pnp/sp/lists\"; const list: IList = web.defaultDocumentLibrary;","title":"defaultDocumentLibrary"},{"location":"v2/sp/webs/#customlisttemplates","text":"Gets the collection of all list definitions and list templates that are available import { IList } from \"@pnp/sp/lists\"; const templates = await web.customListTemplates(); // odata operators chain off the collection as expected const templates2 = await web.customListTemplates.select(\"Title\")();","title":"customListTemplates"},{"location":"v2/sp/webs/#getlist","text":"Gets a list by server relative url (list's root folder) import { IList } from \"@pnp/sp/lists\"; const list: IList = web.getList(\"/sites/dev/lists/test\"); const listData = list();","title":"getList"},{"location":"v2/sp/webs/#getcatalog","text":"Returns the list gallery on the site Name Value WebTemplateCatalog 111 WebPartCatalog 113 ListTemplateCatalog 114 MasterPageCatalog 116 SolutionCatalog 121 ThemeCatalog 123 DesignCatalog 124 AppDataCatalog 125 import { IList } from \"@pnp/sp/lists\"; const templateCatalog: IList = await web.getCatalog(111); const themeCatalog: IList = await web.getCatalog(123);","title":"getCatalog"},{"location":"v2/sp/webs/#navigation-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/navigation\"; Selective 2 import \"@pnp/sp/navigation/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"navigation imports"},{"location":"v2/sp/webs/#navigation","text":"Gets a navigation object that represents navigation on the Web site, including the Quick Launch area and the top navigation bar import { INavigation } from \"@pnp/sp/navigation\"; const nav: INavigation = web.navigation; const navData = await nav();","title":"navigation"},{"location":"v2/sp/webs/#regional-settings-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/regional-settings\"; Selective 2 import \"@pnp/sp/regional-settings/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; import { IRegionalSettings } from \"@pnp/sp/navigation\"; const settings: IRegionalSettings = web.regionalSettings; const settingsData = await settings();","title":"regional-settings imports"},{"location":"v2/sp/webs/#related-items-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/related-items\"; Selective 2 import \"@pnp/sp/related-items/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\"; import { IRelatedItemManager, IRelatedItem } from \"@pnp/sp/related-items\"; const manager: IRelatedItemManager = web.relatedItems; const data: IRelatedItem[] = await manager.getRelatedItems(\"{list name}\", 4);","title":"related-items imports"},{"location":"v2/sp/webs/#security-imports","text":"Please see information around the available security methods in the security article .","title":"security imports"},{"location":"v2/sp/webs/#sharing-imports","text":"Please see information around the available sharing methods in the sharing article .","title":"sharing imports"},{"location":"v2/sp/webs/#site-groups-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/site-groups\"; Selective 2 import \"@pnp/sp/site-groups/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"site-groups imports"},{"location":"v2/sp/webs/#sitegroups","text":"The site groups const groups = await web.siteGroups(); const groups2 = await web.siteGroups.top(2)();","title":"siteGroups"},{"location":"v2/sp/webs/#associatedownergroup","text":"The web's owner group const group = await web.associatedOwnerGroup(); const users = await web.associatedOwnerGroup.users();","title":"associatedOwnerGroup"},{"location":"v2/sp/webs/#associatedmembergroup","text":"The web's member group const group = await web.associatedMemberGroup(); const users = await web.associatedMemberGroup.users();","title":"associatedMemberGroup"},{"location":"v2/sp/webs/#associatedvisitorgroup","text":"The web's visitor group const group = await web.associatedVisitorGroup(); const users = await web.associatedVisitorGroup.users();","title":"associatedVisitorGroup"},{"location":"v2/sp/webs/#createdefaultassociatedgroups","text":"Creates the default associated groups (Members, Owners, Visitors) and gives them the default permissions on the site. The target site must have unique permissions and no associated members / owners / visitors groups await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\"); // copy the role assignments await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\", true); // don't clear sub assignments await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\", false, false); // specify secondary owner, don't copy permissions, clear sub scopes await web.createDefaultAssociatedGroups(\"Contoso\", \"{first owner login}\", false, true, \"{second owner login}\");","title":"createDefaultAssociatedGroups"},{"location":"v2/sp/webs/#site-users-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/site-users\"; Selective 2 import \"@pnp/sp/site-users/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"site-users imports"},{"location":"v2/sp/webs/#siteusers","text":"The site users const users = await web.siteUsers(); const users2 = await web.siteUsers.top(5)(); const users3 = await web.siteUsers.filter(`startswith(LoginName, '${encodeURIComponent(\"i:0#.f|m\")}')`)();","title":"siteUsers"},{"location":"v2/sp/webs/#currentuser","text":"Information on the current user const user = await web.currentUser(); // check the login name of the current user const user2 = await web.currentUser.select(\"LoginName\")();","title":"currentUser"},{"location":"v2/sp/webs/#ensureuser","text":"Checks whether the specified login name belongs to a valid user in the web. If the user doesn't exist, adds the user to the web import { IWebEnsureUserResult } from \"@pnp/sp/site-users/\"; const result: IWebEnsureUserResult = await web.ensureUser(\"i:0#.f|membership|user@domain.onmicrosoft.com\");","title":"ensureUser"},{"location":"v2/sp/webs/#getuserbyid","text":"Returns the user corresponding to the specified member identifier for the current web import { ISiteUser } from \"@pnp/sp/site-users/\"; const user: ISiteUser = web.getUserById(23); const userData = await user(); const userData2 = await user.select(\"LoginName\")();","title":"getUserById"},{"location":"v2/sp/webs/#user-custom-actions-imports","text":"Scenario Import Statement Selective 1 import \"@pnp/sp/user-custom-actions\"; Selective 2 import \"@pnp/sp/user-custom-actions/web\"; Preset: All import { sp } from \"@pnp/sp/presets/all\";","title":"user-custom-actions imports"},{"location":"v2/sp/webs/#usercustomactions","text":"Gets a newly refreshed collection of the SPWeb's SPUserCustomActionCollection import { IUserCustomActions } from \"@pnp/sp/user-custom-actions\"; const actions: IUserCustomActions = web.userCustomActions; const actionsData = await actions();","title":"userCustomActions"},{"location":"v2/sp/webs/#iwebinfosdata","text":"Some web operations return a subset of web information defined by the IWebInfosData interface, shown below. In those cases only these fields are available for select, orderby, and other odata operations. interface IWebInfosData { Configuration: number; Created: string; Description: string; Id: string; Language: number; LastItemModifiedDate: string; LastItemUserModifiedDate: string; ServerRelativeUrl: string; Title: string; WebTemplate: string; WebTemplateId: number; }","title":"IWebInfosData"},{"location":"v2/sp-addinhelpers/","text":"@pnp/sp-addinhelpers \u00b6 This module contains classes to allow use of the libraries within a SharePoint add-in. Getting Started \u00b6 Install the library and all dependencies, npm install @pnp/sp @pnp/sp-addinhelpers --save Now you can make requests to the host web from your add-in using the crossDomainWeb method. // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp, SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\"; // this only needs to be done once within your application sp.setup({ sp: { fetchClientFactory: () => { return new SPRequestExecutorClient(); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\"; const hostWebUrl = \"{The host web url, likely from the query string}\"; // make requests into the host web via the SP.RequestExecutor const w = await sp.crossDomainWeb(addInWenUrl, hostWebUrl)(); console.log(JSON.stringify(w, null, 4)); Library Topics \u00b6 SPRequestExecutorClient SPRestAddIn","title":"@pnp/sp-addinhelpers"},{"location":"v2/sp-addinhelpers/#pnpsp-addinhelpers","text":"This module contains classes to allow use of the libraries within a SharePoint add-in.","title":"@pnp/sp-addinhelpers"},{"location":"v2/sp-addinhelpers/#getting-started","text":"Install the library and all dependencies, npm install @pnp/sp @pnp/sp-addinhelpers --save Now you can make requests to the host web from your add-in using the crossDomainWeb method. // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp, SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\"; // this only needs to be done once within your application sp.setup({ sp: { fetchClientFactory: () => { return new SPRequestExecutorClient(); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\"; const hostWebUrl = \"{The host web url, likely from the query string}\"; // make requests into the host web via the SP.RequestExecutor const w = await sp.crossDomainWeb(addInWenUrl, hostWebUrl)(); console.log(JSON.stringify(w, null, 4));","title":"Getting Started"},{"location":"v2/sp-addinhelpers/#library-topics","text":"SPRequestExecutorClient SPRestAddIn","title":"Library Topics"},{"location":"v2/sp-addinhelpers/sp-request-executor-client/","text":"@pnp/sp-addinhelpers/sprequestexecutorclient \u00b6 The SPRequestExecutorClient is an implementation of the HttpClientImpl interface that facilitates requests to SharePoint from an add-in. It relies on the SharePoint SP product libraries being present to allow use of the SP.RequestExecutor to make the request. Setup \u00b6 To use the client you need to set it using the fetch client factory using the setup method as shown below. This is only required when working within a SharePoint add-in web. // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp, SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\"; // this only needs to be done once within your application sp.setup({ sp: { fetchClientFactory: () => { return new SPRequestExecutorClient(); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\"; const hostWebUrl = \"{The host web url, likely from the query string}\"; // make requests into the host web via the SP.RequestExecutor sp.crossDomainWeb(addInWenUrl, hostWebUrl)().then(w => { console.log(JSON.stringify(w, null, 4)); });","title":"@pnp/sp-addinhelpers/sprequestexecutorclient"},{"location":"v2/sp-addinhelpers/sp-request-executor-client/#pnpsp-addinhelperssprequestexecutorclient","text":"The SPRequestExecutorClient is an implementation of the HttpClientImpl interface that facilitates requests to SharePoint from an add-in. It relies on the SharePoint SP product libraries being present to allow use of the SP.RequestExecutor to make the request.","title":"@pnp/sp-addinhelpers/sprequestexecutorclient"},{"location":"v2/sp-addinhelpers/sp-request-executor-client/#setup","text":"To use the client you need to set it using the fetch client factory using the setup method as shown below. This is only required when working within a SharePoint add-in web. // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp, SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\"; // this only needs to be done once within your application sp.setup({ sp: { fetchClientFactory: () => { return new SPRequestExecutorClient(); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\"; const hostWebUrl = \"{The host web url, likely from the query string}\"; // make requests into the host web via the SP.RequestExecutor sp.crossDomainWeb(addInWenUrl, hostWebUrl)().then(w => { console.log(JSON.stringify(w, null, 4)); });","title":"Setup"},{"location":"v2/sp-addinhelpers/sp-rest-addin/","text":"@pnp/sp-addinhelpers/sprestaddin \u00b6 This class extends the sp export from @pnp/sp and adds in the methods required to make cross domain calls // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp, SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\"; // this only needs to be done once within your application sp.setup({ sp: { fetchClientFactory: () => { return new SPRequestExecutorClient(); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\"; const hostWebUrl = \"{The host web url, likely from the query string}\"; // make requests into the host web via the SP.RequestExecutor const w = await sp.crossDomainWeb(addInWenUrl, hostWebUrl)(); console.log(JSON.stringify(w, null, 4));","title":"@pnp/sp-addinhelpers/sprestaddin"},{"location":"v2/sp-addinhelpers/sp-rest-addin/#pnpsp-addinhelperssprestaddin","text":"This class extends the sp export from @pnp/sp and adds in the methods required to make cross domain calls // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods import { sp, SPRequestExecutorClient } from \"@pnp/sp-addinhelpers\"; // this only needs to be done once within your application sp.setup({ sp: { fetchClientFactory: () => { return new SPRequestExecutorClient(); } } }); // now we need to use the crossDomainWeb method to make our requests to the host web const addInWenUrl = \"{The add-in web url, likely from the query string}\"; const hostWebUrl = \"{The host web url, likely from the query string}\"; // make requests into the host web via the SP.RequestExecutor const w = await sp.crossDomainWeb(addInWenUrl, hostWebUrl)(); console.log(JSON.stringify(w, null, 4));","title":"@pnp/sp-addinhelpers/sprestaddin"}]} \ No newline at end of file diff --git a/docs/v3/v2/sitemap.xml b/docs/v3/v2/sitemap.xml deleted file mode 100644 index d5c63d87f..000000000 --- a/docs/v3/v2/sitemap.xml +++ /dev/null @@ -1,483 +0,0 @@ - - - https://pnp.github.io/pnpjs/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/getting-started/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/news/2020-year-in-review/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/getting-started/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/transition-guide/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/npm-scripts/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/SPFx-on-premises/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/nodejs-support/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/concepts/configuration/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/concepts/selective-imports/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/concepts/custom-bundle/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/concepts/ie11-mode/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/concepts/invokable/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/concepts/polyfill/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/concepts/settings/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/concepts/error-handling/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/authentication/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/authentication/client-spfx/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/authentication/msaljsclient/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/authentication/adaljsclient/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/authentication/client-spa/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/authentication/server-nodejs/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/authentication/sp-app-registration/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/authentication/bearertokenclient/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/authentication/lambdaclient/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/packages/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/common/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/common/collections/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/common/custom-httpclientimpl/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/common/libconfig/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/common/netutil/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/common/storage/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/common/util/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/config-store/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/config-store/configuration/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/config-store/providers/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/groups/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/insights/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/contacts/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/calendars/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/directoryobjects/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/invitations/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/onedrive/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/outlook/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/photos/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/planner/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/search/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/subscriptions/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/teams/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/graph/users/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/logging/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/nodejs/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/nodejs/sp-fetch-client/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/nodejs/adal-fetch-client/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/nodejs/bearer-token-fetch-client/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/nodejs/provider-hosted-app/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/nodejs/sp-extensions/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/nodejs/proxy/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/odata/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/odata/caching/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/odata/core/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/odata/odata-batch/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/odata/extensions/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/odata/debug/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/odata/parsers/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/odata/pipeline/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/odata/queryable/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/pnpjs/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/alias-parameters/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/alm/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/attachments/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/clientside-pages/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/column-defaults/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/comments-likes/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/content-types/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/entity-merging/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/features/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/fields/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/files/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/folders/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/forms/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/hubsites/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/items/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/lists/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/navigation/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/permissions/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/profiles/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/regional-settings/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/related-items/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/search/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/security/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/sharing/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/site-designs/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/site-groups/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/site-scripts/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/site-users/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/sites/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/social/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/sp-utilities-utility/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/subscriptions/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/taxonomy/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/tenant-properties/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/user-custom-actions/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/views/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/webs/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp/custom-irequestclient/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp-addinhelpers/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp-addinhelpers/sp-request-executor-client/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/sp-addinhelpers/sp-rest-addin/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/contributing/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/contributing/setup-dev-machine/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/contributing/local-debug-configuration/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/contributing/debugging/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/contributing/extending-the-library/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/contributing/debug-tests/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/contributing/documentation/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/contributing/pull-requests/ - 2021-10-01 - daily - - https://pnp.github.io/pnpjs/v1/ - 2021-10-01 - daily - - \ No newline at end of file diff --git a/docs/v3/v2/sitemap.xml.gz b/docs/v3/v2/sitemap.xml.gz deleted file mode 100644 index 2fefcbc45ed0688a940d1aed9fe16064f1fef839..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1134 zcmV-!1d;n6iwFpbCRbqs|8r?{Wo=<_E_iKh0PUR1aw9hkMfd&+m-f_SR!K_M>}H*+ z%zOa533bEfYe-O!zYo|g$1|zQB8ybrMZB=pZMJj_faC>1`uO=4{PTrM&s>q$-nJ(*Xwm#St!_Cx2a6KI?j4gcaNL9 z{r1<*sXu#qdhTCGpT<*vxa!k6=Iz8Xw_(evU%l4dao1bCH?~zma;H!GPX}|@oBct5 z{kZpNGR`(mbT0J$;@uQ1mto&I0jeJHFY8P-d|Z zU_|bMGA6%h+!UcK^pLi_7X0dq&BAH5R8ObypD^If^A!#^?9kGzCr|2wHP&+Ov5K_6P;gzt= z#qq*oam33?l_I&q7eYd6;!}D9BFb};gygmCZH!cah^pp$BhsW&+ptzzz&f0e4M;q# zqR0}TrU_-06lneDfl{+MlZ>+gTCRAY^%X~0xOE}QEGtp4knj0{nNt;VeT5G!_UI-Q zE6iB>VYk~I0w_5v7KmiDq1mZJ3r zY9HJ@ZFxAH^oFNIWn5)A!O;j*-FQS0;5x_h)>Dm#> zhy4gI49t}VoE4hYU99iYs0vWYwjaQ7j^j)|Xd!By7f zCd%$zDLgbm5u!fz02W0!+2VL= diff --git a/docs/v3/v2/sp-addinhelpers/index.html b/docs/v3/v2/sp-addinhelpers/index.html deleted file mode 100644 index c71159f87..000000000 --- a/docs/v3/v2/sp-addinhelpers/index.html +++ /dev/null @@ -1,2274 +0,0 @@ - - - - - - - - - - - - - - - - - - sp-addinhelpers - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp-addinhelpers

      -

      npm version

      -

      This module contains classes to allow use of the libraries within a SharePoint add-in.

      -

      Getting Started

      -

      Install the library and all dependencies,

      -

      npm install @pnp/sp @pnp/sp-addinhelpers --save

      -

      Now you can make requests to the host web from your add-in using the crossDomainWeb method.

      -
      // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods
      -import { sp, SPRequestExecutorClient } from "@pnp/sp-addinhelpers";
      -
      -// this only needs to be done once within your application
      -sp.setup({
      -    sp: {
      -        fetchClientFactory: () => {
      -            return new SPRequestExecutorClient();
      -        }
      -    }
      -});
      -
      -// now we need to use the crossDomainWeb method to make our requests to the host web
      -const addInWenUrl = "{The add-in web url, likely from the query string}";
      -const hostWebUrl = "{The host web url, likely from the query string}";
      -
      -// make requests into the host web via the SP.RequestExecutor
      -const w = await sp.crossDomainWeb(addInWenUrl, hostWebUrl)();
      -console.log(JSON.stringify(w, null, 4));
      -
      -

      Library Topics

      - - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp-addinhelpers/sp-request-executor-client/index.html b/docs/v3/v2/sp-addinhelpers/sp-request-executor-client/index.html deleted file mode 100644 index d3ae46a54..000000000 --- a/docs/v3/v2/sp-addinhelpers/sp-request-executor-client/index.html +++ /dev/null @@ -1,2253 +0,0 @@ - - - - - - - - - - - - - - - - - - SPRequestExecutorClient - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp-addinhelpers/sprequestexecutorclient

      -

      The SPRequestExecutorClient is an implementation of the HttpClientImpl interface that facilitates requests to SharePoint from an add-in. It relies on the SharePoint SP product libraries being present to allow use of the SP.RequestExecutor to make the request.

      -

      Setup

      -

      To use the client you need to set it using the fetch client factory using the setup method as shown below. This is only required when working within a SharePoint add-in web.

      -
      // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods
      -import { sp, SPRequestExecutorClient } from "@pnp/sp-addinhelpers";
      -
      -// this only needs to be done once within your application
      -sp.setup({
      -    sp: {
      -        fetchClientFactory: () => {
      -            return new SPRequestExecutorClient();
      -        }
      -    }
      -});
      -
      -// now we need to use the crossDomainWeb method to make our requests to the host web
      -const addInWenUrl = "{The add-in web url, likely from the query string}";
      -const hostWebUrl = "{The host web url, likely from the query string}";
      -
      -// make requests into the host web via the SP.RequestExecutor
      -sp.crossDomainWeb(addInWenUrl, hostWebUrl)().then(w => {
      -    console.log(JSON.stringify(w, null, 4));
      -});
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp-addinhelpers/sp-rest-addin/index.html b/docs/v3/v2/sp-addinhelpers/sp-rest-addin/index.html deleted file mode 100644 index 5404434d6..000000000 --- a/docs/v3/v2/sp-addinhelpers/sp-rest-addin/index.html +++ /dev/null @@ -1,2206 +0,0 @@ - - - - - - - - - - - - - - - - - - SPRestAddIn - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp-addinhelpers/sprestaddin

      -

      This class extends the sp export from @pnp/sp and adds in the methods required to make cross domain calls

      -
      // note we are getting the sp variable from this library, it extends the sp export from @pnp/sp to add the required helper methods
      -import { sp, SPRequestExecutorClient } from "@pnp/sp-addinhelpers";
      -
      -// this only needs to be done once within your application
      -sp.setup({
      -    sp: {
      -        fetchClientFactory: () => {
      -            return new SPRequestExecutorClient();
      -        }
      -    }
      -});
      -
      -// now we need to use the crossDomainWeb method to make our requests to the host web
      -const addInWenUrl = "{The add-in web url, likely from the query string}";
      -const hostWebUrl = "{The host web url, likely from the query string}";
      -
      -// make requests into the host web via the SP.RequestExecutor
      -const w = await sp.crossDomainWeb(addInWenUrl, hostWebUrl)();
      -console.log(JSON.stringify(w, null, 4));
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/alias-parameters/index.html b/docs/v3/v2/sp/alias-parameters/index.html deleted file mode 100644 index 2c472d7c1..000000000 --- a/docs/v3/v2/sp/alias-parameters/index.html +++ /dev/null @@ -1,2330 +0,0 @@ - - - - - - - - - - - - - - - - - - Alias Parameters - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp - Aliased Parameters

      -

      Within the @pnp/sp api you can alias any of the parameters so they will be written into the querystring. This is most helpful if you are hitting up against the url length limits when working with files and folders.

      -

      To alias a parameter you include the label name, a separator ("::") and the value in the string. You also need to prepend a "!" to the string to trigger the replacement. You can see this below, as well as the string that will be generated. Labels must start with a "@" followed by a letter. It is also your responsibility to ensure that the aliases you supply do not conflict, for example if you use "@p1" you should use "@p2" for a second parameter alias in the same query.

      -

      Construct a parameter alias

      -

      Pattern: !@{label name}::{value}

      -

      Example: "!@p1::\sites\dev" or "!@p2::\text.txt"

      -

      Example without aliasing

      -
      import { sp } from "@pnp/sp/presets/all";
      -
      -// still works as expected, no aliasing
      -const query = sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/").files.select("Title").top(3);
      -
      -console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files
      -console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl('/sites/dev/Shared Documents/')/files?$select=Title&$top=3
      -
      -const r = await query();
      -console.log(r);;
      -
      -

      Example with aliasing

      -
      import { sp } from "@pnp/sp/presets/all";
      -
      -// same query with aliasing
      -const query = sp.web.getFolderByServerRelativeUrl("!@p1::/sites/dev/Shared Documents/").files.select("Title").top(3);
      -
      -console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files
      -console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3
      -
      -const r = await query();
      -console.log(r);
      -
      -

      Example with aliasing and batching

      -

      Aliasing is supported with batching as well:

      -
      import { sp } from "@pnp/sp/presets/all";
      -// same query with aliasing and batching
      -const batch = sp.web.createBatch();
      -
      -const query = sp.web.getFolderByServerRelativeUrl("!@p1::/sites/dev/Shared Documents/").files.select("Title").top(3);
      -
      -console.log(query.toUrl()); // _api/web/getFolderByServerRelativeUrl('!@p1::/sites/dev/Shared Documents/')/files
      -console.log(query.toUrlAndQuery()); // _api/web/getFolderByServerRelativeUrl(@p1)/files?@p1='/sites/dev/Shared Documents/'&$select=Title&$top=3
      -
      -query.inBatch(batch)().then(r => {
      -
      -    console.log(r);
      -});
      -
      -batch.execute();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/alm/index.html b/docs/v3/v2/sp/alm/index.html deleted file mode 100644 index 4295e9138..000000000 --- a/docs/v3/v2/sp/alm/index.html +++ /dev/null @@ -1,2433 +0,0 @@ - - - - - - - - - - - - - - - - - - ALM api - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/appcatalog

      -

      The ALM api allows you to manage app installations both in the tenant app catalog and individual site app catalogs. Some of the methods are still in beta and as such may change in the future. This article outlines how to call this api using @pnp/sp. Remember all these actions are bound by permissions so it is likely most users will not have the rights to perform these ALM actions.

      -

      Understanding the App Catalog Hierarchy

      -

      Before you begin provisioning applications it is important to understand the relationship between a local web catalog and the tenant app catalog. Some of the methods described below only work within the context of the tenant app catalog web, such as adding an app to the catalog and the app actions retract, remove, and deploy. You can install, uninstall, and upgrade an app in any web. Read more in the official documentation.

      -

      Referencing an App Catalog

      -

      There are several ways using @pnp/sp to get a reference to an app catalog. These methods are to provide you the greatest amount of flexibility in gaining access to the app catalog. Ultimately each method produces an AppCatalog instance differentiated only by the web to which it points.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/appcatalog";
      -import "@pnp/sp/webs";
      -
      -// get the current context web's app catalog
      -const catalog = await sp.web.getAppCatalog()();
      -
      -// you can also chain off the app catalog
      -const apps = await sp.web.getAppCatalog()();
      -console.log(apps);
      -
      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/appcatalog";
      -import "@pnp/sp/webs";
      -
      -// you can get the tenant app catalog (or any app catalog) by using the getTenantAppCatalogWeb method
      -const appCatWeb = await sp.getTenantAppCatalogWeb()();
      -const appCatalog = await appCatWeb.getAppCatalog()();
      -
      -// you can get the tenant app catalog (or any app catalog) by passing in a url
      -// get the tenant app catalog
      -const tenantCatalog = await sp.web.getAppCatalog("https://mytenant.sharepoint.com/sites/appcatalog")();
      -
      -// get a different app catalog
      -const catalog = await sp.web.getAppCatalog("https://mytenant.sharepoint.com/sites/anothersite")();
      -
      -
      // alternatively you can create a new app catalog instance directly by importing the AppCatalog class
      -import { IAppCatalog, AppCatalog } from '@pnp/sp/appcatalog';
      -
      -const catalog: IAppCatalog = await AppCatalog("https://mytenant.sharepoint.com/sites/apps")();
      -
      -
      // and finally you can combine use of the Web and AppCatalog classes to create an AppCatalog instance from an existing Web
      -import { Web } from '@pnp/sp/webs';
      -import { AppCatalog } from '@pnp/sp/appcatalog';
      -
      -const web = Web("https://mytenant.sharepoint.com/sites/apps");
      -const catalog = await AppCatalog(web)();
      -
      -

      The following examples make use of a variable "catalog" which is assumed to represent an AppCatalog instance obtained using one of the above methods, supporting code is omitted for brevity.

      -

      List Available Apps

      -

      The AppCatalog is itself a queryable collection so you can query this object directly to get a list of available apps. Also, the odata operators work on the catalog to sort, filter, and select.

      -
      // get available apps
      -await catalog();
      -
      -// get available apps selecting two fields
      -await catalog.select("Title", "Deployed")();
      -
      -

      Add an App

      -

      This action must be performed in the context of the tenant app catalog

      -
      // this represents the file bytes of the app package file
      -const blob = new Blob();
      -
      -// there is an optional third argument to control overwriting existing files
      -const r = await catalog.add("myapp.app", blob);
      -
      -// this is at its core a file add operation so you have access to the response data as well
      -// as a File instance representing the created file
      -console.log(JSON.stringify(r.data, null, 4));
      -
      -// all file operations are available
      -const nameData = await r.file.select("Name")();
      -
      -

      Get an App

      -

      You can get the details of a single app by GUID id. This is also the branch point to perform specific app actions

      -
      const app = await catalog.getAppById("5137dff1-0b79-4ebc-8af4-ca01f7bd393c")();
      -
      -

      Perform app actions

      -

      Remember: retract, deploy, and remove only work in the context of the tenant app catalog web. All of these methods return void and you can monitor success by wrapping the call in a try/catch block.

      -
      const myAppId = "5137dff1-0b79-4ebc-8af4-ca01f7bd393c";
      -
      -// deploy
      -await catalog.getAppById(myAppId).deploy();
      -
      -// retract
      -await catalog.getAppById(myAppId).retract();
      -
      -// install
      -await catalog.getAppById(myAppId).install();
      -
      -// uninstall
      -await catalog.getAppById(myAppId).uninstall();
      -
      -// upgrade
      -await catalog.getAppById(myAppId).upgrade();
      -
      -// remove
      -await catalog.getAppById(myAppId).remove();
      -
      -
      -

      Synchronize a solution/app to the Microsoft Teams App Catalog

      -

      By default this REST call requires the SharePoint item id of the app, not the app id. PnPjs will try to fetch the SharePoint item id by default. You can still use this the second parameter useSharePointItemId to pass your own item id in the first parameter id.

      -
      // Using the app id
      -await catalog.syncSolutionToTeams("5137dff1-0b79-4ebc-8af4-ca01f7bd393c");
      -
      -// Using the SharePoint apps item id
      -await catalog.syncSolutionToTeams("123", true);
      -
      -

      Notes

      -
        -
      • The app catalog is just a document library under the hood, so you can also perform non-ALM actions on the library if needed. But you should be aware of possible side-effects to the ALM life-cycle when doing so.
      • -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/attachments/index.html b/docs/v3/v2/sp/attachments/index.html deleted file mode 100644 index 679adc539..000000000 --- a/docs/v3/v2/sp/attachments/index.html +++ /dev/null @@ -1,2504 +0,0 @@ - - - - - - - - - - - - - - - - - - Attachments - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/attachments

      -

      The ability to attach file to list items allows users to track documents outside of a document library. You can use the PnP JS Core library to work with attachments as outlined below.

      - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/attachments";
      Preset: Allimport { sp, IFeatures, Features } from "@pnp/sp/presets/all";
      -

      Get attachments

      -
      import { sp } from "@pnp/sp";
      -import { IAttachmentInfo } from "@pnp/sp/attachments";
      -import { IItem } from "@pnp/sp/items/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/items";
      -import "@pnp/sp/attachments";
      -
      -const item: IItem = sp.web.lists.getByTitle("MyList").items.getById(1);
      -
      -// get all the attachments
      -const info: IAttachmentInfo[] = await item.attachmentFiles();
      -
      -// get a single file by file name
      -const info2: IAttachmentInfo = await item.attachmentFiles.getByName("file.txt")();
      -
      -// select specific properties using odata operators and use Pick to type the result
      -const info3: Pick<IAttachmentInfo, "ServerRelativeUrl">[] = await item.attachmentFiles.select("ServerRelativeUrl")();
      -
      -

      Add an Attachment

      -

      You can add an attachment to a list item using the add method. This method takes either a string, Blob, or ArrayBuffer.

      -
      import { sp } from "@pnp/sp";
      -import { IItem } from "@pnp/sp/items";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/items";
      -import "@pnp/sp/attachments";
      -
      -const item: IItem = sp.web.lists.getByTitle("MyList").items.getById(1);
      -
      -await item.attachmentFiles.add("file2.txt", "Here is my content");
      -
      -

      Add Multiple

      -

      This method allows you to pass an array of AttachmentFileInfo plain objects that will be added one at a time as attachments. Essentially automating the promise chaining.

      -
      import { sp } from "@pnp/sp";
      -import { IList } from "@pnp/sp/lists";
      -import { IAttachmentFileInfo } from "@pnp/sp/attachments";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/items";
      -import "@pnp/sp/attachments";
      -
      -const list: IList = sp.web.lists.getByTitle("MyList");
      -
      -let fileInfos: IAttachmentFileInfo[] = [];
      -
      -fileInfos.push({
      -    name: "My file name 1",
      -    content: "string, blob, or array"
      -});
      -
      -fileInfos.push({
      -    name: "My file name 2",
      -    content: "string, blob, or array"
      -});
      -
      -await list.items.getById(2).attachmentFiles.addMultiple(fileInfos);
      -
      -

      Delete Multiple

      -
      import { sp } from "@pnp/sp";
      -import { IList } from "./@pnp/sp/lists/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/items";
      -import "@pnp/sp/attachments";
      -
      -const list: IList = sp.web.lists.getByTitle("MyList");
      -
      -await list.items.getById(2).attachmentFiles.deleteMultiple("1.txt", "2.txt");
      -
      -

      Read Attachment Content

      -

      You can read the content of an attachment as a string, Blob, ArrayBuffer, or json using the methods supplied.

      -
      import { sp } from "@pnp/sp";
      -import { IItem } from "@pnp/sp/items/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/items";
      -import "@pnp/sp/attachments";
      -
      -const item: IItem = sp.web.lists.getByTitle("MyList").items.getById(1);
      -
      -const text = await item.attachmentFiles.getByName("file.txt").getText();
      -
      -// use this in the browser, does not work in nodejs
      -const blob = await item.attachmentFiles.getByName("file.mp4").getBlob();
      -
      -// use this in nodejs
      -const buffer = await item.attachmentFiles.getByName("file.mp4").getBuffer();
      -
      -// file must be valid json
      -const json = await item.attachmentFiles.getByName("file.json").getJSON();
      -
      -

      Update Attachment Content

      -

      You can also update the content of an attachment. This API is limited compared to the full file API - so if you need to upload large files consider using a document library.

      -
      import { sp } from "@pnp/sp";
      -import { IItem } from "@pnp/sp/items/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/items";
      -import "@pnp/sp/attachments";
      -
      -const item: IItem = sp.web.lists.getByTitle("MyList").items.getById(1);
      -
      -await item.attachmentFiles.getByName("file2.txt").setContent("My new content!!!");
      -
      -

      Delete Attachment

      -
      import { sp } from "@pnp/sp";
      -import { IItem } from "@pnp/sp/items/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/items";
      -import "@pnp/sp/attachments";
      -
      -const item: IItem = sp.web.lists.getByTitle("MyList").items.getById(1);
      -
      -await item.attachmentFiles.getByName("file2.txt").delete();
      -
      -

      Recycle Attachment

      -

      Delete the attachment and send it to recycle bin

      -
      import { sp } from "@pnp/sp";
      -import { IItem } from "@pnp/sp/items/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/items";
      -import "@pnp/sp/attachments";
      -
      -const item: IItem = sp.web.lists.getByTitle("MyList").items.getById(1);
      -
      -await item.attachmentFiles.getByName("file2.txt").recycle();
      -
      -

      Recycle Multiple Attachments

      -

      Delete multiple attachments and send them to recycle bin

      -
      import { sp } from "@pnp/sp";
      -import { IList } from "@pnp/sp/lists/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/items";
      -import "@pnp/sp/attachments";
      -
      -const list: IList = sp.web.lists.getByTitle("MyList");
      -
      -await list.items.getById(2).attachmentFiles.recycleMultiple("1.txt","2.txt");
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/clientside-pages/index.html b/docs/v3/v2/sp/clientside-pages/index.html deleted file mode 100644 index 97ddb0fce..000000000 --- a/docs/v3/v2/sp/clientside-pages/index.html +++ /dev/null @@ -1,3477 +0,0 @@ - - - - - - - - - - - - - - - - - - Client-side Pages - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/clientside-pages

      -

      The 'clientside-pages' module allows you to create, edit, and delete modern SharePoint pages. There are methods to update the page settings and add/remove client-side web parts.

      -

      Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import { ClientsidePageFromFile, ClientsideText, ClientsideWebpartPropertyTypes, CreateClientsidePage, ClientsideWebpart, IClientsidePage } from "@pnp/sp/clientside-pages";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/clientside-pages";
      Preset: Allimport { sp, ClientsidePageFromFile, ClientsideText, ClientsideWebpartPropertyTypes, CreateClientsidePage, ClientsideWebpart, IClientsidePage } from "@pnp/sp/presets/all";
      -

      Create a new Page

      -

      You can create a new client-side page in several ways, all are equivalent.

      -

      Create using IWeb.addClientsidePage

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/clientside-pages/web";
      -import { PromotedState } from "@pnp/sp/clientside-pages";
      -
      -// Create a page providing a file name
      -const page = await sp.web.addClientsidePage("mypage1");
      -
      -// ... other operations on the page as outlined below
      -
      -// the page is initially not published, you must publish it so it appears for others users
      -await page.save();
      -
      -// include title and page layout
      -const page2 = await sp.web.addClientsidePage("mypage", "My Page Title", "Article");
      -
      -// you must publish the new page
      -await page2.save();
      -
      -// include title, page layout, and specifying the publishing status (Added in 2.0.4)
      -const page3 = await sp.web.addClientsidePage("mypage", "My Page Title", "Article", PromotedState.PromoteOnPublish);
      -
      -// you must publish the new page, after which the page will immediately be promoted to a news article
      -await page3.save();
      -
      -

      Create using CreateClientsidePage method

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import { Web } from "@pnp/sp/webs";
      -import { CreateClientsidePage, PromotedState } from "@pnp/sp/clientside-pages";
      -
      -const page1 = await CreateClientsidePage(sp.web, "mypage2", "My Page Title");
      -
      -// you must publish the new page
      -await page1.save(true);
      -
      -// specify the page layout type parameter
      -const page2 = await CreateClientsidePage(sp.web, "mypage3", "My Page Title", "Article");
      -
      -// you must publish the new page
      -await page2.save();
      -
      -// specify the page layout type parameter while also specifying the publishing status (Added in 2.0.4)
      -const page2half = await CreateClientsidePage(sp.web, "mypage3", "My Page Title", "Article", PromotedState.PromoteOnPublish);
      -
      -// you must publish the new page, after which the page will immediately be promoted to a news article
      -await page2half.save();
      -
      -// use the web factory to create a page in a specific web
      -const page3 = await CreateClientsidePage(Web("https://{absolute web url}"), "mypage4", "My Page Title");
      -
      -// you must publish the new page
      -await page3.save();
      -
      -

      Load Pages

      -

      There are a few ways to load pages, each of which results in an IClientsidePage instance being returned.

      -

      Load using IWeb.loadClientsidePage

      -

      This method takes a server relative path to the page to load.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import { Web } from "@pnp/sp/webs";
      -import "@pnp/sp/clientside-pages/web";
      -
      -// use from the sp.web fluent chain
      -const page = await sp.web.loadClientsidePage("/sites/dev/sitepages/mypage3.aspx");
      -
      -// use the web factory to target a specific web
      -const page2 = await Web("https://{absolute web url}").loadClientsidePage("/sites/dev/sitepages/mypage3.aspx");
      -
      -

      Load using ClientsidePageFromFile

      -

      This method takes an IFile instance and loads an IClientsidePage instance.

      -
      import { sp } from "@pnp/sp";
      -import { ClientsidePageFromFile } from "@pnp/sp/clientside-pages";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files/web";
      -
      -const page = await ClientsidePageFromFile(sp.web.getFileByServerRelativePath("/sites/dev/sitepages/mypage3.aspx"));
      -
      -

      Edit Sections and Columns

      -

      Client-side pages are made up of sections, columns, and controls. Sections contain columns which contain controls. There are methods to operate on these within the page, in addition to the standard array methods available in JavaScript. These samples use a variable page that is understood to be an IClientsidePage instance which is either created or loaded as outlined in previous sections.

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// add two columns with factor 6 - this is a two column layout as the total factor in a section should add up to 12
      -const section1 = page.addSection();
      -section1.addColumn(6);
      -section1.addColumn(6);
      -
      -// create a three column layout in a new section
      -const section2 = page.addSection();
      -section2.addColumn(4);
      -section2.addColumn(4);
      -section2.addColumn(4);
      -
      -// publish our changes
      -await page.save();
      -
      -

      Manipulate Sections and Columns

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// drop all the columns in this section
      -// this will also DELETE all controls contained in the columns
      -page.sections[1].columns.length = 0;
      -
      -// create a new column layout
      -page.sections[1].addColumn(4);
      -page.sections[1].addColumn(8);
      -
      -// publish our changes
      -await page.save();
      -
      -

      Vertical Section

      -

      The vertical section, if on the page, is stored within the sections array. However, you access it slightly differently to make things easier.

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// add or get a vertical section (handles case where section already exists)
      -const vertSection = page.addVerticalSection();
      -
      -// ****************************************************************
      -
      -// if you know or want to test if a vertical section is present:
      -if (page.hasVerticalSection) {
      -
      -    // access the vertical section (this method will NOT create the section if it does not exist)
      -    page.verticalSection.addControl(new ClientsideText("hello"));
      -} else {
      -
      -    const vertSection = page.addVerticalSection();
      -    vertSection.addControl(new ClientsideText("hello"));
      -}
      -
      -

      Reorder Sections

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// swap the order of two sections
      -// this will preserve the controls within the columns
      -page.sections = [page.sections[1], page.sections[0]];
      -
      -// publish our changes
      -await page.save();
      -
      -

      Reorder Columns

      -

      The sections and columns are arrays, so normal array operations work as expected

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// swap the order of two columns
      -// this will preserve the controls within the columns
      -page.sections[1].columns = [page.sections[1].columns[1], page.sections[1].columns[0]];
      -
      -// publish our changes
      -await page.save();
      -
      -

      Clientside Controls

      -

      Once you have your sections and columns defined you will want to add/edit controls within those columns.

      -

      Add Text Content

      -
      import { ClientsideText } from "@pnp/sp/clientside-pages";
      -
      -// our page instance
      -const page: IClientsidePage;
      -
      -page.addSection().addControl(new ClientsideText("@pnp/sp is a great library!"));
      -
      -await page.save();
      -
      -

      Add Controls

      -

      Adding controls involves loading the available client-side part definitions from the server or creating a text part.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/clientside-pages/web";
      -import { ClientsideWebpart } from "@pnp/sp/clientside-pages";
      -
      -// this will be a ClientsidePageComponent array
      -// this can be cached on the client in production scenarios
      -const partDefs = await sp.web.getClientsideWebParts();
      -
      -// find the definition we want, here by id
      -const partDef = partDefs.filter(c => c.Id === "490d7c76-1824-45b2-9de3-676421c997fa");
      -
      -// optionally ensure you found the def
      -if (partDef.length < 1) {
      -    // we didn't find it so we throw an error
      -    throw new Error("Could not find the web part");
      -}
      -
      -// create a ClientWebPart instance from the definition
      -const part = ClientsideWebpart.fromComponentDef(partDef[0]);
      -
      -// set the properties on the web part. Here for the embed web part we only have to supply an embedCode - in this case a YouTube video.
      -// the structure of the properties varies for each web part and each version of a web part, so you will need to ensure you are setting
      -// the properties correctly
      -part.setProperties<{ embedCode: string }>({
      -    embedCode: "https://www.youtube.com/watch?v=IWQFZ7Lx-rg",
      -});
      -
      -// we add that part to a new section
      -page.addSection().addControl(part);
      -
      -await page.save();
      -
      -

      Handle Different Webpart's Settings

      -

      There are many ways that client side web parts are implemented and we can't provide handling within the library for all possibilities. This example shows how to handle a property set within the serverProcessedContent, in this case a List part's display title.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import { ClientsideWebpart } from "@pnp/sp/clientside-pages";
      -
      -// we create a class to wrap our functionality in a reusable way
      -class ListWebpart extends ClientsideWebpart {
      -
      -  constructor(control: ClientsideWebpart) {
      -    super((<any>control).json);
      -  }
      -
      -  // add property getter/setter for what we need, in this case "listTitle" within searchablePlainTexts
      -  public get DisplayTitle(): string {
      -    return this.json.webPartData?.serverProcessedContent?.searchablePlainTexts?.listTitle || "";
      -  }
      -
      -  public set DisplayTitle(value: string) {
      -    this.json.webPartData.serverProcessedContent.searchablePlainTexts.listTitle = value;
      -  }
      -}
      -
      -// now we load our page
      -const page = await sp.web.loadClientsidePage("/sites/dev/SitePages/List-Web-Part.aspx");
      -
      -// get our part and pass it to the constructor of our wrapper class
      -const part = new ListWebpart(page.sections[0].columns[0].getControl(0));
      -
      -part.DisplayTitle = "My New Title!";
      -
      -await page.save();
      -
      -
      -

      Unfortunately each webpart can be authored differently, so there isn't a way to know how the setting for a given webpart are stored without loading it and examining the properties.

      -
      -

      Page Operations

      -

      There are other operation you can perform on a page in addition to manipulating the content.

      -

      pageLayout

      -

      You can get and set the page layout. Changing the layout after creating the page may have side effects and should be done cautiously.

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// get the current value
      -const value = page.pageLayout;
      -
      -// set the value
      -page.pageLayout = "Article";
      -await page.save();
      -
      -

      bannerImageUrl

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// get the current value
      -const value = page.bannerImageUrl;
      -
      -// set the value
      -page.bannerImageUrl = "/server/relative/path/to/image.png";
      -await page.save();
      -
      -
      -

      Banner images need to exist within the same site collection as the page where you want to use them.

      -
      -

      thumbnailUrl

      -

      Allows you to set the thumbnail used for the page independently of the banner.

      -
      -

      If you set the bannerImageUrl property and not thumbnailUrl the thumbnail will be reset to match the banner, mimicking the UI functionality.

      -
      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// get the current value
      -const value = page.thumbnailUrl;
      -
      -// set the value
      -page.thumbnailUrl = "/server/relative/path/to/image.png";
      -await page.save();
      -
      -

      topicHeader

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// get the current value
      -const value = page.topicHeader;
      -
      -// set the value
      -page.topicHeader = "My cool header!";
      -await page.save();
      -
      -// clear the topic header and hide it
      -page.topicHeader = "";
      -await page.save();
      -
      -

      title

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// get the current value
      -const value = page.title;
      -
      -// set the value
      -page.title = "My page title";
      -await page.save();
      -
      -

      description

      -
      -

      Descriptions are limited to 255 chars

      -
      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// get the current value
      -const value = page.description;
      -
      -// set the value
      -page.description = "A description";
      -await page.save();
      -
      -

      layoutType

      -

      Sets the layout type of the page. The valid values are: "FullWidthImage", "NoImage", "ColorBlock", "CutInShape"

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// get the current value
      -const value = page.layoutType;
      -
      -// set the value
      -page.layoutType = "ColorBlock";
      -await page.save();
      -
      -

      headerTextAlignment

      -

      Sets the header text alignment to one of "Left" or "Center"

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// get the current value
      -const value = page.headerTextAlignment;
      -
      -// set the value
      -page.headerTextAlignment = "Center";
      -await page.save();
      -
      -

      showTopicHeader

      -

      Sets if the topic header is displayed on a page.

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// get the current value
      -const value = page.showTopicHeader;
      -
      -// show the header
      -page.showTopicHeader = true;
      -await page.save();
      -
      -// hide the header
      -page.showTopicHeader = false;
      -await page.save();
      -
      -

      showPublishDate

      -

      Sets if the publish date is displayed on a page.

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// get the current value
      -const value = page.showPublishDate;
      -
      -// show the date
      -page.showPublishDate = true;
      -await page.save();
      -
      -// hide the date
      -page.showPublishDate = false;
      -await page.save();
      -
      -

      Get / Set author details

      -

      Added in 2.0.4

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// get the author details (string | null)
      -const value = page.authorByLine;
      -
      -// set the author by user id
      -const user = await web.currentUser.select("Id", "LoginName")();
      -const userId = user.Id;
      -const userLogin = user.LoginName;
      -
      -await page.setAuthorById(userId);
      -await page.save();
      -
      -await page.setAuthorByLoginName(userLogin);
      -await page.save();
      -
      -
      -

      you must still save the page after setting the author to persist your changes as shown in the example.

      -
      -

      load

      -

      Loads the page from the server. This will overwrite any local unsaved changes.

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -await page.load();
      -
      -

      save

      -

      Saves any changes to the page, optionally keeping them in draft state.

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// changes are published
      -await page.save();
      -
      -// changes remain in draft
      -await page.save(false);
      -
      -

      discardPageCheckout

      -

      Discards any current checkout of the page by the current user.

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -await page.discardPageCheckout();
      -
      -

      promoteToNews

      -

      Promotes the page as a news article.

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -await page.promoteToNews();
      -
      -

      enableComments & disableComments

      -

      Used to control the availability of comments on a page.

      -

      Known Issue Banner

      -
      // you need to import the comments sub-module or use the all preset
      -import "@pnp/sp/comments/clientside-page";
      -
      -// our page instance
      -const page: IClientsidePage;
      -
      -// turn on comments
      -await page.enableComments();
      -
      -// turn off comments
      -await page.disableComments();
      -
      -

      findControlById

      -

      Finds a control within the page by id.

      -
      import { ClientsideText } from "@pnp/sp/clientside-pages";
      -
      -// our page instance
      -const page: IClientsidePage;
      -
      -const control = page.findControlById("06d4cdf6-bce6-4200-8b93-667a1b0a6c9d");
      -
      -// you can also type the control
      -const control = page.findControlById<ClientsideText>("06d4cdf6-bce6-4200-8b93-667a1b0a6c9d");
      -
      -

      findControl

      -

      Finds a control within the page using the supplied delegate. Can also be used to iterate through all controls in the page.

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// find the first control whose order is 9
      -const control = page.findControl((c) => c.order === 9);
      -
      -// iterate all the controls and output the id to the console
      -page.findControl((c) => {
      -    console.log(c.id);
      -    return false;
      -});
      -
      -

      like & unlike

      -

      Updates the page's like value for the current user.

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// like this page
      -await page.like();
      -
      -// unlike this page
      -await page.unlike();
      -
      -

      getLikedByInformation

      -

      Gets the likes information for this page.

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -const info = await page.getLikedByInformation();
      -
      -

      copy

      -

      Creates a copy of the page, including all controls.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -
      -// our page instance
      -const page: IClientsidePage;
      -
      -// creates a published copy of the page
      -const pageCopy = await page.copy(sp.web, "newpagename", "New Page Title");
      -
      -// creates a draft (unpublished) copy of the page
      -const pageCopy2 = await page.copy(sp.web, "newpagename", "New Page Title", false);
      -
      -// edits to pageCopy2 ...
      -
      -// publish the page
      -pageCopy2.save();
      -
      -

      copyTo

      -

      Copies the contents of a page to another existing page instance.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -
      -// our page instances, loaded in any of the ways shown above
      -const source: IClientsidePage;
      -const target: IClientsidePage;
      -const target2: IClientsidePage;
      -
      -// creates a published copy of the page
      -await source.copyTo(target);
      -
      -// creates a draft (unpublished) copy of the page
      -await source.copyTo(target2, false);
      -
      -// edits to target2...
      -
      -// publish the page
      -target2.save();
      -
      -

      setBannerImage

      -

      Sets the banner image url and optionally additional properties. Allows you to set additional properties if needed, if you do not need to set the additional properties they are equivalent.

      -
      -

      Banner images need to exist within the same site collection as the page where you want to use them.

      -
      -
      // our page instance
      -const page: IClientsidePage;
      -
      -page.setBannerImage("/server/relative/path/to/image.png");
      -
      -// save the changes
      -await page.save();
      -
      -// set additional props
      -page.setBannerImage("/server/relative/path/to/image.png", {
      -    altText: "Image description",
      -    imageSourceType: 2,
      -    translateX: 30,
      -    translateY: 1234,
      -});
      -
      -// save the changes
      -await page.save();
      -
      -

      This sample shows the full process of adding a page, image file, and setting the banner image in nodejs. The same code would work in a browser with an update on how you get the file - likely from a file input or similar.

      -
      import { SPFetchClient } from "@pnp/nodejs";
      -import { join } from "path";
      -import { readFileSync } from "fs";
      -import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -import "@pnp/sp/folders";
      -import "@pnp/sp/clientside-pages";
      -
      -// configure your node options
      -sp.setup({
      -  sp: {
      -    fetchClientFactory: () => {
      -      return new SPFetchClient("{Site Url}", "{Client Id}", "{Client Secret}");
      -    },
      -  },
      -});
      -
      -// add the banner image
      -const dirname = join("C:/path/to/file", "img-file.jpg");
      -const file: Uint8Array = new Uint8Array(readFileSync(dirname));
      -const far = await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents").files.add("banner.jpg", file, true);
      -
      -// add the page
      -const page = await sp.web.addClientsidePage("MyPage", "Page Title");
      -
      -// set the banner image
      -page.setBannerImage(far.data.ServerRelativeUrl);
      -
      -// publish the page
      -await page.save();
      -
      -

      setBannerImageFromExternalUrl

      -

      Added in 2.0.12

      -

      Allows you to set the banner image from a source outside the current site collection. The image file will be copied to the SiteAssets library and referenced from there.

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// you must await this method
      -await page.setBannerImageFromExternalUrl("https://absolute.url/to/my/image.jpg");
      -
      -// save the changes
      -await page.save();
      -
      -

      You can optionally supply additional props for the banner image, these match the properties when calling setBannerImage

      -
      // our page instance
      -const page: IClientsidePage;
      -
      -// you must await this method
      -await page.setBannerImageFromExternalUrl("https://absolute.url/to/my/image.jpg", {
      -    altText: "Image description",
      -    imageSourceType: 2,
      -    translateX: 30,
      -    translateY: 1234,
      -});
      -
      -// save the changes
      -await page.save();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/column-defaults/index.html b/docs/v3/v2/sp/column-defaults/index.html deleted file mode 100644 index df2fb8371..000000000 --- a/docs/v3/v2/sp/column-defaults/index.html +++ /dev/null @@ -1,2540 +0,0 @@ - - - - - - - - - - - - - - - - - - Column Defaults - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/column-defaults

      -

      The column defaults sub-module allows you to manage the default column values on a library or library folder.

      -

      Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import { IFieldDefault, IFieldDefaultProps, AllowedDefaultColumnValues } from "@pnp/sp/column-defaults";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/column-defaults";
      Preset: Allimport { sp, IFieldDefault, IFieldDefaultProps, AllowedDefaultColumnValues } from "@pnp/sp/presents/all";
      -

      Get Folder Defaults

      -

      You can get the default values for a specific folder as shown below:

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders/web";
      -import "@pnp/sp/column-defaults";
      -
      -const defaults = await sp.web.getFolderByServerRelativePath("/sites/dev/DefaultColumnValues/fld_GHk5").getDefaultColumnValues();
      -
      -/*
      -The resulting structure will have the form:
      -
      -[
      -  {
      -    "name": "{field internal name}",
      -    "path": "/sites/dev/DefaultColumnValues/fld_GHk5",
      -    "value": "{the default value}"
      -  },
      -  {
      -    "name": "{field internal name}",
      -    "path": "/sites/dev/DefaultColumnValues/fld_GHk5",
      -    "value": "{the default value}"
      -  }
      -]
      -*/
      -
      -

      Set Folder Defaults

      -

      When setting the defaults for a folder you need to include the field's internal name and the value.

      -
      -

      For more examples of other field types see the section Pattern for setting defaults on various column types

      -

      Note: Be very careful when setting the path as the site collection url is case sensitive

      -
      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders/web";
      -import "@pnp/sp/column-defaults";
      -
      -await sp.web.getFolderByServerRelativePath("/sites/dev/DefaultColumnValues/fld_GHk5").setDefaultColumnValues([{
      -  name: "TextField",
      -  value: "Something",
      -},
      -{
      -  name: "NumberField",
      -  value: 14,
      -}]);
      -
      -

      Get Library Defaults

      -

      You can also get all of the defaults for the entire library.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/column-defaults";
      -
      -const defaults = await sp.web.lists.getByTitle("DefaultColumnValues").getDefaultColumnValues();
      -
      -/*
      -The resulting structure will have the form:
      -
      -[
      -  {
      -    "name": "{field internal name}",
      -    "path": "/sites/dev/DefaultColumnValues",
      -    "value": "{the default value}"
      -  },
      -  {
      -    "name": "{field internal name}",
      -    "path": "/sites/dev/DefaultColumnValues/fld_GHk5",
      -    "value": "{a different default value}"
      -  }
      -]
      -*/
      -
      -

      Set Library Defaults

      -

      You can also set the defaults for an entire library at once (root and all sub-folders). This may be helpful in provisioning a library or other scenarios. When setting the defaults for the entire library you must also include the path value with is the server relative path to the folder. When setting the defaults for a folder you need to include the field's internal name and the value.

      -
      -

      For more examples of other field types see the section Pattern for setting defaults on various column types

      -

      Note: Be very careful when setting the path as the site collection url is case sensitive

      -
      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/column-defaults";
      -
      -await sp.web.lists.getByTitle("DefaultColumnValues").setDefaultColumnValues([{
      -                name: "TextField",
      -                path: "/sites/dev/DefaultColumnValues",
      -                value: "#PnPjs Rocks!",
      -            }]);
      -
      -

      Clear Folder Defaults

      -

      If you want to clear all of the folder defaults you can use the clear method:

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders/web";
      -import "@pnp/sp/column-defaults";
      -
      -await sp.web.getFolderByServerRelativePath("/sites/dev/DefaultColumnValues/fld_GHk5").clearDefaultColumnValues();
      -
      -

      Clear Library Defaults

      -

      If you need to clear all of the default column values in a library you can pass an empty array to the list's setDefaultColumnValues method.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/column-defaults";
      -
      -await sp.web.lists.getByTitle("DefaultColumnValues").setDefaultColumnValues([]);
      -
      -

      Pattern for setting defaults on various column types

      -

      The following is an example of the structure for setting the default column value when using the setDefaultColumnValues that covers the various field types.

      -
      [{
      -    // Text/Boolean/CurrencyDateTime/Choice/User
      -    name: "TextField":
      -    path: "/sites/dev/DefaultColumnValues",
      -    value: "#PnPjs Rocks!",
      -}, {
      -    //Number
      -    name: "NumberField",
      -    path: "/sites/dev/DefaultColumnValues",
      -    value: 42,
      -}, {
      -    //Date
      -    name: "NumberField",
      -    path: "/sites/dev/DefaultColumnValues",
      -    value: "1900-01-01T00:00:00Z",
      -}, {
      -    //Date - Today
      -    name: "NumberField",
      -    path: "/sites/dev/DefaultColumnValues",
      -    value: "[today]",
      -}, {
      -    //MultiChoice
      -    name: "MultiChoiceField",
      -    path: "/sites/dev/DefaultColumnValues",
      -    value: ["Item 1", "Item 2"],
      -}, {
      -    //MultiChoice - single value
      -    name: "MultiChoiceField",
      -    path: "/sites/dev/DefaultColumnValues/folder2",
      -    value: ["Item 1"],
      -}, {
      -    //Taxonomy - single value
      -    name: "TaxonomyField",
      -    path: "/sites/dev/DefaultColumnValues",
      -    value: {
      -        wssId:"-1",
      -        termName: "TaxValueName",
      -        termId: "924d2077-d5e3-4507-9f36-4a3655e74274"
      -        }
      -}, {
      -    //Taxonomy - multiple value
      -    name: "TaxonomyMultiField",
      -    path: "/sites/dev/DefaultColumnValues",
      -    value: [{
      -        wssId:"-1",
      -        termName: "TaxValueName",
      -        termId: "924d2077-d5e3-4507-9f36-4a3655e74274"
      -        },{
      -        wssId:"-1",
      -        termName: "TaxValueName2",
      -        termId: "95d4c307-dde5-49d8-b861-392e145d94d3"
      -        },]
      -}]);
      -
      -

      Taxonomy Full Example

      -

      This example shows fully how to get the taxonomy values and set them as a default column value using PnPjs.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/column-defaults";
      -import "@pnp/sp/taxonomy";
      -
      -// get the term's info we want to use as the default
      -const term = await sp.termStore.sets.getById("ea6fc521-d293-4f3d-9e84-f3a5bc0936ce").getTermById("775c9cf6-c3cd-4db9-8cfa-fc0aeefad93a")();
      -
      -// get the default term label
      -const defLabel = term.labels.find(v => v.isDefault);
      -
      -// set the default value using -1, the term id, and the term's default label name
      -await sp.web.lists.getByTitle("MetaDataDocLib").rootFolder.setDefaultColumnValues([{
      -    name: "MetaDataColumnInternalName",
      -    value: {
      -        wssId: "-1",
      -        termId: term.id,
      -        termName: defLabel.name,
      -    }
      -}])
      -
      -// check that the defaults have updated
      -const newDefaults = await sp.web.lists.getByTitle("MetaDataDocLib").getDefaultColumnValues();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/comments-likes/index.html b/docs/v3/v2/sp/comments-likes/index.html deleted file mode 100644 index ff6e0c55f..000000000 --- a/docs/v3/v2/sp/comments-likes/index.html +++ /dev/null @@ -1,2646 +0,0 @@ - - - - - - - - - - - - - - - - - - Comments and Likes - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      - -
      - - -
      -
      - - - - - - - -

      @pnp/sp/comments and likes

      -

      Comments can be accessed through either IItem or IClientsidePage instances, though in slightly different ways. For information on loading clientside pages or items please refer to those articles.

      -

      These APIs are currently in BETA and are subject to change or may not work on all tenants.

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/comments";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      ClientsidePage Comments

      -

      The IClientsidePage interface has three methods to provide easier access to the comments for a page, without requiring that you load the item separately.

      -

      Add Comments

      -

      You can add a comment using the addComment method as shown

      -
      import { CreateClientsidePage } from "@pnp/sp/clientside-pages";
      -import "@pnp/sp/comments/clientside-page";
      -
      -const page = await CreateClientsidePage(sp.web, "mypage", "My Page Title", "Article");
      -// optionally publish the page first
      -await page.save();
      -
      -const comment = await page.addComment("A test comment");
      -
      -

      Get Page Comments

      -
      import { CreateClientsidePage } from "@pnp/sp/clientside-pages";
      -import "@pnp/sp/comments/clientside-page";
      -
      -const page = await CreateClientsidePage(sp.web, "mypage", "My Page Title", "Article");
      -// optionally publish the page first
      -await page.save();
      -
      -await page.addComment("A test comment");
      -await page.addComment("A test comment");
      -await page.addComment("A test comment");
      -await page.addComment("A test comment");
      -await page.addComment("A test comment");
      -await page.addComment("A test comment");
      -
      -const comments = await page.getComments();
      -
      -

      enableComments & disableComments

      -

      Used to control the availability of comments on a page

      -
      // you need to import the comments sub-module or use the all preset
      -import "@pnp/sp/comments/clientside-page";
      -
      -// our page instance
      -const page: IClientsidePage;
      -
      -// turn on comments
      -await page.enableComments();
      -
      -// turn off comments
      -await page.disableComments();
      -
      -

      GetById

      -
      import { CreateClientsidePage } from "@pnp/sp/clientside-pages";
      -import "@pnp/sp/comments/clientside-page";
      -
      -const page = await CreateClientsidePage(sp.web, "mypage", "My Page Title", "Article");
      -// optionally publish the page first
      -await page.save();
      -
      -const comment = await page.addComment("A test comment");
      -
      -const commentData = await page.getCommentById(parseInt(comment.id, 10));
      -
      -

      Clear Comments

      -

      Item Comments

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files/web";
      -import "@pnp/sp/items";
      -import "@pnp/sp/comments/item";
      -
      -const item = await sp.web.getFileByServerRelativeUrl("/sites/dev/SitePages/Test_8q5L.aspx").getItem();
      -
      -// as an example, or any of the below options
      -await item.like();
      -
      -

      The below examples use a variable named "item" which is taken to represent an IItem instance.

      -

      Comments

      -

      Get Item Comments

      -
      const comments = await item.comments();
      -
      -

      You can also get the comments merged with instances of the Comment class to immediately start accessing the properties and methods:

      -
      import { spODataEntityArray } from "@pnp/sp/odata";
      -import { Comment, ICommentData } from "@pnp/sp/comments";
      -
      -const comments = await item.comments(spODataEntityArray<Comment, CommentData>(Comment));
      -
      -// these will be Comment instances in the array
      -comments[0].replies.add({ text: "#PnPjs is pretty ok!" });
      -
      -//load the top 20 replies and comments for an item including likedBy information
      -const comments = await item.comments.expand("replies", "likedBy", "replies/likedBy").top(20)();
      -
      -

      Add Comment

      -
      // you can add a comment as a string
      -item.comments.add("string comment");
      -
      -// or you can add it as an object to include mentions
      -item.comments.add({ text: "comment from object property" });
      -
      -

      Delete a Comment

      -
      import { spODataEntityArray, Comment, CommentData } from "@pnp/sp";
      -
      -const comments = await item.comments(spODataEntityArray<Comment, CommentData>(Comment));
      -
      -// these will be Comment instances in the array
      -comments[0].delete()
      -
      -

      Like Comment

      -
      import { spODataEntityArray, Comment, CommentData } from "@pnp/sp";
      -
      -const comments = await item.comments(spODataEntityArray<Comment, CommentData>(Comment));
      -
      -// these will be Comment instances in the array
      -comments[0].like()
      -
      -

      Unlike Comment

      -
      import { spODataEntityArray, Comment, CommentData } from "@pnp/sp";
      -
      -const comments = await item.comments(spODataEntityArray<Comment, CommentData>(Comment));
      -
      -comments[0].unlike()
      -
      -

      Reply to a Comment

      -
      import { spODataEntityArray, Comment, CommentData } from "@pnp/sp";
      -
      -const comments = await item.comments(spODataEntityArray<Comment, CommentData>(Comment));
      -
      -const comment: Comment & CommentData = await comments[0].replies.add({ text: "#PnPjs is pretty ok!" });
      -
      -

      Load Replies to a Comment

      -
      import { spODataEntityArray, Comment, CommentData } from "@pnp/sp";
      -
      -const comments = await item.comments(spODataEntityArray<Comment, CommentData>(Comment));
      -
      -const replies = await comments[0].replies();
      -
      -

      Like

      -

      You can like/unlike client-side pages, items, and comments on items. See above for how to like or unlike a comment. Below you can see how to like and unlike an items, as well as get the liked by data.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/comments/item";
      -import { ILikeData, ILikedByInformation } from "@pnp/sp/comments";
      -
      -// like an item
      -await item.like();
      -
      -// unlike an item
      -await item.unlike();
      -
      -// get the liked by data
      -const likedByData: ILikeData[] = await item.getLikedBy();
      -
      -// get the liked by information
      -const likedByInfo: ILikedByInformation = await item.getLikedByInformation();
      -
      -

      To like/unlike a client-side page and get liked by information.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/comments/clientside-page";
      -import { ILikedByInformation } from "@pnp/sp/comments";
      -
      -// like a page
      -await page.like();
      -
      -// unlike a page
      -await page.unlike();
      -
      -// get the liked by information
      -const likedByInfo: ILikedByInformation = await page.getLikedByInformation();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/content-types/index.html b/docs/v3/v2/sp/content-types/index.html deleted file mode 100644 index 9bb4d4b1b..000000000 --- a/docs/v3/v2/sp/content-types/index.html +++ /dev/null @@ -1,2462 +0,0 @@ - - - - - - - - - - - - - - - - - - Content Types - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/content-types

      -

      Content Types are used to define sets of columns in SharePoint.

      -

      IContentTypes

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import { Webs, IWebs } from "@pnp/sp/webs";
      import { ContentTypes, IContentTypes } from "@pnp/sp/content-types";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/content-types";
      Preset: Allimport { sp, ContentTypes, IContentTypes } from "@pnp/sp/presets/all";
      -

      Add an existing Content Type to a collection

      -

      The following example shows how to add the built in Picture Content Type to the Documents library.

      -
      sp.web.lists.getByTitle("Documents").contentTypes.addAvailableContentType("0x010102");
      -
      -

      Get a Content Type by Id

      -
      const d: IContentType = await sp.web.contentTypes.getById("0x01")();
      -
      -// log content type name to console
      -console.log(d.name);
      -
      -

      Add a new Content Type

      -

      To add a new Content Type to a collection, parameters id and name are required. For more information on creating content type IDs reference the Microsoft Documentation. While this documentation references SharePoint 2010 the structure of the IDs has not changed.

      -
      sp.web.contentTypes.add("0x01008D19F38845B0884EBEBE239FDF359184", "My Content Type");
      -
      -

      It is also possible to provide a description and group parameter. For other settings, we can use the parameter named 'additionalSettings' which is a TypedHash, meaning you can send whatever properties you'd like in the body (provided that the property is supported by the SharePoint API).

      -
      //Adding a content type with id, name, description, group and setting it to read only mode (using additionalsettings)
      -sp.web.contentTypes.add("0x01008D19F38845B0884EBEBE239FDF359184", "My Content Type", "This is my content type.", "_PnP Content Types", { ReadOnly: true });
      -
      -

      IContentType

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import { ContentType, IContentType } from "@pnp/sp/content-types";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/content-types";
      Preset: Allimport { sp, ContentType, IContentType } from "@pnp/sp/presets/all";
      -

      Invokable Banner Selective Imports Banner

      - -

      Use this method to get a collection containing all the field links (SP.FieldLink) for a Content Type.

      -
      // get field links from built in Content Type Document (Id: "0x0101")
      -const d = await sp.web.contentTypes.getById("0x0101").fieldLinks();
      -
      -// log collection of fieldlinks to console
      -console.log(d);
      -
      -

      Get Content Type fields

      -

      To get a collection with all fields on the Content Type, simply use this method.

      -
      // get fields from built in Content Type Document (Id: "0x0101")
      -const d = await sp.web.contentTypes.getById("0x0101").fields();
      -
      -// log collection of fields to console
      -console.log(d);
      -
      -

      Get parent Content Type

      -
      // get parent Content Type from built in Content Type Document (Id: "0x0101")
      -const d = await sp.web.contentTypes.getById("0x0101").parent();
      -
      -// log name of parent Content Type to console
      -console.log(d.Name)
      -
      -

      Get Content Type Workflow associations

      -
      // get workflow associations from built in Content Type Document (Id: "0x0101")
      -const d = await sp.web.contentTypes.getById("0x0101").workflowAssociations();
      -
      -// log collection of workflow associations to console
      -console.log(d);
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/custom-irequestclient/index.html b/docs/v3/v2/sp/custom-irequestclient/index.html deleted file mode 100644 index b6258e94f..000000000 --- a/docs/v3/v2/sp/custom-irequestclient/index.html +++ /dev/null @@ -1,2339 +0,0 @@ - - - - - - - - - - - - - - - - - - Custom Request Client - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      Custom IRequestClient

      -

      Scenario: You have some special requirements involving auth scenarios or other needs that the library can't directly support. You may need to create a custom IRequestClient implementation to meet those needs as we can't customize the library to handle every case. This article walks you through how to create a custom IRequestClient and register it for use by the library.

      -
      -

      It is very unlikely this is a step you ever need to take and we encourage you to ask a question in the issues list before going down this path.

      -
      -

      Create the Client

      -

      The easiest way to create a new IRequestClient is to subclass the existing SPHttpClient. You can always write a full client from scratch so long as it supports the IRequestClient interface but you need to handle all of the logic for retry, headers, and the request digest.

      -

      Here we show implementing a client to solve the need discussed in pull request 1264 as an example.

      -
      // we subclass SPHttpClient
      -class CustomSPHttpClient extends SPHttpClient {
      -
      -  // optionally add a constructor, done here as an example
      -  constructor(impl?: IHttpClientImpl) {
      -    super(impl);
      -  }
      -
      -  // override the fetchRaw method to ensure we always include the credentials = "include" option
      -  // you could also override fetch, but fetchRaw ensures no matter what all requests get your custom logic is applied
      -  public fetchRaw(url: string, options?: IFetchOptions): Promise<Response> {
      -    options.credentials = "include";
      -    return super.fetchRaw(url, options);
      -  }
      -}
      -
      -

      The final step is to register the custom client with the library so it is used instead of the default. For that we import the registerCustomRequestClientFactory function and call it before our request generating code. You can reset to the default client factory by passing null to this same function.

      -
      import { sp, registerCustomRequestClientFactory } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -
      -registerCustomRequestClientFactory(() => new CustomSPHttpClient());
      -
      -// configure your other options
      -sp.setup({
      -    // ...
      -});
      -
      -// this request will be executed through your custom client
      -const w = await sp.web();
      -
      -

      Unregister Custom Client

      -
      // unregister custom client factory
      -registerCustomRequestClientFactory(null);
      -
      -

      IRequestClient Interface

      -

      If you want to 100% roll your own client you need to implement the below interface, found in common.

      -
      import { IRequestClient } from "@pnp/core";
      -
      -
      export interface IRequestClient {
      -    fetch(url: string, options?: IFetchOptions): Promise<Response>;
      -    fetchRaw(url: string, options?: IFetchOptions): Promise<Response>;
      -    get(url: string, options?: IFetchOptions): Promise<Response>;
      -    post(url: string, options?: IFetchOptions): Promise<Response>;
      -    patch(url: string, options?: IFetchOptions): Promise<Response>;
      -    delete(url: string, options?: IFetchOptions): Promise<Response>;
      -}
      -
      -

      Supportability Note

      -

      We cannot provide support for your custom client implementation, and creating your own client assumes an intimate knowledge of how SharePoint requests work. Again, this is very likely something you will never need to do - and we recommend exhausting all other options before taking this route.

      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/entity-merging/index.html b/docs/v3/v2/sp/entity-merging/index.html deleted file mode 100644 index 89363ad03..000000000 --- a/docs/v3/v2/sp/entity-merging/index.html +++ /dev/null @@ -1,2325 +0,0 @@ - - - - - - - - - - - - - - - - - - Entity Merging - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp - entity merging

      -

      Sometimes when we make a query entity's data we would like then to immediately run other commands on the returned entity. To have data returned as its representing type we make use of the spODataEntity and spODataEntityArray parsers. The below approach works for all instance types such as List, Web, Item, or Field as examples.

      -

      Importing spODataEntity and spODataEntityArray

      -

      You can import spODataEntity and spODataEntityArray in two ways, depending on your use case. The simplest way is to use the presets/all import as shown in the examples. The downside of this approach is that you can't take advantage of selective imports.

      -

      If you want to take advantage of selective imports while using either of the entity parsers you can use:

      -
      import { spODataEntity, spODataEntityArray } from "@pnp/sp/odata";
      -
      -

      The full selective import for the first sample would be:

      -
      import { sp } from "@pnp/sp";
      -import { spODataEntity } from "@pnp/sp/odata";
      -import { Item, IItem } from "@pnp/sp/items";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -
      -

      Request a single entity

      -

      If we are loading a single entity we use the spODataEntity method. Here we show loading a list item using the Item class and a simple get query.

      -
      import { sp, spODataEntity, Item, IItem } from "@pnp/sp/presets/all";
      -
      -// interface defining the returned properties
      -interface MyProps {
      -    Id: number;
      -}
      -
      -try {
      -
      -    // get a list item loaded with data and merged into an instance of Item
      -    const item = await sp.web.lists.getByTitle("ListTitle").items.getById(1).usingParser(spODataEntity<IItem, MyProps>(Item))();
      -
      -    // log the item id, all properties specified in MyProps will be type checked
      -    Logger.write(`Item id: ${item.Id}`);
      -
      -    // now we can call update because we have an instance of the Item type to work with as well
      -    await item.update({
      -        Title: "New title.",
      -    });
      -
      -} catch (e) {
      -    Logger.error(e);
      -}
      -
      -

      Request a collection

      -

      The same pattern works when requesting a collection of objects with the exception of using the spODataEntityArray method.

      -
      import { sp, spODataEntityArray, Item, IItem } from "@pnp/sp/presets/all";
      -
      -// interface defining the returned properties
      -interface MyProps {
      -    Id: number;
      -    Title: string;
      -}
      -
      -try {
      -
      -    // get a list item loaded with data and merged into an instance of Item
      -    const items = await sp.web.lists.getByTitle("OrderByList").items.select("Id", "Title").usingParser(spODataEntityArray<IItem, MyProps>(Item))();
      -
      -    Logger.write(`Item id: ${items.length}`);
      -
      -    Logger.write(`Item id: ${items[0].Title}`);
      -
      -    // now we can call update because we have an instance of the Item type to work with as well
      -    await items[0].update({
      -        Title: "New title.",
      -    });
      -
      -} catch (e) {
      -
      -    Logger.error(e);
      -}
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/features/index.html b/docs/v3/v2/sp/features/index.html deleted file mode 100644 index 784a4ff7b..000000000 --- a/docs/v3/v2/sp/features/index.html +++ /dev/null @@ -1,2440 +0,0 @@ - - - - - - - - - - - - - - - - - - Features - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/features

      -

      Features module provides method to get the details of activated features. And to activate/deactivate features scoped at Site Collection and Web.

      -

      IFeatures

      -

      Invokable Banner Selective Imports Banner

      -

      Represents a collection of features. SharePoint Sites and Webs will have a collection of features

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/features/site";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/features/web";
      Selective 3import { sp } from "@pnp/sp";
      import "@pnp/sp/features";
      Selective 4import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/features";
      Preset: Allimport { sp, IFeatures, Features } from "@pnp/sp/presets/all";
      -

      getById

      -

      Gets the information about a feature for the given GUID

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/features";
      -
      -//Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a
      -const webFeatureId = "guid-of-web-feature";
      -const webFeature = await sp.web.features.getById(webFeatureId)();
      -
      -const siteFeatureId = "guid-of-site-scope-feature";
      -const siteFeature = await sp.site.features.getById(siteFeatureId)();
      -
      -

      add

      -

      Adds (activates) a feature at the Site or Web level

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/features";
      -
      -//Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a
      -const webFeatureId = "guid-of-web-feature";
      -let res = await sp.web.features.add(webFeatureId);
      -// Activate with force
      -res = await sp.web.features.add(webFeatureId, true);
      -
      -

      remove

      -

      Removes and deactivates the specified feature from the SharePoint Site or Web

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/features";
      -
      -//Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a
      -const webFeatureId = "guid-of-web-feature";
      -let res = await sp.web.features.remove(webFeatureId);
      -// Deactivate with force
      -res = await sp.web.features.remove(webFeatureId, true);
      -
      -

      IFeature

      -

      Represents an instance of a SharePoint feature.

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/features/site";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/features/web";
      Selective 3import { sp } from "@pnp/sp";
      import "@pnp/sp/features";
      Selective 4import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/features";
      Preset: Allimport { sp, IFeatures, Features, IFeature, Feature } from "@pnp/sp/presets/all";
      -

      deactivate

      -

      Deactivates the specified feature from the SharePoint Site or Web

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/features";
      -
      -//Example of GUID format a7a2793e-67cd-4dc1-9fd0-43f61581207a
      -const webFeatureId = "guid-of-web-feature";
      -sp.web.features.getById(webFeatureId).deactivate()
      -
      -// Deactivate with force
      -sp.web.features.getById(webFeatureId).deactivate(true)
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/fields/index.html b/docs/v3/v2/sp/fields/index.html deleted file mode 100644 index cf538cc9e..000000000 --- a/docs/v3/v2/sp/fields/index.html +++ /dev/null @@ -1,3103 +0,0 @@ - - - - - - - - - - - - - - - - - - Fields - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/lists

      -

      Fields in SharePoint can be applied to both webs and lists. When referencing a webs' fields you are effectively looking at site columns which are common fields that can be utilized in any list/library in the site. When referencing a lists' fields you are looking at the fields only associated to that particular list.

      -

      IFields

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import { Webs, IWebs } from "@pnp/sp/webs";
      import { Fields, IFields } from "@pnp/sp/fields";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/fields";
      Preset: Allimport { sp, Fields, IFields } from "@pnp/sp/presets/all";
      Preset: Coreimport { sp, Fields, IFields } from "@pnp/sp/presets/core";
      -

      Get Field by Id

      -

      Gets a field from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive.

      -
      import { sp } from "@pnp/sp";
      -import { IField } from "@pnp/sp/fields/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/fields";
      -
      -// get the field by Id for web
      -const field: IField = sp.web.fields.getById("03b05ff4-d95d-45ed-841d-3855f77a2483");
      -// get the field by Id for list 'My List'
      -const field2: IFieldInfo = await sp.web.lists.getByTitle("My List").fields.getById("03b05ff4-d95d-45ed-841d-3855f77a2483")();
      -
      -// we can use this 'field' variable to execute more queries on the field:
      -const r = await field.select("Title")();
      -
      -// show the response from the server
      -console.log(r.Title);
      -
      -

      Get Field by Title

      -

      You can also get a field from the collection by title.

      -
      import { sp } from "@pnp/sp";
      -import { IField } from "@pnp/sp/fields/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists"
      -import "@pnp/sp/fields";
      -
      -// get the field with the title 'Author' for web
      -const field: IField = sp.web.fields.getByTitle("Author");
      -// get the field with the title 'Author' for list 'My List'
      -const field2: IFieldInfo = await sp.web.lists.getByTitle("My List").fields.getByTitle("Author")();
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Get Field by Internal Name or Title

      -

      You can also get a field from the collection regardless of if the string is the fields internal name or title which can be different.

      -
      import { sp } from "@pnp/sp";
      -import { IField } from "@pnp/sp/fields/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists"
      -import "@pnp/sp/fields";
      -
      -// get the field with the internal name 'ModifiedBy' for web
      -const field: IField = sp.web.fields.getByInternalNameOrTitle("ModifiedBy");
      -// get the field with the internal name 'ModifiedBy' for list 'My List'
      -const field2: IFieldInfo = await sp.web.lists.getByTitle("My List").fields.getByInternalNameOrTitle("ModifiedBy")();
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Create a Field using an XML schema

      -

      Create a new field by defining an XML schema that assigns all the properties for the field.

      -
      import { sp } from "@pnp/sp";
      -import { IField } from "@pnp/sp/fields/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// define the schema for your new field, in this case a date field with a default date of today.
      -const fieldSchema = `<Field ID="{03b09ff4-d99d-45ed-841d-3855f77a2483}" StaticName="MyField" Name="MyField" DisplayName="My New Field" FriendlyDisplayFormat="Disabled" Format="DateOnly" Type="DateTime" Group="My Group"><Default>[today]</Default></Field>`;
      -
      -// create the new field in the web
      -const field: IFieldAddResult = await sp.web.fields.createFieldAsXml(fieldSchema);
      -// create the new field in the list 'My List'
      -const field2: IFieldAddResult = await sp.web.lists.getByTitle("My List").fields.createFieldAsXml(fieldSchema);
      -
      -// we can use this 'field' variable to run more queries on the list:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a New Field

      -

      Use the add method to create a new field where you define the field type

      -
      import { sp } from "@pnp/sp";
      -import { IField } from "@pnp/sp/fields/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// create a new field called 'My Field' in web.
      -const field: IFieldAddResult = await sp.web.fields.add("My Field", "SP.FieldText", { FieldTypeKind: 3, Group: "My Group" });
      -// create a new field called 'My Field' in the list 'My List'
      -const field2: IFieldAddResult = await sp.web.lists.getByTitle("My List").fields.add("My Field", "SP.FieldText", { FieldTypeKind: 3, Group: "My Group" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a Site Field to a List

      -

      Use the createFieldAsXml method to add a site field to a list.

      -
      import { sp } from "@pnp/sp";
      -import { IFieldAddResult } from "@pnp/sp/fields/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// create a new field called 'My Field' in web.
      -const field: IFieldAddResult = await sp.web.fields.add("My Field", "SP.FieldText", { FieldTypeKind: 3, Group: "My Group" });
      -// add the site field 'My Field' to the list 'My List'
      -const r = await sp.web.lists.getByTitle("My List").fields.createFieldAsXml(field.data.SchemaXml);
      -
      -// log the field Id to console
      -console.log(r.data.Id);
      -
      -

      Add a Text Field

      -

      Use the addText method to create a new text field.

      -
      import { sp } from "@pnp/sp";
      -import { IFieldAddResult } from "@pnp/sp/fields/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// create a new text field called 'My Field' in web.
      -const field: IFieldAddResult = await sp.web.fields.addText("My Field", 255, { Group: "My Group" });
      -// create a new text field called 'My Field' in the list 'My List'.
      -const field2: IFieldAddResult = await sp.web.lists.getByTitle("My List").fields.addText("My Field", 255, { Group: "My Group" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a Calculated Field

      -

      Use the addCalculated method to create a new calculated field.

      -
      import { sp } from "@pnp/sp";
      -import { DateTimeFieldFormatType, FieldTypes } from "@pnp/sp/fields/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// create a new calculated field called 'My Field' in web
      -const field = await sp.web.fields.addCalculated("My Field", "=Modified+1", DateTimeFieldFormatType.DateOnly, FieldTypes.DateTime, { Group: "MyGroup" });
      -// create a new calculated field called 'My Field' in the list 'My List'
      -const field2 = await sp.web.lists.getByTitle("My List").fields.addCalculated("My Field", "=Modified+1", DateTimeFieldFormatType.DateOnly, FieldTypes.DateTime, { Group: "MyGroup" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a Date/Time Field

      -

      Use the addDateTime method to create a new date/time field.

      -
      import { sp } from "@pnp/sp";
      -import { DateTimeFieldFormatType, CalendarType, DateTimeFieldFriendlyFormatType } from "@pnp/sp/fields/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// create a new date/time field called 'My Field' in web
      -const field = await sp.web.fields.addDateTime("My Field", DateTimeFieldFormatType.DateOnly, CalendarType.Gregorian, DateTimeFieldFriendlyFormatType.Disabled, { Group: "My Group" });
      -// create a new date/time field called 'My Field' in the list 'My List'
      -const field2 = await sp.web.lists.getByTitle("My List").fields.addDateTime("My Field", DateTimeFieldFormatType.DateOnly, CalendarType.Gregorian, DateTimeFieldFriendlyFormatType.Disabled, { Group: "My Group" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a Currency Field

      -

      Use the addCurrency method to create a new currency field.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// create a new currency field called 'My Field' in web
      -const field = await sp.web.fields.addCurrency("My Field", 0, 100, 1033, { Group: "My Group" });
      -// create a new currency field called 'My Field' in list 'My List'
      -const field2 = await sp.web.lists.getByTitle("My List").fields.addCurrency("My Field", 0, 100, 1033, { Group: "My Group" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a Multi-line Text Field

      -

      Use the addMultilineText method to create a new multi-line text field.

      -
      -

      For Enhanced Rich Text mode, see the next section.

      -
      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// create a new multi-line text field called 'My Field' in web
      -const field = await sp.web.fields.addMultilineText("My Field", 6, true, false, false, true, { Group: "My Group" });
      -// create a new multi-line text field called 'My Field' in list 'My List'
      -const field2 = await sp.web.lists.getByTitle("My List").fields.addMultilineText("My Field", 6, true, false, false, true, { Group: "My Group" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a Multi-line Text Field with Enhanced Rich Text

      -

      The REST endpoint doesn't support setting the RichTextMode field therefore you will need to revert to Xml to create the field. The following is an example that will create a multi-line text field in Enhanced Rich Text mode.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -//Create a new multi-line text field called 'My Field' in web
      -const field = await sp.web.lists.getByTitle("My List").fields.createFieldAsXml(
      -    `<Field Type="Note" Name="MyField" DisplayName="My Field" Required="FALSE" RichText="TRUE" RichTextMode="FullHtml" />`
      -);
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a Number Field

      -

      Use the addNumber method to create a new number field.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// create a new number field called 'My Field' in web
      -const field = await sp.web.fields.addNumber("My Field", 1, 100, { Group: "My Group" });
      -// create a new number field called 'My Field' in list 'My List'
      -const field2 = await sp.web.lists.getByTitle("My List").fields.addNumber("My Field", 1, 100, { Group: "My Group" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a URL Field

      -

      Use the addUrl method to create a new url field.

      -
      import { sp } from "@pnp/sp";
      -import { UrlFieldFormatType } from "@pnp/sp/fields/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// create a new url field called 'My Field' in web
      -const field = await sp.web.fields.addUrl("My Field", UrlFieldFormatType.Hyperlink, { Group: "My Group" });
      -// create a new url field called 'My Field' in list 'My List'
      -const field2 = await sp.web.lists.getByTitle("My List").fields.addUrl("My Field", UrlFieldFormatType.Hyperlink, { Group: "My Group" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a User Field

      -

      Use the addUser method to create a new user field.

      -
      import { sp } from "@pnp/sp";
      -import { FieldUserSelectionMode } from "@pnp/sp/fields/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// create a new user field called 'My Field' in web
      -const field = await sp.web.fields.addUser("My Field", FieldUserSelectionMode.PeopleOnly, { Group: "My Group" });
      -// create a new user field called 'My Field' in list 'My List'
      -const field2 = await sp.web.lists.getByTitle("My List").fields.addUser("My Field", FieldUserSelectionMode.PeopleOnly, { Group: "My Group" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a Lookup Field

      -

      Use the addLookup method to create a new lookup field.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -const list = await sp.web.lists.getByTitle("My Lookup List")();
      -// create a new lookup field called 'My Field' based on an existing list 'My Lookup List' showing 'Title' field in web.
      -const field = await sp.web.fields.addLookup("My Field", list.Id, "Title",  { Group: "My Group" });
      -// create a new lookup field called 'My Field' based on an existing list 'My Lookup List' showing 'Title' field in list 'My List'
      -const field2 = await sp.web.lists.getByTitle("My List").fields.addLookup("My Field", list.Id, "Title",  { Group: "My Group" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -// **
      -// Adding a lookup that supports multiple values takes two calls:
      -const fieldAddResult = await sp.web.fields.addLookup("Test Lookup 124", "GUID", "Title");
      -
      -await fieldAddResult.field.update({ Description: 'New Description' }, "SP.FieldLookup");
      -
      -

      Add a Choice Field

      -

      Use the addChoice method to create a new choice field.

      -
      import { sp } from "@pnp/sp";
      -import { ChoiceFieldFormatType } from "@pnp/sp/fields/types";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -const choices = [`ChoiceA`, `ChoiceB`, `ChoiceC`];
      -// create a new choice field called 'My Field' in web
      -const field = await sp.web.fields.addChoice("My Field", choices, ChoiceFieldFormatType.Dropdown, false, { Group: "My Group" });
      -// create a new choice field called 'My Field' in list 'My List'
      -const field2 = await sp.web.lists.getByTitle("My List").fields.addChoice("My Field", choices, ChoiceFieldFormatType.Dropdown, false, { Group: "My Group" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a Multi-Choice Field

      -

      Use the addMultiChoice method to create a new multi-choice field.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -const choices = [`ChoiceA`, `ChoiceB`, `ChoiceC`];
      -// create a new multi-choice field called 'My Field' in web
      -const field = await sp.web.fields.addMultiChoice("My Field", choices, false, { Group: "My Group" });
      -// create a new multi-choice field called 'My Field' in list 'My List'
      -const field2 = await sp.web.lists.getByTitle("My List").fields.addMultiChoice("My Field", choices, false, { Group: "My Group" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a Boolean Field

      -

      Use the addBoolean method to create a new boolean field.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// create a new boolean field called 'My Field' in web
      -const field = await sp.web.fields.addBoolean("My Field", { Group: "My Group" });
      -// create a new boolean field called 'My Field' in list 'My List'
      -const field2 = await sp.web.lists.getByTitle("My List").fields.addBoolean("My Field", { Group: "My Group" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a Dependent Lookup Field

      -

      Use the addDependentLookupField method to create a new dependent lookup field.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// create a new dependent lookup field called 'My Dep Field' showing 'Description' based on an existing 'My Field' lookup field in web.
      -const field = await sp.web.fields.getByTitle("My Field")();
      -const fieldDep = await sp.web.fields.addDependentLookupField("My Dep Field", field.Id, "Description");
      -// create a new dependent lookup field called 'My Dep Field' showing 'Description' based on an existing 'My Field' lookup field in list 'My List'
      -const field2 = await sp.web.lists.getByTitle("My List").fields.getByTitle("My Field")();
      -const fieldDep2 = await sp.web.lists.getByTitle("My List").fields.addDependentLookupField("My Dep Field", field2.Id, "Description");
      -
      -// we can use this 'fieldDep' variable to run more queries on the field:
      -const r = await fieldDep.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Add a Location Field

      -

      Use the addLocation method to create a new location field.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// create a new location field called 'My Field' in web
      -const field = await sp.web.fields.addLocation("My Field", { Group: "My Group" });
      -// create a new location field called 'My Field' in list 'My List'
      -const field2 = await sp.web.lists.getByTitle("My List").fields.addLocation("My Field", { Group: "My Group" });
      -
      -// we can use this 'field' variable to run more queries on the field:
      -const r = await field.field.select("Id")();
      -
      -// log the field Id to console
      -console.log(r.Id);
      -
      -

      Delete a Field

      -

      Use the delete method to delete a field.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/fields";
      -
      -// delete one or more fields from web, returns boolean
      -const result = await sp.web.fields.getByTitle("My Field").delete();
      -const result2 = await sp.web.fields.getByTitle("My Field 2").delete();
      -// delete one or more fields from list 'My List', returns boolean
      -const result = await sp.web.lists.getByTitle("My List").fields.getByTitle("My Field").delete();
      -const result2 = await sp.web.lists.getByTitle("My List").fields.getByTitle("My Field 2").delete();
      -
      -

      Update a Field

      -

      Use the update method to update a field.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// update the field called 'My Field' with a description in web, returns FieldUpdateResult
      -const fieldUpdate = await sp.web.fields.getByTitle("My Field").update({ Description: "My Description" });
      -// update the field called 'My Field' with a description in list 'My List', returns FieldUpdateResult
      -const fieldUpdate2 = await sp.web.lists.getByTitle("My List").fields.getByTitle("My Field").update({ Description: "My Description" });
      -
      -// if you need to update a field with properties for a specific field type you can optionally include the field type as a second param
      -// if you do not include it we will look up the type, but that adds a call to the server
      -const fieldUpdate2 = await sp.web.lists.getByTitle("My List").fields.getByTitle("My Look up Field").update({ RelationshipDeleteBehavior: 1 }, "SP.FieldLookup");
      -
      -

      Show a Field in the Display Form

      -

      Use the setShowInDisplayForm method to add a field to the display form.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// show field called 'My Field' in display form throughout web
      -await sp.web.fields.getByTitle("My Field").setShowInDisplayForm(true);
      -// show field called 'My Field' in display form for list 'My List'
      -await sp.web.lists.getByTitle("My List").fields.getByTitle("My Field").setShowInDisplayForm(true);
      -
      -

      Show a Field in the Edit Form

      -

      Use the setShowInEditForm method to add a field to the edit form.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// show field called 'My Field' in edit form throughout web
      -await sp.web.fields.getByTitle("My Field").setShowInEditForm(true);
      -// show field called 'My Field' in edit form for list 'My List'
      -await sp.web.lists.getByTitle("My List").fields.getByTitle("My Field").setShowInEditForm(true);
      -
      -

      Show a Field in the New Form

      -

      Use the setShowInNewForm method to add a field to the display form.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/fields";
      -
      -// show field called 'My Field' in new form throughout web
      -await sp.web.fields.getByTitle("My Field").setShowInNewForm(true);
      -// show field called 'My Field' in new form for list 'My List'
      -await sp.web.lists.getByTitle("My List").fields.getByTitle("My Field").setShowInNewForm(true);
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/files/index.html b/docs/v3/v2/sp/files/index.html deleted file mode 100644 index 72572674f..000000000 --- a/docs/v3/v2/sp/files/index.html +++ /dev/null @@ -1,2949 +0,0 @@ - - - - - - - - - - - - - - - - - - Files - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      - -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/files

      -

      One of the more challenging tasks on the client side is working with SharePoint files, especially if they are large files. We have added some methods to the library to help and their use is outlined below.

      -

      Reading Files

      -

      Reading files from the client using REST is covered in the below examples. The important thing to remember is choosing which format you want the file in so you can appropriately process it. You can retrieve a file as Blob, Buffer, JSON, or Text. If you have a special requirement you could also write your own parser.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -import "@pnp/sp/folders";
      -
      -const blob: Blob = await sp.web.getFileByServerRelativeUrl("/sites/dev/documents/file.avi").getBlob();
      -
      -const buffer: ArrayBuffer = await sp.web.getFileByServerRelativeUrl("/sites/dev/documents/file.avi").getBuffer();
      -
      -const json: any = await sp.web.getFileByServerRelativeUrl("/sites/dev/documents/file.json").getJSON();
      -
      -const text: string = await sp.web.getFileByServerRelativeUrl("/sites/dev/documents/file.txt").getText();
      -
      -// all of these also work from a file object no matter how you access it
      -const text2: string = await sp.web.getFolderByServerRelativeUrl("/sites/dev/documents").files.getByName("file.txt").getText();
      -
      -

      getFileByUrl

      -

      Added in 2.0.4

      -

      This method supports opening files from sharing links or absolute urls. The file must reside in the site from which you are trying to open the file.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files/web";
      -
      -const url = "{absolute file url OR sharing url}";
      -
      -// file is an IFile and supports all the file operations
      -const file = sp.web.getFileByUrl(url);
      -
      -// for example
      -const fileContent = await file.getText();
      -
      -

      Adding Files

      -

      Likewise you can add files using one of two methods, add or addChunked. AddChunked is appropriate for larger files, generally larger than 10 MB but this may differ based on your bandwidth/latency so you can adjust the code to use the chunked method. The below example shows getting the file object from an input and uploading it to SharePoint, choosing the upload method based on file size.

      -
      declare var require: (s: string) => any;
      -
      -import { ConsoleListener, Logger, LogLevel } from "@pnp/logging";
      -import { sp } from "@pnp/sp";
      -import { Web } from "@pnp/sp/webs";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -import "@pnp/sp/folders";
      -import { auth } from "./auth";
      -let $ = require("jquery"); // <-- used here for illustration
      -
      -let siteUrl = "https://mytenant.sharepoint.com/sites/dev";
      -
      -// comment this out for non-node execution
      -// auth(siteUrl);
      -
      -Logger.subscribe(new ConsoleListener());
      -Logger.activeLogLevel = LogLevel.Verbose;
      -
      -let web = Web(siteUrl);
      -
      -$(() => {
      -    $("#testingdiv").append("<button id='thebuttontodoit'>Do It</button>");
      -
      -    $("#thebuttontodoit").on('click', async (e) => {
      -
      -        e.preventDefault();
      -
      -        let input = <HTMLInputElement>document.getElementById("thefileinput");
      -        let file = input.files[0];
      -
      -        // you can adjust this number to control what size files are uploaded in chunks
      -        if (file.size <= 10485760) {
      -
      -            // small upload
      -            await web.getFolderByServerRelativeUrl("/sites/dev/Shared%20Documents/test/").files.add(file.name, file, true);
      -            Logger.write("done");
      -        } else {
      -
      -            // large upload
      -            await web.getFolderByServerRelativeUrl("/sites/dev/Shared%20Documents/test/").files.addChunked(file.name, file, data => {
      -
      -                Logger.log({ data: data, level: LogLevel.Verbose, message: "progress" });
      -
      -            }, true);
      -            Logger.write("done!")
      -        }
      -    });
      -});
      -
      -

      Adding a file using Nodejs Streams

      -

      If you are working in nodejs you can also add a file using a stream. This example makes a copy of a file using streams.

      -
      // triggers auto-application of extensions, in this case to add getStream
      -import "@pnp/nodejs";
      -
      -// get a stream of an existing file
      -const sr = await sp.web.getFileByServerRelativePath("/sites/dev/shared documents/old.md").getStream();
      -
      -// now add the stream as a new file, remember to set the content-length header
      -const fr = await sp.web.lists.getByTitle("Documents").rootFolder.files.configure({
      -    headers: {
      -        "content-length": `${sr.knownLength}`,
      -    },
      -}).add("new.md", sr.body);
      -
      -

      Setting Associated Item Values

      -

      You can also update the file properties of a newly uploaded file using code similar to the below snippet:

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -import "@pnp/sp/folders";
      -
      -const file = await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared%20Documents/test/").files.add("file.name", "file", true);
      -const item = await file.file.getItem();
      -await item.update({
      -  Title: "A Title",
      -  OtherField: "My Other Value"
      -});
      -
      -

      AddUsingPath

      -

      If you need to support the percent or pound characters you can use the addUsingPath method of IFiles

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -import "@pnp/sp/folders";
      -
      -const file = await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared%20Documents/test/").files.addUsingPath("file%#%.name", "content");
      -
      -

      Update File Content

      -

      You can of course use similar methods to update existing files as shown below. This overwrites the existing content in the file.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -import "@pnp/sp/folders";
      -
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/documents/test.txt").setContent("New string content for the file.");
      -
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/documents/test.mp4").setContentChunked(file);
      -
      -

      Check in, Check out, and Approve & Deny

      -

      The library provides helper methods for checking in, checking out, and approving files. Examples of these methods are shown below.

      -

      Check In

      -

      Check in takes two optional arguments, comment and check in type.

      -
      import { sp } from "@pnp/sp";
      -import { CheckinType } from "@pnp/sp/files";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -
      -// default options with empty comment and CheckinType.Major
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").checkin();
      -console.log("File checked in!");
      -
      -// supply a comment (< 1024 chars) and using default check in type CheckinType.Major
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").checkin("A comment");
      -console.log("File checked in!");
      -
      -// Supply both comment and check in type
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").checkin("A comment", CheckinType.Overwrite);
      -console.log("File checked in!");
      -
      -

      Check Out

      -

      Check out takes no arguments.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -
      -sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").checkout();
      -console.log("File checked out!");
      -
      -

      Approve and Deny

      -

      You can also approve or deny files in libraries that use approval. Approve takes a single required argument of comment, the comment is optional for deny.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").approve("Approval Comment");
      -console.log("File approved!");
      -
      -// deny with no comment
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").deny();
      -console.log("File denied!");
      -
      -// deny with a supplied comment.
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").deny("Deny comment");
      -console.log("File denied!");
      -
      -

      Publish and Unpublish

      -

      You can both publish and unpublish a file using the library. Both methods take an optional comment argument.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -
      -// publish with no comment
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").publish();
      -console.log("File published!");
      -
      -// publish with a supplied comment.
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").publish("Publish comment");
      -console.log("File published!");
      -
      -// unpublish with no comment
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").unpublish();
      -console.log("File unpublished!");
      -
      -// unpublish with a supplied comment.
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").unpublish("Unpublish comment");
      -console.log("File unpublished!");
      -
      -

      Advanced Upload Options

      -

      Both the addChunked and setContentChunked methods support options beyond just supplying the file content.

      -

      progress function

      -

      A method that is called each time a chunk is uploaded and provides enough information to report progress or update a progress bar easily. The method has the signature:

      -

      (data: ChunkedFileUploadProgressData) => void

      -

      The data interface is:

      -
      export interface ChunkedFileUploadProgressData {
      -    stage: "starting" | "continue" | "finishing";
      -    blockNumber: number;
      -    totalBlocks: number;
      -    chunkSize: number;
      -    currentPointer: number;
      -    fileSize: number;
      -}
      -
      -

      chunkSize

      -

      This property controls the size of the individual chunks and is defaulted to 10485760 bytes (10 MB). You can adjust this based on your bandwidth needs - especially if writing code for mobile uploads or you are seeing frequent timeouts.

      -

      getItem

      -

      This method allows you to get the item associated with this file. You can optionally specify one or more select fields. The result will be merged with a new Item instance so you will have both the returned property values and chaining ability in a single object.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -import "@pnp/sp/folders";
      -import "@pnp/sp/security";
      -
      -const item = await sp.web.getFileByServerRelativePath("/sites/dev/Shared Documents/test.txt").getItem();
      -console.log(item);
      -
      -const item2 = await sp.web.getFileByServerRelativePath("/sites/dev/Shared Documents/test.txt").getItem("Title", "Modified");
      -console.log(item2);
      -
      -// you can also chain directly off this item instance
      -const perms = await item.getCurrentUserEffectivePermissions();
      -console.log(perms);
      -
      -

      You can also supply a generic typing parameter and the resulting type will be a union type of Item and the generic type parameter. This allows you to have proper intellisense and type checking.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -import "@pnp/sp/folders";
      -import "@pnp/sp/items";
      -import "@pnp/sp/security";
      -
      -// also supports typing the objects so your type will be a union type
      -const item = await sp.web.getFileByServerRelativePath("/sites/dev/Shared Documents/test.txt").getItem<{ Id: number, Title: string }>("Id", "Title");
      -
      -// You get intellisense and proper typing of the returned object
      -console.log(`Id: ${item.Id} -- ${item.Title}`);
      -
      -// You can also chain directly off this item instance
      -const perms = await item.getCurrentUserEffectivePermissions();
      -console.log(perms);
      -
      -

      move

      -

      It's possible to move a file to a new destination within a site collection

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -
      -// destination is a server-relative url of a new file
      -const destinationUrl = `/sites/dev/SiteAssets/new-file.docx`;
      -
      -await sp.web.getFileByServerRelativePath("/sites/dev/Shared Documents/test.docx").moveTo(destinationUrl);
      -
      -

      copy

      -

      It's possible to copy a file to a new destination within a site collection

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -
      -// destination is a server-relative url of a new file
      -const destinationUrl = `/sites/dev/SiteAssets/new-file.docx`;
      -
      -await sp.web.getFileByServerRelativePath("/sites/dev/Shared Documents/test.docx").copyTo(destinationUrl, false);
      -
      -

      move by path

      -

      It's possible to move a file to a new destination within the same or a different site collection

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -
      -// destination is a server-relative url of a new file
      -const destinationUrl = `/sites/dev2/SiteAssets/new-file.docx`;
      -
      -await sp.web.getFileByServerRelativePath("/sites/dev/Shared Documents/test.docx").moveByPath(destinationUrl, false, true);
      -
      -

      copy by path

      -

      It's possible to copy a file to a new destination within the same or a different site collection

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -
      -// destination is a server-relative url of a new file
      -const destinationUrl = `/sites/dev2/SiteAssets/new-file.docx`;
      -
      -await sp.web.getFileByServerRelativePath("/sites/dev/Shared Documents/test.docx").copyByPath(destinationUrl, false, true);
      -
      -

      getFileById

      -

      You can get a file by Id from a web.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -import { IFile } from "@pnp/sp/files";
      -
      -const file: IFile = sp.web.getFileById("2b281c7b-ece9-4b76-82f9-f5cf5e152ba0");
      -
      -

      delete

      -

      Deletes a file

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -
      -await sp.web.rootFolder.files.getByName("name.txt").delete();
      -
      -

      delete with params

      -

      Added in 2.0.9

      -

      Deletes a file with options

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -
      -await sp.web.rootFolder.files.getByName("name.txt").deleteWithParams({
      -    BypassSharedLock: true,
      -});
      -
      -

      exists

      -

      Added in 2.0.9

      -

      Checks to see if a file exists

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/files";
      -
      -const exists = await sp.web.rootFolder.files.getByName("name.txt").exists();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/folders/index.html b/docs/v3/v2/sp/folders/index.html deleted file mode 100644 index 35ed45650..000000000 --- a/docs/v3/v2/sp/folders/index.html +++ /dev/null @@ -1,2968 +0,0 @@ - - - - - - - - - - - - - - - - - - Folders - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/folders

      -

      Folders serve as a container for your files and list items.

      -

      IFolders

      -

      Invokable Banner Selective Imports Banner

      -

      Represents a collection of folders. SharePoint webs, lists, and list items have a collection of folders under their properties.

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import { IFolders, Folders } from "@pnp/sp/folders";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/folders";
      Selective 3import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/folders/web";
      Selective 4import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/folders/list";
      Selective 5import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/folders/list";
      import "@pnp/sp/folders/item";
      Preset: Allimport { sp, IFolders, Folders } from "@pnp/sp/presets/all";
      -

      Get folders collection for various SharePoint objects

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/items";
      -import "@pnp/sp/folders";
      -import "@pnp/sp/lists";
      -
      -// gets web's folders
      -const webFolders = await sp.web.folders();
      -
      -// gets list's folders
      -const listFolders = await sp.web.lists.getByTitle("My List").rootFolder.folders();
      -
      -// gets item's folders
      -const itemFolders = await sp.web.lists.getByTitle("My List").items.getById(1).folder.folders();
      -
      -

      add

      -

      Adds a new folder to collection of folders

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -// creates a new folder for web with specified url
      -const folderAddResult = await sp.web.folders.add("folder url");
      -
      -

      getByName

      -

      Gets a folder instance from a collection by folder's name

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -const folder = await sp.web.folders.getByName("folder name")();
      -
      -

      IFolder

      -

      Represents an instance of a SharePoint folder.

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import { IFolders, Folders } from "@pnp/sp/folders";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/folders";
      Selective 3import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/folders/web";
      Selective 4import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/folders/list";
      Selective 5import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/folders/list";
      import "@pnp/sp/folders/item";
      Preset: Allimport { sp, IFolders, Folders } from "@pnp/sp/presets/all";
      -

      Get a folder object associated with different SharePoint artifacts (web, list, list item)

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -
      -// web's folder
      -const rootFolder = await sp.web.rootFolder();
      -
      -// list's folder
      -const listRootFolder = await sp.web.lists.getByTitle("234").rootFolder();
      -
      -// item's folder
      -const itemFolder = await sp.web.lists.getByTitle("234").items.getById(1).folder();
      -
      -

      getItem

      -

      Gets list item associated with a folder

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -const folderItem = await sp.web.rootFolder.folders.getByName("SiteAssets").folders.getByName("My Folder").getItem();
      -
      -

      move

      -

      It's possible to move a folder to a new destination within a site collection

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -// destination is a server-relative url of a new folder
      -const destinationUrl = `/sites/my-site/SiteAssets/new-folder`;
      -
      -await sp.web.rootFolder.folders.getByName("SiteAssets").folders.getByName("My Folder").moveTo(destinationUrl);
      -
      -

      copy

      -

      It's possible to copy a folder to a new destination within a site collection

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -// destination is a server-relative url of a new folder
      -const destinationUrl = `/sites/my-site/SiteAssets/new-folder`;
      -
      -await sp.web.rootFolder.folders.getByName("SiteAssets").folders.getByName("My Folder").copyTo(destinationUrl);
      -
      -

      move by path

      -

      It's possible to move a folder to a new destination within the same or a different site collection

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -// destination is a server-relative url of a new folder
      -const destinationUrl = `/sites/my-site/SiteAssets/new-folder`;
      -
      -await sp.web.rootFolder.folders.getByName("SiteAssets").folders.getByName("My Folder").moveByPath(destinationUrl, true);
      -
      -

      copy by path

      -

      It's possible to copy a folder to a new destination within the same or a different site collection

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -// destination is a server-relative url of a new folder
      -const destinationUrl = `/sites/my-site/SiteAssets/new-folder`;
      -
      -await sp.web.rootFolder.folders.getByName("SiteAssets").folders.getByName("My Folder").copyByPath(destinationUrl, true);
      -
      -

      delete

      -

      Deletes a folder

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -await sp.web.rootFolder.folders.getByName("My Folder").delete();
      -
      -

      delete with params

      -

      Added in 2.0.9

      -

      Deletes a folder with options

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -await sp.web.rootFolder.folders.getByName("My Folder").deleteWithParams({
      -                BypassSharedLock: true,
      -                DeleteIfEmpty: true,
      -            });
      -
      -

      recycle

      -

      Recycles a folder

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -await sp.web.rootFolder.folders.getByName("My Folder").recycle();
      -
      -

      serverRelativeUrl

      -

      Gets folder's server relative url

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -const relUrl = await sp.web.rootFolder.folders.getByName("SiteAssets").serverRelativeUrl();
      -
      -

      update

      -

      Updates folder's properties

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -await sp.web.getFolderByServerRelativePath("Shared Documents/Folder2").update({
      -        "Name": "New name",
      -    });
      -
      -

      contentTypeOrder

      -

      Gets content type order of a folder

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -const order = await sp.web.getFolderByServerRelativePath("Shared Documents").contentTypeOrder();
      -
      -

      folders

      -

      Gets all child folders associated with the current folder

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -const folders = await sp.web.rootFolder.folders();
      -
      -

      files

      -

      Gets all files inside a folder

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -import "@pnp/sp/files/folder";
      -
      -const files = await sp.web.getFolderByServerRelativePath("Shared Documents").files();
      -
      -

      listItemAllFields

      -

      Gets this folder's list item field values

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -const itemFields = await sp.web.getFolderByServerRelativePath("Shared Documents/My Folder").listItemAllFields();
      -
      -

      parentFolder

      -

      Gets the parent folder, if available

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -const parentFolder = await sp.web.getFolderByServerRelativePath("Shared Documents/My Folder").parentFolder();
      -
      -

      properties

      -

      Gets this folder's properties

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -const properties = await sp.web.getFolderByServerRelativePath("Shared Documents/Folder2").properties();
      -
      -

      uniqueContentTypeOrder

      -

      Gets a value that specifies the content type order.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -const contentTypeOrder = await sp.web.getFolderByServerRelativePath("Shared Documents/Folder2").uniqueContentTypeOrder();
      -
      -

      Rename a folder

      -

      You can rename a folder by updating FileLeafRef property:

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -const folder = sp.web.getFolderByServerRelativePath("Shared Documents/My Folder");
      -
      -const item = await folder.getItem();
      -const result = await item.update({ FileLeafRef: "Folder2" });
      -
      -

      Create a folder with custom content type

      -

      Below code creates a new folder under Document library and assigns custom folder content type to a newly created folder. Additionally it sets a field of a custom folder content type.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/items";
      -import "@pnp/sp/folders";
      -import "@pnp/sp/lists";
      -
      -const newFolderResult = await sp.web.rootFolder.folders.getByName("Shared Documents").folders.add("My New Folder");
      -const item = await newFolderResult.folder.listItemAllFields();
      -
      -await sp.web.lists.getByTitle("Documents").items.getById(item.ID).update({
      -    ContentTypeId: "0x0120001E76ED75A3E3F3408811F0BF56C4CDDD",
      -    MyFolderField: "field value",
      -    Title: "My New Folder",
      -});
      -
      -

      addSubFolderUsingPath

      -

      Added in 2.0.9

      -

      You can use the addSubFolderUsingPath method to add a folder with some special chars supported

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -import { IFolder } from "@pnp/sp/folders";
      -
      -// add a folder to site assets
      -const folder: IFolder = await web.rootFolder.folders.getByName("SiteAssets").addSubFolderUsingPath("folder name");
      -
      -

      getFolderById

      -

      You can get a folder by Id from a web.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -import { IFolder } from "@pnp/sp/folders";
      -
      -const folder: IFolder = sp.web.getFolderById("2b281c7b-ece9-4b76-82f9-f5cf5e152ba0");
      -
      -

      getParentInfos

      -

      Added in 2.0.12

      -

      Gets information about folder, including details about the parent list, parent list root folder, and parent web.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/folders";
      -
      -const folder: IFolder = sp.web.getFolderById("2b281c7b-ece9-4b76-82f9-f5cf5e152ba0");
      -await folder.getParentInfos();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/forms/index.html b/docs/v3/v2/sp/forms/index.html deleted file mode 100644 index c72177b9b..000000000 --- a/docs/v3/v2/sp/forms/index.html +++ /dev/null @@ -1,2289 +0,0 @@ - - - - - - - - - - - - - - - - - - Forms - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/forms

      -

      Forms in SharePoint are the Display, New, and Edit forms associated with a list.

      -

      IFields

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import { Webs, IWebs } from "@pnp/sp/webs";
      import "@pnp/sp/forms";
      import "@pnp/sp/lists";
      -

      Get Form by Id

      -

      Gets a form from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/forms";
      -import "@pnp/sp/lists";
      -
      -// get the field by Id for web
      -const form = sp.web.lists.getByTitle("Documents").forms.getById("{c4486774-f1e2-4804-96f3-91edf3e22a19}")();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/hubsites/index.html b/docs/v3/v2/sp/hubsites/index.html deleted file mode 100644 index aa86b669f..000000000 --- a/docs/v3/v2/sp/hubsites/index.html +++ /dev/null @@ -1,2485 +0,0 @@ - - - - - - - - - - - - - - - - - - Hubsites - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/hubsites

      -

      This module helps you with working with hub sites in your tenant.

      -

      IHubSites

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selectiveimport { sp } from "@pnp/sp";
      import "@pnp/sp/hubsites";
      Preset: Allimport { sp, HubSites, IHubSites } from "@pnp/sp/presets/all";
      -

      Get a Listing of All Hub sites

      -
      import { sp } from "@pnp/sp";
      -import { IHubSiteInfo } from  "@pnp/sp/hubsites";
      -import "@pnp/sp/hubsites";
      -
      -// invoke the hub sites object
      -const hubsites: IHubSiteInfo[] = await sp.hubSites();
      -
      -// you can also use select to only return certain fields:
      -const hubsites2: IHubSiteInfo[] = await sp.hubSites.select("ID", "Title", "RelatedHubSiteIds")();
      -
      -

      Get Hub site by Id

      -

      Using the getById method on the hubsites module to get a hub site by site Id (guid).

      -
      import { sp } from "@pnp/sp";
      -import { IHubSiteInfo } from  "@pnp/sp/hubsites";
      -import "@pnp/sp/hubsites";
      -
      -const hubsite: IHubSiteInfo = await sp.hubSites.getById("3504348e-b2be-49fb-a2a9-2d748db64beb")();
      -
      -// log hub site title to console
      -console.log(hubsite.Title);
      -
      -

      Get ISite instance

      -

      We provide a helper method to load the ISite instance from the HubSite

      -
      import { sp } from "@pnp/sp";
      -import { ISite } from  "@pnp/sp/sites";
      -import "@pnp/sp/hubsites";
      -
      -const site: ISite = await sp.hubSites.getById("3504348e-b2be-49fb-a2a9-2d748db64beb").getSite();
      -
      -const siteData = await site();
      -
      -console.log(siteData.Title);
      -
      -

      Get Hub site data for a web

      -
      import { sp } from "@pnp/sp";
      -import { IHubSiteWebData } from  "@pnp/sp/hubsites";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/hubsites/web";
      -
      -const webData: Partial<IHubSiteWebData> = await sp.web.hubSiteData();
      -
      -// you can also force a refresh of the hub site data
      -const webData2: Partial<IHubSiteWebData> = await sp.web.hubSiteData(true);
      -
      -

      syncHubSiteTheme

      -

      Allows you to apply theme updates from the parent hub site collection.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/hubsites/web";
      -
      -await sp.web.syncHubSiteTheme();
      -
      -

      Hub site Site Methods

      -

      You manage hub sites at the Site level.

      -

      joinHubSite

      -

      Id of the hub site collection you want to join. If you want to disassociate the site collection from hub site, then pass the siteId as 00000000-0000-0000-0000-000000000000

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/sites";
      -import "@pnp/sp/hubsites/site";
      -
      -// join a site to a hub site
      -await sp.site.joinHubSite("{parent hub site id}");
      -
      -// remove a site from a hub site
      -await sp.site.joinHubSite("00000000-0000-0000-0000-000000000000");
      -
      -

      registerHubSite

      -

      Registers the current site collection as hub site collection

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/sites";
      -import "@pnp/sp/hubsites/site";
      -
      -// register current site as a hub site
      -await sp.site.registerHubSite();
      -
      -

      unRegisterHubSite

      -

      Un-registers the current site collection as hub site collection.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/sites";
      -import "@pnp/sp/hubsites/site";
      -
      -// make a site no longer a hub
      -await sp.site.unRegisterHubSite();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/index.html b/docs/v3/v2/sp/index.html deleted file mode 100644 index bc732bbb2..000000000 --- a/docs/v3/v2/sp/index.html +++ /dev/null @@ -1,2328 +0,0 @@ - - - - - - - - - - - - - - - - - - sp - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp

      -

      npm version

      -

      This package contains the fluent api used to call the SharePoint rest services.

      -

      Getting Started

      -

      Install the library and required dependencies

      -

      npm install @pnp/sp --save

      -

      Import the library into your application and access the root sp object

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -
      -(function main() {
      -
      -    // here we will load the current web's title
      -    const w = await sp.web.select("Title")();
      -    console.log(`Web Title: ${w.Title}`);
      -)()
      -
      -

      Getting Started: SharePoint Framework

      -

      Install the library and required dependencies

      -

      npm install @pnp/sp --save

      -

      Import the library into your application, update OnInit, and access the root sp object in render

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -
      -// ...
      -
      -public onInit(): Promise<void> {
      -
      -  return super.onInit().then(_ => {
      -
      -    // other init code may be present
      -
      -    sp.setup({
      -      spfxContext: this.context
      -    });
      -  });
      -}
      -
      -// ...
      -
      -public render(): void {
      -
      -    // A simple loading message
      -    this.domElement.innerHTML = `Loading...`;
      -
      -    const w = await sp.web.select("Title")();
      -    this.domElement.innerHTML = `Web Title: ${w.Title}`;
      -}
      -
      -

      Getting Started: Nodejs

      -

      Install the library and required dependencies

      -

      npm install @pnp/sp @pnp/nodejs --save

      -

      Import the library into your application, setup the node client, make a request

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import { SPFetchClient } from "@pnp/nodejs";
      -
      -// do this once per page load
      -sp.setup({
      -    sp: {
      -        fetchClientFactory: () => {
      -            return new SPFetchClient("{your site url}", "{your client id}", "{your client secret}");
      -        },
      -    },
      -});
      -
      -// now make any calls you need using the configured client
      -
      -const w = await sp.web.select("Title")();
      -console.log(`Web Title: ${w.Title}`);
      -
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/items/index.html b/docs/v3/v2/sp/items/index.html deleted file mode 100644 index 002e57ca7..000000000 --- a/docs/v3/v2/sp/items/index.html +++ /dev/null @@ -1,2974 +0,0 @@ - - - - - - - - - - - - - - - - - - List Items - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/items

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/lists";
      import "@pnp/sp/items";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      Preset: Coreimport { sp } from "@pnp/sp/presets/core";
      -

      GET

      -

      Getting items from a list is one of the basic actions that most applications require. This is made easy through the library and the following examples demonstrate these actions.

      -

      Basic Get

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -
      -// get all the items from a list
      -const items: any[] = await sp.web.lists.getByTitle("My List").items();
      -console.log(items);
      -
      -// get a specific item by id.
      -const item: any = await sp.web.lists.getByTitle("My List").items.getById(1)();
      -console.log(item);
      -
      -// use odata operators for more efficient queries
      -const items2: any[] = await sp.web.lists.getByTitle("My List").items.select("Title", "Description").top(5).orderBy("Modified", true)();
      -console.log(items2);
      -
      -

      Get Paged Items

      -

      Working with paging can be a challenge as it is based on skip tokens and item ids, something that is hard to guess at runtime. To simplify things you can use the getPaged method on the Items class to assist. Note that there isn't a way to move backwards in the collection, this is by design. The pattern you should use to support backwards navigation in the results is to cache the results into a local array and use the standard array operators to get previous pages. Alternatively you can append the results to the UI, but this can have performance impact for large result sets.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -
      -// basic case to get paged items form a list
      -let items = await sp.web.lists.getByTitle("BigList").items.getPaged();
      -
      -// you can also provide a type for the returned values instead of any
      -let items = await sp.web.lists.getByTitle("BigList").items.getPaged<{Title: string}[]>();
      -
      -// the query also works with select to choose certain fields and top to set the page size
      -let items = await sp.web.lists.getByTitle("BigList").items.select("Title", "Description").top(50).getPaged<{Title: string}[]>();
      -
      -// the results object will have two properties and one method:
      -
      -// the results property will be an array of the items returned
      -if (items.results.length > 0) {
      -    console.log("We got results!");
      -
      -    for (let i = 0; i < items.results.length; i++) {
      -        // type checking works here if we specify the return type
      -        console.log(items.results[i].Title);
      -    }
      -}
      -
      -// the hasNext property is used with the getNext method to handle paging
      -// hasNext will be true so long as there are additional results
      -if (items.hasNext) {
      -
      -    // this will carry over the type specified in the original query for the results array
      -    items = await items.getNext();
      -    console.log(items.results.length);
      -}
      -
      -

      getListItemChangesSinceToken

      -

      The GetListItemChangesSinceToken method allows clients to track changes on a list. Changes, including deleted items, are returned along with a token that represents the moment in time when those changes were requested. By including this token when you call GetListItemChangesSinceToken, the server looks for only those changes that have occurred since the token was generated. Sending a GetListItemChangesSinceToken request without including a token returns the list schema, the full list contents and a token.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -
      -// Using RowLimit. Enables paging
      -let changes = await sp.web.lists.getByTitle("BigList").getListItemChangesSinceToken({RowLimit: '5'});
      -
      -// Use QueryOptions to make a XML-style query.
      -// Because it's XML we need to escape special characters
      -// Instead of & we use &amp; in the query
      -let changes = await sp.web.lists.getByTitle("BigList").getListItemChangesSinceToken({QueryOptions: '<Paging ListItemCollectionPositionNext="Paged=TRUE&amp;p_ID=5" />'});
      -
      -// Get everything. Using null with ChangeToken gets everything
      -let changes = await sp.web.lists.getByTitle("BigList").getListItemChangesSinceToken({ChangeToken: null});
      -
      -
      -

      Get All Items

      -

      Using the items collection's getAll method you can get all of the items in a list regardless of the size of the list. Sample usage is shown below. Only the odata operations top, select, and filter are supported. usingCaching and inBatch are ignored - you will need to handle caching the results on your own. This method will write a warning to the Logger and should not frequently be used. Instead the standard paging operations should be used.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -
      -// basic usage
      -const allItems: any[] = await sp.web.lists.getByTitle("BigList").items.getAll();
      -console.log(allItems.length);
      -
      -// set page size
      -const allItems: any[] = await sp.web.lists.getByTitle("BigList").items.getAll(4000);
      -console.log(allItems.length);
      -
      -// use select and top. top will set page size and override the any value passed to getAll
      -const allItems: any[] = await sp.web.lists.getByTitle("BigList").items.select("Title").top(4000).getAll();
      -console.log(allItems.length);
      -
      -// we can also use filter as a supported odata operation, but this will likely fail on large lists
      -const allItems: any[] = await sp.web.lists.getByTitle("BigList").items.select("Title").filter("Title eq 'Test'").getAll();
      -console.log(allItems.length);
      -
      -

      Retrieving Lookup Fields

      -

      When working with lookup fields you need to use the expand operator along with select to get the related fields from the lookup column. This works for both the items collection and item instances.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -
      -const items = await sp.web.lists.getByTitle("LookupList").items.select("Title", "Lookup/Title", "Lookup/ID").expand("Lookup")();
      -console.log(items);
      -
      -const item = await sp.web.lists.getByTitle("LookupList").items.getById(1).select("Title", "Lookup/Title", "Lookup/ID").expand("Lookup")();
      -console.log(item);
      -
      -

      Filter using Metadata fields

      -

      To filter on a metadata field you must use the getItemsByCAMLQuery method as $filter does not support these fields.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -
      -const r = await sp.web.lists.getByTitle("TaxonomyList").getItemsByCAMLQuery({
      -    ViewXml: `<View><Query><Where><Eq><FieldRef Name="MetaData"/><Value Type="TaxonomyFieldType">Term 2</Value></Eq></Where></Query></View>`,
      -});
      -
      -

      Retrieving PublishingPageImage

      -

      The PublishingPageImage and some other publishing-related fields aren't stored in normal fields, rather in the MetaInfo field. To get these values you need to use the technique shown below, and originally outlined in this thread. Note that a lot of information can be stored in this field so will pull back potentially a significant amount of data, so limit the rows as possible to aid performance.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -import { Web } from "@pnp/sp/webs";
      -try {
      -  const w = Web("https://{publishing site url}");
      -  const r = await w.lists.getByTitle("Pages").items
      -    .select("Title", "FileRef", "FieldValuesAsText/MetaInfo")
      -    .expand("FieldValuesAsText")
      -    ();
      -
      -  // look through the returned items.
      -  for (var i = 0; i < r.length; i++) {
      -
      -    // the title field value
      -    console.log(r[i].Title);
      -
      -    // find the value in the MetaInfo string using regex
      -    const matches = /PublishingPageImage:SW\|(.*?)\r\n/ig.exec(r[i].FieldValuesAsText.MetaInfo);
      -    if (matches !== null && matches.length > 1) {
      -
      -      // this wil be the value of the PublishingPageImage field
      -      console.log(matches[1]);
      -    }
      -  }
      -}
      -catch (e) {
      -  console.error(e);
      -}
      -
      -

      Add Items

      -

      There are several ways to add items to a list. The simplest just uses the add method of the items collection passing in the properties as a plain object.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -import { IItemAddResult } from "@pnp/sp/items";
      -
      -// add an item to the list
      -const iar: IItemAddResult = await sp.web.lists.getByTitle("My List").items.add({
      -  Title: "Title",
      -  Description: "Description"
      -});
      -
      -console.log(iar);
      -
      -

      Content Type

      -

      You can also set the content type id when you create an item as shown in the example below. For more information on content type IDs reference the Microsoft Documentation. While this documentation references SharePoint 2010 the structure of the IDs has not changed.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -
      -await sp.web.lists.getById("4D5A36EA-6E84-4160-8458-65C436DB765C").items.add({
      -    Title: "Test 1",
      -    ContentTypeId: "0x01030058FD86C279252341AB303852303E4DAF"
      -});
      -
      -

      User Fields

      -

      There are two types of user fields, those that allow a single value and those that allow multiple. For both types, you first need to determine the Id field name, which you can do by doing a GET REST request on an existing item. Typically the value will be the user field internal name with "Id" appended. So in our example, we have two fields User1 and User2 so the Id fields are User1Id and User2Id.

      -

      Next, you need to remember there are two types of user fields, those that take a single value and those that allow multiple - these are updated in different ways. For single value user fields you supply just the user's id. For multiple value fields, you need to supply an object with a "results" property and an array. Examples for both are shown below.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -import { getGUID } from "@pnp/core";
      -
      -const i = await sp.web.lists.getByTitle("PeopleFields").items.add({
      -  Title: getGUID(),
      -  User1Id: 9, // allows a single user
      -  User2Id: {
      -    results: [16, 45] // allows multiple users
      -  }
      -});
      -
      -console.log(i);
      -
      -

      If you want to update or add user field values when using validateUpdateListItem you need to use the form shown below. You can specify multiple values in the array.

      -
      import { sp } from "@pnp/sp";
      -
      -const result = await sp.web.lists.getByTitle("UserFieldList").items.getById(1).validateUpdateListItem([{
      -    FieldName: "UserField",
      -    FieldValue: JSON.stringify([{ "Key": "i:0#.f|membership|person@tenant.com" }]),
      -},
      -{
      -    FieldName: "Title",
      -    FieldValue: "Test - Updated",
      -}]);
      -
      -

      Lookup Fields

      -

      What is said for User Fields is, in general, relevant to Lookup Fields:

      -
        -
      • Lookup Field types:
      • -
      • Single-valued lookup
      • -
      • Multiple-valued lookup
      • -
      • Id suffix should be appended to the end of lookups EntityPropertyName in payloads
      • -
      • Numeric Ids for lookups' items should be passed as values
      • -
      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -import { getGUID } from "@pnp/core";
      -
      -await sp.web.lists.getByTitle("LookupFields").items.add({
      -    Title: getGUID(),
      -    LookupFieldId: 2,       // allows a single lookup value
      -    MultiLookupFieldId: {
      -        results: [ 1, 56 ]  // allows multiple lookup value
      -    }
      -});
      -
      -

      Add Multiple Items

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -
      -let list = sp.web.lists.getByTitle("rapidadd");
      -
      -const entityTypeFullName = await list.getListItemEntityTypeFullName()
      -
      -let batch = sp.web.createBatch();
      -
      -list.items.inBatch(batch).add({ Title: "Batch 6" }, entityTypeFullName).then(b => {
      -  console.log(b);
      -});
      -
      -list.items.inBatch(batch).add({ Title: "Batch 7" }, entityTypeFullName).then(b => {
      -  console.log(b);
      -});
      -
      -await batch.execute();
      -console.log("Done");
      -
      -

      Update

      -

      The update method is very similar to the add method in that it takes a plain object representing the fields to update. The property names are the internal names of the fields. If you aren't sure you can always do a get request for an item in the list and see the field names that come back - you would use these same names to update the item.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -
      -let list = sp.web.lists.getByTitle("MyList");
      -
      -const i = await list.items.getById(1).update({
      -  Title: "My New Title",
      -  Description: "Here is a new description"
      -});
      -
      -console.log(i);
      -
      -

      Getting and updating a collection using filter

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -
      -// you are getting back a collection here
      -const items: any[] = await sp.web.lists.getByTitle("MyList").items.top(1).filter("Title eq 'A Title'")();
      -
      -// see if we got something
      -if (items.length > 0) {
      -  const updatedItem = await sp.web.lists.getByTitle("MyList").items.getById(items[0].Id).update({
      -    Title: "Updated Title",
      -  });
      -
      -  console.log(JSON.stringify(updatedItem));
      -}
      -
      -

      Update Multiple Items

      -

      This approach avoids multiple calls for the same list's entity type name.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -
      -let list = sp.web.lists.getByTitle("rapidupdate");
      -
      -const entityTypeFullName = await list.getListItemEntityTypeFullName()
      -
      -let batch = sp.web.createBatch();
      -
      -// note requirement of "*" eTag param - or use a specific eTag value as needed
      -list.items.getById(1).inBatch(batch).update({ Title: "Batch 6" }, "*", entityTypeFullName).then(b => {
      -  console.log(b);
      -});
      -
      -list.items.getById(2).inBatch(batch).update({ Title: "Batch 7" }, "*", entityTypeFullName).then(b => {
      -  console.log(b);
      -});
      -
      -await batch.execute();
      -console.log("Done")
      -
      -
      -

      Recycle

      -

      To send an item to the recycle bin use recycle.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -
      -let list = sp.web.lists.getByTitle("MyList");
      -
      -const recycleBinIdentifier = await list.items.getById(1).recycle();
      -
      -

      Delete

      -

      Delete is as simple as calling the .delete method. It optionally takes an eTag if you need to manage concurrency.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -
      -let list = sp.web.lists.getByTitle("MyList");
      -
      -await list.items.getById(1).delete();
      -
      -

      Delete With Params

      -

      Added in 2.0.9

      -

      Deletes the item object with options.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -
      -let list = sp.web.lists.getByTitle("MyList");
      -
      -await list.items.getById(1).deleteWithParams({
      -                BypassSharedLock: true,
      -            });
      -
      -
      -

      The deleteWithParams method can only be used by accounts where UserToken.IsSystemAccount is true

      -
      -

      Resolving field names

      -

      It's a very common mistake trying wrong field names in the requests. -Field's EntityPropertyName value should be used.

      -

      The easiest way to get know EntityPropertyName is to use the following snippet:

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/items";
      -import "@pnp/sp/fields";
      -
      -const response =
      -  await sp.web.lists
      -    .getByTitle('[Lists_Title]')
      -    .fields
      -    .select('Title, EntityPropertyName')
      -    .filter(`Hidden eq false and Title eq '[Field's_Display_Name]'`)
      -    ();
      -
      -console.log(response.map(field => {
      -  return {
      -    Title: field.Title,
      -    EntityPropertyName: field.EntityPropertyName
      -  };
      -}));
      -
      -

      Lookup fields' names should be ended with additional Id suffix. E.g. for Editor EntityPropertyName EditorId should be used.

      -

      getParentInfos

      -

      Added in 2.0.12

      -

      Gets information about an item, including details about the parent list, parent list root folder, and parent web.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/items";
      -
      -const item: any = await sp.web.lists.getByTitle("My List").items.getById(1)();
      -await item.getParentInfos();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/lists/index.html b/docs/v3/v2/sp/lists/index.html deleted file mode 100644 index a11f5a42b..000000000 --- a/docs/v3/v2/sp/lists/index.html +++ /dev/null @@ -1,3387 +0,0 @@ - - - - - - - - - - - - - - - - - - Lists - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/lists

      -

      Lists in SharePoint are collections of information built in a structural way using columns and rows. Columns for metadata, and rows representing each entry. Visually, it reminds us a lot of a database table or an Excel spreadsheet.

      -

      ILists

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import { Webs, IWebs } from "@pnp/sp/webs";
      import { Lists, ILists } from "@pnp/sp/lists";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/lists";
      Preset: Allimport { sp, Lists, ILists } from "@pnp/sp/presets/all";
      Preset: Coreimport { sp, Lists, ILists } from "@pnp/sp/presets/core";
      -

      Get List by Id

      -

      Gets a list from the collection by id (guid). Note that the library will handle a guid formatted with curly braces (i.e. '{03b05ff4-d95d-45ed-841d-3855f77a2483}') as well as without curly braces (i.e. '03b05ff4-d95d-45ed-841d-3855f77a2483'). The Id parameter is also case insensitive.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -
      -// get the list by Id
      -const list = sp.web.lists.getById("03b05ff4-d95d-45ed-841d-3855f77a2483");
      -
      -// we can use this 'list' variable to execute more queries on the list:
      -const r = await list.select("Title")();
      -
      -// show the response from the server
      -console.log(r.Title);
      -
      -

      Get List by Title

      -

      You can also get a list from the collection by title.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -
      -// get the default document library 'Documents'
      -const list = sp.web.lists.getByTitle("Documents");
      -
      -// we can use this 'list' variable to run more queries on the list:
      -const r = await list.select("Id")();
      -
      -// log the list Id to console
      -console.log(r.Id);
      -
      -

      Add List

      -

      You can add a list to the web's list collection using the .add-method. To invoke this method in its most simple form, you can provide only a title as a parameter. This will result in a standard out of the box list with all default settings, and the title you provide.

      -
      // create a new list, passing only the title
      -const listAddResult = await sp.web.lists.add("My new list");
      -
      -// we can work with the list created using the IListAddResult.list property:
      -const r = await listAddResult.list.select("Title")();
      -
      -// log newly created list title to console
      -console.log(r.Title);
      -});
      -
      -

      You can also provide other (optional) parameters like description, template and enableContentTypes. If that is not enough for you, you can use the parameter named 'additionalSettings' which is just a TypedHash, meaning you can sent whatever properties you'd like in the body (provided that the property is supported by the SharePoint API). You can find a listing of list template codes in the official docs.

      -
      // this will create a list with template 101 (Document library), content types enabled and show it on the quick launch (using additionalSettings)
      -const listAddResult = await sp.web.lists.add("My Doc Library", "This is a description of doc lib.", 101, true, { OnQuickLaunch: true });
      -
      -// get the Id of the newly added document library
      -const r = await listAddResult.list.select("Id")();
      -
      -// log id to console
      -console.log(r.Id);
      -
      -

      Ensure that a List exists (by title)

      -

      Ensures that the specified list exists in the collection (note: this method not supported for batching). Just like with the add-method (see examples above) you can provide only the title, or any or all of the optional parameters desc, template, enableContentTypes and additionalSettings.

      -
      // ensure that a list exists. If it doesn't it will be created with the provided title (the rest of the settings will be default):
      -const listEnsureResult = await sp.web.lists.ensure("My List");
      -
      -// check if the list was created, or if it already existed:
      -if (listEnsureResult.created) {
      -    console.log("My List was created!");
      -} else {
      -    console.log("My List already existed!");
      -}
      -
      -// work on the created/updated list
      -const r = await listEnsureResult.list.select("Id")();
      -
      -// log the Id
      -console.log(r.Id);
      -
      -

      If the list already exists, the other settings you provide will be used to update the existing list.

      -
      // add a new list to the lists collection of the web
      -sp.web.lists.add("My List 2").then(async () => {
      -
      -// then call ensure on the created list with an updated description
      -const listEnsureResult = await sp.web.lists.ensure("My List 2", "Updated description");
      -
      -// get the updated description
      -const r = await listEnsureResult.list.select("Description")();
      -
      -// log the updated description
      -console.log(r.Description);
      -});
      -
      -

      Ensure Site Assets Library exist

      -

      Gets a list that is the default asset location for images or other files, which the users upload to their wiki pages.

      -
      // get Site Assets library
      -const siteAssetsList = await sp.web.lists.ensureSiteAssetsLibrary();
      -
      -// get the Title
      -const r = await siteAssetsList.select("Title")();
      -
      -// log Title
      -console.log(r.Title);
      -
      -

      Ensure Site Pages Library exist

      -

      Gets a list that is the default location for wiki pages.

      -
      // get Site Pages library
      -const siteAssetsList = await sp.web.lists.ensureSitePagesLibrary();
      -
      -// get the Title
      -const r = await siteAssetsList.select("Title")();
      -
      -// log Title
      -console.log(r.Title);
      -
      -

      IList

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import { List, IList } from "@pnp/sp/lists";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/lists";
      Preset: Allimport { sp, List, IList } from "@pnp/sp/presets/all";
      Preset: Coreimport { sp, List, IList } from "@pnp/sp/presets/core";
      -

      Update a list

      -

      Update an existing list with the provided properties. You can also provide an eTag value that will be used in the IF-Match header (default is "*")

      -
      import { IListUpdateResult } from "@pnp/sp/lists";
      -
      -// create a TypedHash object with the properties to update
      -const updateProperties = {
      -    Description: "This list title and description has been updated using PnPjs.",
      -    Title: "Updated title",
      -};
      -
      -// update the list with the properties above
      -list.update(updateProperties).then(async (l: IListUpdateResult) => {
      -
      -    // get the updated title and description
      -    const r = await l.list.select("Title", "Description")();
      -
      -    // log the updated properties to the console
      -    console.log(r.Title);
      -    console.log(r.Description);
      -});
      -
      -

      Get changes on a list

      -

      From the change log, you can get a collection of changes that have occurred within the list based on the specified query.

      -
      import { sp, IChangeQuery } from "@pnp/sp";
      -
      -// build the changeQuery object, here we look att changes regarding Add, DeleteObject and Restore
      -const changeQuery: IChangeQuery = {
      -    Add: true,
      -    ChangeTokenEnd: null,
      -    ChangeTokenStart: null,
      -    DeleteObject: true,
      -    Rename: true,
      -    Restore: true,
      -};
      -
      -// get list changes
      -const r = await list.getChanges(changeQuery);
      -
      -// log changes to console
      -console.log(r);
      -
      -

      Get list items using a CAML Query

      -

      You can get items from SharePoint using a CAML Query.

      -
      import { ICamlQuery } from "@pnp/sp/lists";
      -
      -// build the caml query object (in this example, we include Title field and limit rows to 5)
      -const caml: ICamlQuery = {
      -    ViewXml: "<View><ViewFields><FieldRef Name='Title' /></ViewFields><RowLimit>5</RowLimit></View>",
      -};
      -
      -// get list items
      -const r = await list.getItemsByCAMLQuery(caml);
      -
      -// log resulting array to console
      -console.log(r);
      -
      -

      If you need to get and expand a lookup field, there is a spread array parameter on the getItemsByCAMLQuery. This means that you can provide multiple properties to this method depending on how many lookup fields you are working with on your list. Below is a minimal example showing how to expand one field (RoleAssignment)

      -
      import { ICamlQuery } from "@pnp/sp/lists";
      -
      -// build the caml query object (in this example, we include Title field and limit rows to 5)
      -const caml: ICamlQuery = {
      -    ViewXml: "<View><ViewFields><FieldRef Name='Title' /><FieldRef Name='RoleAssignments' /></ViewFields><RowLimit>5</RowLimit></View>",
      -};
      -
      -// get list items
      -const r = await list.getItemsByCAMLQuery(caml, "RoleAssignments");
      -
      -// log resulting item array to console
      -console.log(r);
      -
      -

      Get list items changes using a Token

      -
      import {  IChangeLogItemQuery } from "@pnp/sp/lists";
      -
      -// build the caml query object (in this example, we include Title field and limit rows to 5)
      -const changeLogItemQuery: IChangeLogItemQuery = {
      -    Contains: `<Contains><FieldRef Name="Title"/><Value Type="Text">Item16</Value></Contains>`,
      -    QueryOptions: `<QueryOptions>
      -    <IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns>
      -    <DateInUtc>False</DateInUtc>
      -    <IncludePermissions>TRUE</IncludePermissions>
      -    <IncludeAttachmentUrls>FALSE</IncludeAttachmentUrls>
      -    <Folder>My List</Folder></QueryOptions>`,
      -};
      -
      -// get list items
      -const r = await list.getListItemChangesSinceToken(changeLogItemQuery);
      -
      -// log resulting XML to console
      -console.log(r);
      -
      -

      Recycle a list

      -

      Removes the list from the web's list collection and puts it in the recycle bin.

      -
      await list.recycle();
      -
      -

      Render list data

      -
      import { IRenderListData } from "@pnp/sp/lists";
      -
      -// render list data, top 5 items
      -const r: IRenderListData = await list.renderListData("<View><RowLimit>5</RowLimit></View>");
      -
      -// log array of items in response
      -console.log(r.Row);
      -
      -

      Render list data as stream

      -
      import { IRenderListDataParameters } from "@pnp/sp/lists";
      -
      -// setup parameters object
      -const renderListDataParams: IRenderListDataParameters = {
      -    ViewXml: "<View><RowLimit>5</RowLimit></View>",
      -};
      -
      -// render list data as stream
      -const r = await list.renderListDataAsStream(renderListDataParams);
      -
      -// log array of items in response
      -console.log(r.Row);
      -
      -

      Reserve list item Id for idempotent list item creation

      -
      const listItemId = await list.reserveListItemId();
      -
      -// log id to console
      -console.log(listItemId);
      -
      -

      Get list item entity type name

      -
      const entityTypeFullName = await list.getListItemEntityTypeFullName();
      -
      -// log entity type name
      -console.log(entityTypeFullName);
      -
      -

      Add a list item using path (folder), validation and set field values

      -
      const list = await sp.webs.lists.getByTitle("MyList").select("Title", "ParentWebUrl")();
      -const formValues: IListItemFormUpdateValue[] = [
      -                {
      -                    FieldName: "Title",
      -                    FieldValue: title,
      -                },
      -            ];
      -
      -list.addValidateUpdateItemUsingPath(formValues,`${list.ParentWebUrl}/Lists/${list.Title}/MyFolder`)
      -
      -
      -

      content-types imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/content-types";
      Selective 2import "@pnp/sp/content-types/list";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      contentTypes

      -

      Get all content types for a list

      -
      const list = sp.web.lists.getByTitle("Documents");
      -const r = await list.contentTypes();
      -
      -

      fields imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/fields";
      Selective 2import "@pnp/sp/fields/list";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      fields

      -

      Get all the fields for a list

      -
      const list = sp.web.lists.getByTitle("Documents");
      -const r = await list.fields();
      -
      -

      Add a field to the site, then add the site field to a list

      -
      const fld = await sp.site.rootWeb.fields.addText("MyField");
      -await sp.web.lists.getByTitle("MyList").fields.createFieldAsXml(fld.data.SchemaXml);
      -
      -

      folders imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/folders";
      Selective 2import "@pnp/sp/folders/list";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      folders

      -

      Get the root folder of a list.

      -
      const list = sp.web.lists.getByTitle("Documents");
      -const r = await list.rootFolder();
      -
      -

      forms imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/forms";
      Selective 2import "@pnp/sp/forms/list";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      forms

      -
      const r = await list.forms();
      -
      -

      items imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/items";
      Selective 2import "@pnp/sp/items/list";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      items

      -

      Get a collection of list items.

      -
      const r = await list.items();
      -
      -

      views imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/views";
      Selective 2import "@pnp/sp/views/list";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      views

      -

      Get the default view of the list

      -
      const list = sp.web.lists.getByTitle("Documents");
      -const views = await list.views();
      -const defaultView = await list.defaultView();
      -
      -

      Get a list view by Id

      -
      const view = await list.getView(defaultView.Id).select("Title")();
      -
      -

      security imports

      -

      To work with list security, you can import the list methods as follows:

      -
      import "@pnp/sp/security/list";
      -
      -

      For more information on how to call security methods for lists, please refer to the @pnp/sp/security documentation.

      -

      subscriptions imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/subscriptions";
      Selective 2import "@pnp/sp/subscriptions/list";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      subscriptions

      -

      Get all subscriptions on the list

      -
      const list = sp.web.lists.getByTitle("Documents");
      -const subscriptions = await list.subscriptions();
      -
      -

      user-custom-actions imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/user-custom-actions";
      Selective 2import "@pnp/sp/user-custom-actions/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      userCustomActions

      -

      Get a collection of the list's user custom actions.

      -
      const list = sp.web.lists.getByTitle("Documents");
      -const r = await list.userCustomActions();
      -
      -

      getParentInfos

      -

      Added in 2.0.12

      -

      Gets information about an list, including details about the parent list root folder, and parent web.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/items";
      -
      -const list = sp.web.lists.getByTitle("Documents");
      -await list.getParentInfos();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/navigation/index.html b/docs/v3/v2/sp/navigation/index.html deleted file mode 100644 index 2391520f9..000000000 --- a/docs/v3/v2/sp/navigation/index.html +++ /dev/null @@ -1,2535 +0,0 @@ - - - - - - - - - - - - - - - - - - Navigation - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp - navigation

      -

      Selective Imports Banner

      - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/navigation";
      -

      getMenuState

      -

      The MenuState service operation returns a Menu-State (dump) of a SiteMapProvider on a site. It will return an exception if the SiteMapProvider cannot be found on the site, the SiteMapProvider does not implement the IEditableSiteMapProvider interface or the SiteMapNode key cannot be found within the provider hierarchy.

      -

      The IEditableSiteMapProvider also supports Custom Properties which is an optional feature. What will be return in the custom properties is up to the IEditableSiteMapProvider implementation and can differ for for each SiteMapProvider implementation. The custom properties can be requested by providing a comma separated string of property names like: property1,property2,property3\,containingcomma

      -

      NOTE: the , separator can be escaped using the \ as escape character as done in the example above. The string above would split like:

      -
        -
      • property1
      • -
      • property2
      • -
      • property3,containingcomma
      • -
      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/navigation";
      -
      -// Will return a menu state of the default SiteMapProvider 'SPSiteMapProvider' where the dump starts a the RootNode (within the site) with a depth of 10 levels.
      -const state = await sp.navigation.getMenuState();
      -
      -// Will return the menu state of the 'SPSiteMapProvider', starting with the node with the key '1002' with a depth of 5
      -const state2 = await sp.navigation.getMenuState("1002", 5);
      -
      -// Will return the menu state of the 'CurrentNavSiteMapProviderNoEncode' from the root node of the provider with a depth of 5
      -const state3 = await sp.navigation.getMenuState(null, 5, "CurrentNavSiteMapProviderNoEncode");
      -
      -

      getMenuNodeKey

      -

      Tries to get a SiteMapNode.Key for a given URL within a site collection. If the SiteMapNode cannot be found an Exception is returned. The method is using SiteMapProvider.FindSiteMapNodeFromKey(string rawUrl) to lookup the SiteMapNode. Depending on the actual implementation of FindSiteMapNodeFromKey the matching can differ for different SiteMapProviders.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/navigation";
      -
      -const key = await sp.navigation.getMenuNodeKey("/sites/dev/Lists/SPPnPJSExampleList/AllItems.aspx");
      -
      -

      Web Navigation

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/navigation";
      -

      The navigation object contains two properties "quicklaunch" and "topnavigationbar". Both have the same set of methods so our examples below show use of only quicklaunch but apply equally to topnavigationbar.

      -

      Get navigation

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/navigation";
      -
      -const top = await sp.web.navigation.topNavigationBar();
      -const quick = await sp.web.navigation.quicklaunch();
      -
      -

      For the following examples we will refer to a variable named "nav" that is understood to be one of topNavigationBar or quicklaunch.

      -

      getById

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/navigation";
      -
      -const node = await nav.getById(3)();
      -
      -

      add

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/navigation";
      -
      -const result = await nav.add("Node Title", "/sites/dev/pages/mypage.aspx", true);
      -
      -const nodeDataRaw = result.data;
      -
      -// request the data from the created node
      -const nodeData = result.node();
      -
      -

      moveAfter

      -

      Places a navigation node after another node in the tree

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/navigation";
      -
      -const node1result = await nav.add(`Testing - ${getRandomString(4)} (1)`, url, true);
      -const node2result = await nav.add(`Testing - ${getRandomString(4)} (2)`, url, true);
      -const node1 = await node1result.node();
      -const node2 = await node2result.node();
      -
      -await nav.moveAfter(node1.Id, node2.Id);
      -
      -

      Delete

      -

      Deletes a given node

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/navigation";
      -
      -const node1result = await nav.add(`Testing - ${getRandomString(4)}`, url, true);
      -let nodes = await nav();
      -// check we added a node
      -let index = nodes.findIndex(n => n.Id === node1result.data.Id)
      -// index >= 0
      -
      -// delete a node
      -await nav.getById(node1result.data.Id).delete();
      -
      -nodes = await nav();
      -index = nodes.findIndex(n => n.Id === node1result.data.Id)
      -// index = -1
      -
      -

      Update

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/navigation";
      -
      -
      -await nav.getById(4).update({
      -    Title: "A new title",
      -});
      -
      -

      Children

      -

      The children property of a Navigation Node represents a collection with all the same properties and methods available on topNavigationBar or quicklaunch.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/navigation";
      -
      -const childrenData = await nav.getById(1).children();
      -
      -// add a child
      -await nav.getById(1).children.add("Title", "Url", true);
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/permissions/index.html b/docs/v3/v2/sp/permissions/index.html deleted file mode 100644 index 399c98c8d..000000000 --- a/docs/v3/v2/sp/permissions/index.html +++ /dev/null @@ -1,2338 +0,0 @@ - - - - - - - - - - - - - - - - - - Permissions - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      - -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp - permissions

      -

      A common task is to determine if a user or the current user has a certain permission level. It is a great idea to check before performing a task such as creating a list to ensure a user can without getting back an error. This allows you to provide a better experience to the user.

      -

      Permissions in SharePoint are assigned to the set of securable objects which include Site, Web, List, and List Item. These are the four level to which unique permissions can be assigned. As such @pnp/sp provides a set of methods defined in the QueryableSecurable class to handle these permissions. These examples all use the Web to get the values, however the methods work identically on all securables.

      -

      Get Role Assignments

      -

      This gets a collection of all the role assignments on a given securable. The property returns a RoleAssignments collection which supports the OData collection operators.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/security";
      -import { Logger } from "@pnp/logging";
      -
      -const roles = await sp.web.roleAssignments();
      -Logger.writeJSON(roles);
      -
      -

      First Unique Ancestor Securable Object

      -

      This method can be used to find the securable parent up the hierarchy that has unique permissions. If everything inherits permissions this will be the Site. If a sub web has unique permissions it will be the web, and so on.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/security";
      -import { Logger } from "@pnp/logging";
      -
      -const obj = await sp.web.firstUniqueAncestorSecurableObject();
      -Logger.writeJSON(obj);
      -
      -

      User Effective Permissions

      -

      This method returns the BasePermissions for a given user or the current user. This value contains the High and Low values for a user on the securable you have queried.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/security";
      -import { Logger } from "@pnp/logging";
      -
      -const perms = await sp.web.getUserEffectivePermissions("i:0#.f|membership|user@site.com");
      -Logger.writeJSON(perms);
      -
      -const perms2 = await sp.web.getCurrentUserEffectivePermissions();
      -Logger.writeJSON(perms2);
      -
      -

      User Has Permissions

      -

      Because the High and Low values in the BasePermission don't obviously mean anything you can use these methods along with the PermissionKind enumeration to check actual rights on the securable.

      -
      import { sp } from "@pnp/sp";
      -import { PermissionKind } from "@pnp/sp/security";
      -
      -const perms = await sp.web.userHasPermissions("i:0#.f|membership|user@site.com", PermissionKind.ApproveItems);
      -console.log(perms);
      -
      -const perms2 = await sp.web.currentUserHasPermissions(PermissionKind.ApproveItems);
      -console.log(perms2);
      -
      -

      Has Permissions

      -

      If you need to check multiple permissions it can be more efficient to get the BasePermissions once and then use the hasPermissions method to check them as shown below.

      -
      import { sp } from "@pnp/sp";
      -import { PermissionKind } from "@pnp/sp/security";
      -
      -const perms = await sp.web.getCurrentUserEffectivePermissions();
      -if (sp.web.hasPermissions(perms, PermissionKind.AddListItems) && sp.web.hasPermissions(perms, PermissionKind.DeleteVersions)) {
      -    // ...
      -}
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/profiles/index.html b/docs/v3/v2/sp/profiles/index.html deleted file mode 100644 index f3c5ef899..000000000 --- a/docs/v3/v2/sp/profiles/index.html +++ /dev/null @@ -1,2806 +0,0 @@ - - - - - - - - - - - - - - - - - - Profiles - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/profiles

      -

      The profile services allows you to work with the SharePoint User Profile Store.

      -

      Profiles

      -

      Profiles is accessed directly from the root sp object.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/profiles";
      -
      - -
      editProfileLink(): Promise<string>
      -
      -
      const editProfileLink = await sp.profiles.editProfileLink();
      -console.log("My edit profile link =" + editProfileLink);
      -
      -

      Is My People List Public

      -

      Provides a boolean that indicates if the current users "People I'm Following" list is public or not

      -
      isMyPeopleListPublic(): Promise<boolean>
      -
      -
      const isPublic = await sp.profiles.isMyPeopleListPublic();
      -console.log("Is my Following list Public =" + isPubic);
      -
      -

      Find out if the current user is followed by another user

      -

      Provides a boolean that indicates if the current users is followed by a specific user.

      -
      amIFollowedBy(loginName: string): Promise<boolean>
      -
      -
      const loginName = "i:0#.f|membership|testuser@mytenant.onmicrosoft.com";
      -const isFollowedBy = await sp.profiles.amIFollowedBy(loginName);
      -console.log("Is " + loginName + " following me? " + isFollowedBy);
      -
      -

      Find out if I am following a specific user

      -

      Provides a boolean that indicates if the current users is followed by a specific user.

      -
      amIFollowing(loginName: string): Promise<boolean>
      -
      -
      const loginName = "i:0#.f|membership|testuser@mytenant.onmicrosoft.com";
      -const following = await sp.profiles.amIFollowing(loginName);
      -console.log("Am I following " + loginName + "? " + following);
      -
      -

      Get the tags I follow

      -

      Gets the tags the current user is following. Accepts max count, default is 20.

      -
      getFollowedTags(maxCount = 20): Promise<string[]>
      -
      -
      const tags = await sp.profiles.getFollowedTags();
      -console.log(tags);
      -
      -

      Get followers for a specific user

      -

      Gets the people who are following the specified user.

      -
      getFollowersFor(loginName: string): Promise<any[]>
      -
      -
      const loginName = "i:0#.f|membership|testuser@mytenant.onmicrosoft.com";
      -const followers = await sp.profiles.getFollowersFor(loginName);
      -followers.forEach((value) => {
      -  console.log(value);
      -});
      -
      -

      Get followers for the current

      -

      Gets the people who are following the current user.

      -
      myFollowers(): ISharePointQueryableCollection
      -
      -
      const folowers = await sp.profiles.myFollowers();
      -console.log(folowers);
      -
      -

      Get the properties for the current user

      -

      Gets user properties for the current user.

      -
      myProperties(): _SharePointQueryableInstance
      -
      -
      const profile = await sp.profiles.myProperties();
      -console.log(profile.DisplayName);
      -console.log(profile.Email);
      -console.log(profile.Title);
      -console.log(profile.UserProfileProperties.length);
      -
      -// Properties are stored in Key/Value pairs,
      -// so parse into an object called userProperties
      -var props = {};
      -profile.UserProfileProperties.forEach((prop) => {
      -  props[prop.Key] = prop.Value;
      -});
      -profile.userProperties = props;
      -console.log("Account Name: " + profile.userProperties.AccountName);
      -
      -

      Gets people specified user is following

      -
      getPeopleFollowedBy(loginName: string): Promise<any[]>
      -
      -
      const loginName = "i:0#.f|membership|testuser@mytenant.onmicrosoft.com";
      -const folowers = await sp.profiles.getFollowersFor(loginName);
      -followers.forEach((value) => {
      -  console.log(value);
      -});
      -
      -

      Gets properties for a specified user

      -
      getPropertiesFor(loginName: string): Promise<any>
      -
      -
      const loginName = "i:0#.f|membership|testuser@mytenant.onmicrosoft.com";
      -const profile = await sp.profiles.getPropertiesFor(loginName);
      -console.log(profile.DisplayName);
      -console.log(profile.Email);
      -console.log(profile.Title);
      -console.log(profile.UserProfileProperties.length);
      -
      -// Properties are stored in inconvenient Key/Value pairs,
      -// so parse into an object called userProperties
      -var props = {};
      -profile.UserProfileProperties.forEach((prop) => {
      -  props[prop.Key] = prop.Value;
      -});
      -
      -profile.userProperties = props;
      -console.log("Account Name: " + profile.userProperties.AccountName);
      -
      - -

      Gets the 20 most popular hash tags over the past week, sorted so that the most popular tag appears first

      -
      trendingTags(): Promise<IHashTagCollection>
      -
      -
      const tags = await sp.profiles.trendingTags();
      -tags.Items.forEach((tag) => {
      -  console.log(tag);
      -});
      -
      -

      Gets specified user profile property for the specified user

      -
      getUserProfilePropertyFor(loginName: string, propertyName: string): Promise<string>
      -
      -
      const loginName = "i:0#.f|membership|testuser@mytenant.onmicrosoft.com";
      -const propertyName = "AccountName";
      -const property = await sp.profiles.getUserProfilePropertyFor(loginName, propertyName);
      -console.log(property);
      -
      -

      Hide specific user from list of suggested people

      -

      Removes the specified user from the user's list of suggested people to follow.

      -
      hideSuggestion(loginName: string): Promise<void>
      -
      -
      const loginName = "i:0#.f|membership|testuser@mytenant.onmicrosoft.com";
      -await sp.profiles.hideSuggestion(loginName);
      -
      -

      Is one user following another

      -

      Indicates whether the first user is following the second user. -First parameter is the account name of the user who might be following the followee. -Second parameter is the account name of the user who might be followed by the follower.

      -
      isFollowing(follower: string, followee: string): Promise<boolean>
      -
      -
      const follower = "i:0#.f|membership|testuser@mytenant.onmicrosoft.com";
      -const followee = "i:0#.f|membership|testuser2@mytenant.onmicrosoft.com";
      -const isFollowing = await sp.profiles.isFollowing(follower, followee);
      -console.log(isFollowing);
      -
      -

      Set User Profile Picture

      -

      Uploads and sets the user profile picture (Users can upload a picture to their own profile only). Not supported for batching. -Accepts the profilePicSource Blob data representing the user's picture in BMP, JPEG, or PNG format of up to 4.76MB.

      -
      setMyProfilePic(profilePicSource: Blob): Promise<void>
      -
      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/profiles";
      -import "@pnp/sp/folders";
      -import "@pnp/sp/files";
      -
      -// get the blob object through a request or from a file input
      -const blob = await sp.web.lists.getByTitle("Documents").rootFolder.files.getByName("profile.jpg").getBlob();
      -
      -await sp.profiles.setMyProfilePic(blob);
      -
      -

      Sets single value User Profile property

      -

      accountName The account name of the user -propertyName Property name -propertyValue Property value

      -
      setSingleValueProfileProperty(accountName: string, propertyName: string, propertyValue: string): Promise<void>
      -
      -
      const loginName = "i:0#.f|membership|testuser@mytenant.onmicrosoft.com";
      -await sp.profiles.setSingleValueProfileProperty(loginName, "CellPhone", "(123) 555-1212");
      -
      -

      Sets a mult-value User Profile property

      -

      accountName The account name of the user -propertyName Property name -propertyValues Property values

      -
      setMultiValuedProfileProperty(accountName: string, propertyName: string, propertyValues: string[]): Promise<void>
      -
      -
      const loginName = "i:0#.f|membership|testuser@mytenant.onmicrosoft.com";
      -const propertyName = "SPS-Skills";
      -const propertyValues = ["SharePoint", "Office 365", "Architecture", "Azure"];
      -await sp.profiles.setMultiValuedProfileProperty(loginName, propertyName, propertyValues);
      -const profile = await sp.profiles.getPropertiesFor(loginName);
      -var props = {};
      -profile.UserProfileProperties.forEach((prop) => {
      -  props[prop.Key] = prop.Value;
      -});
      -profile.userProperties = props;
      -console.log(profile.userProperties[propertyName]);
      -
      -

      Create Personal Site for specified users

      -

      Provisions one or more users' personal sites. (My Site administrator on SharePoint Online only) -Emails The email addresses of the users to provision sites for

      -
      createPersonalSiteEnqueueBulk(...emails: string[]): Promise<void>
      -
      -
      let userEmails: string[] = ["testuser1@mytenant.onmicrosoft.com", "testuser2@mytenant.onmicrosoft.com"];
      -await sp.profiles.createPersonalSiteEnqueueBulk(userEmails);
      -
      -

      Get the user profile of the owner for the current site

      -
      ownerUserProfile(): Promise<IUserProfile>
      -
      -
      const profile = await sp.profiles.ownerUserProfile();
      -console.log(profile);
      -
      -

      Get the user profile of the current user

      -
      userProfile(): Promise<any>
      -
      -
      const profile = await sp.profiles.userProfile();
      -console.log(profile);
      -
      -

      Create personal site for current user

      -
      createPersonalSite(interactiveRequest = false): Promise<void>
      -
      -
      await sp.profiles.createPersonalSite();
      -
      -

      Make all profile data public or private

      -

      Set the privacy settings for all social data.

      -
      shareAllSocialData(share: boolean): Promise<void>
      -
      -
      await sp.profiles.shareAllSocialData(true);
      -
      -

      Resolve a user or group

      -

      Resolves user or group using specified query parameters

      -
      clientPeoplePickerResolveUser(queryParams: IClientPeoplePickerQueryParameters): Promise<IPeoplePickerEntity>
      -
      -
      const result = await sp.profiles.clientPeoplePickerSearchUser({
      -  AllowEmailAddresses: true,
      -  AllowMultipleEntities: false,
      -  MaximumEntitySuggestions: 25,
      -  QueryString: 'John'
      -});
      -console.log(result);
      -
      -

      Search a user or group

      -

      Searches for users or groups using specified query parameters

      -
      clientPeoplePickerSearchUser(queryParams: IClientPeoplePickerQueryParameters): Promise<IPeoplePickerEntity[]>
      -
      -
      const result = await sp.profiles.clientPeoplePickerSearchUser({
      -  AllowEmailAddresses: true,
      -  AllowMultipleEntities: false,
      -  MaximumEntitySuggestions: 25,
      -  QueryString: 'John'
      -});
      -console.log(result);
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/regional-settings/index.html b/docs/v3/v2/sp/regional-settings/index.html deleted file mode 100644 index 52fc5eb1e..000000000 --- a/docs/v3/v2/sp/regional-settings/index.html +++ /dev/null @@ -1,2394 +0,0 @@ - - - - - - - - - - - - - - - - - - Regional Settings - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/regional-settings

      -

      The regional settings module helps with managing dates and times across various timezones.

      -

      IRegionalSettings

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import { IRegionalSettings, ITimeZone, ITimeZones, RegionalSettings, TimeZone, TimeZones, } from "@pnp/sp/regional-settings";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/regional-settings";
      Selective 3import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/regional-settings/web";
      Preset: Allimport { sp, Webs, IWebs } from "@pnp/sp/presets/all";
      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/regional-settings/web";
      -
      -// get all the web's regional settings
      -const s = await sp.web.regionalSettings();
      -
      -// select only some settings to return
      -const s2 = await sp.web.regionalSettings.select("DecimalSeparator", "ListSeparator", "IsUIRightToLeft")();
      -
      -

      Installed Languages

      -

      You can get a list of the installed languages in the web.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/regional-settings/web";
      -
      -const s = await sp.web.regionalSettings.getInstalledLanguages();
      -
      -
      -

      The installedLanguages property accessor is deprecated after 2.0.4 in favor of getInstalledLanguages and will be removed in future versions.

      -
      -

      TimeZones

      -

      You can also get information about the selected timezone in the web and all of the defined timezones.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/regional-settings/web";
      -
      -// get the web's configured timezone
      -const s = await sp.web.regionalSettings.timeZone();
      -
      -// select just the Description and Id
      -const s2 = await sp.web.regionalSettings.timeZone.select("Description", "Id")();
      -
      -// get all the timezones
      -const s3 = await sp.web.regionalSettings.timeZones();
      -
      -// get a specific timezone by id
      -// list of ids: https://msdn.microsoft.com/en-us/library/office/jj247008.aspx
      -const s4 = await sp.web.regionalSettings.timeZones.getById(23);
      -const s5 = await s.localTimeToUTC(new Date());
      -
      -// convert a given date from web's local time to UTC time
      -const s6 = await sp.web.regionalSettings.timeZone.localTimeToUTC(new Date());
      -
      -// convert a given date from UTC time to web's local time
      -const s6 = await sp.web.regionalSettings.timeZone.utcToLocalTime(new Date())
      -const s7 = await sp.web.regionalSettings.timeZone.utcToLocalTime(new Date(2019, 6, 10, 10, 0, 0, 0))
      -
      -

      Title and Description Resources

      -

      Added in 2.0.4

      -

      Some objects allow you to read language specific title information as shown in the following sample. This applies to Web, List, Field, Content Type, and User Custom Actions.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/regional-settings";
      -
      -//
      -// The below methods appears on
      -// - Web
      -// - List
      -// - Field
      -// - ContentType
      -// - User Custom Action
      -//
      -// after you import @pnp/sp/regional-settings
      -//
      -// you can also import just parts of the regional settings:
      -// import "@pnp/sp/regional-settings/web";
      -// import "@pnp/sp/regional-settings/list";
      -// import "@pnp/sp/regional-settings/content-type";
      -// import "@pnp/sp/regional-settings/field";
      -// import "@pnp/sp/regional-settings/user-custom-actions";
      -
      -
      -const title = await sp.web.titleResource("en-us");
      -const title2 = await sp.web.titleResource("de-de");
      -
      -const description = await sp.web.descriptionResource("en-us");
      -const description2 = await sp.web.descriptionResource("de-de");
      -
      -
      -

      You can only read the values through the REST API, not set the value.

      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/related-items/index.html b/docs/v3/v2/sp/related-items/index.html deleted file mode 100644 index a98a74d3c..000000000 --- a/docs/v3/v2/sp/related-items/index.html +++ /dev/null @@ -1,2417 +0,0 @@ - - - - - - - - - - - - - - - - - - Related Items - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/related-items

      -

      The related items API allows you to add related items to items within a task or workflow list. Related items need to be in the same site collection.

      -

      Setup

      -

      Instead of copying this block of code into each sample, understand that each sample is meant to run with this supporting code to work.

      -
      import { sp, extractWebUrl } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/related-items/web";
      -import "@pnp/sp/lists/web";
      -import "@pnp/sp/items/list";
      -import "@pnp/sp/files/list";
      -import { IList } from "@pnp/sp/lists";
      -import { getRandomString } from "@pnp/core";
      -
      -// setup some lists (or just use existing ones this is just to show the complete process)
      -// we need two lists to use for creating related items, they need to use template 107 (task list)
      -const ler1 = await sp.web.lists.ensure("RelatedItemsSourceList", "", 107);
      -const ler2 = await sp.web.lists.ensure("RelatedItemsTargetList", "", 107);
      -
      -const sourceList = ler1.list;
      -const targetList = ler2.list;
      -
      -const sourceListName = await sourceList.select("Id")().then(r => r.Id);
      -const targetListName = await targetList.select("Id")().then(r => r.Id);
      -
      -// or whatever you need to get the web url, both our example lists are in the same web.
      -const webUrl = sp.web.toUrl();
      -
      -// ...individual samples start here
      -
      - -
      const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data);
      -const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data);
      -
      -await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl);
      -
      -

      addSingleLinkToUrl

      -

      This method adds a link to task item based on a url. The list name and item id are to the task item, the url is to the related item/document.

      -
      // get a file's server relative url in some manner, here we add one
      -const file = await sp.web.defaultDocumentLibrary.rootFolder.files.add(`file_${getRandomString(4)}.txt`, "Content", true).then(r => r.data);
      -// add an item or get an item from the task list
      -const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data);
      -
      -await sp.web.relatedItems.addSingleLinkToUrl(targetListName, targetItem.Id, file.ServerRelativeUrl);
      -
      -

      addSingleLinkFromUrl

      -

      This method adds a link to task item based on a url. The list name and item id are to related item, the url is to task item to which the related reference is being added. I haven't found a use case for this method.

      - -

      This method allows you to delete a link previously created.

      -
      const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data);
      -const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data);
      -
      -// add the link
      -await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl);
      -
      -// delete the link
      -await sp.web.relatedItems.deleteSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl);
      -
      -

      getRelatedItems

      -

      Gets the related items for an item

      -
      import { IRelatedItem } from "@pnp/sp/related-items";
      -
      -const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data);
      -const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data);
      -
      -// add a link
      -await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl);
      -
      -const targetItem2 = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data);
      -
      -// add a link
      -await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem2.Id, webUrl);
      -
      -const items: IRelatedItem[] = await sp.web.relatedItems.getRelatedItems(sourceListName, sourceItem.Id);
      -
      -// items.length === 2
      -
      -

      Related items are defined by the IRelatedItem interface

      -
      export interface IRelatedItem {
      -    ListId: string;
      -    ItemId: number;
      -    Url: string;
      -    Title: string;
      -    WebId: string;
      -    IconUrl: string;
      -}
      -
      -

      getPageOneRelatedItems

      -

      Gets an abbreviated set of related items

      -
      import { IRelatedItem } from "@pnp/sp/related-items";
      -
      -const sourceItem = await sourceList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data);
      -const targetItem = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data);
      -
      -// add a link
      -await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem.Id, webUrl);
      -
      -const targetItem2 = await targetList.items.add({ Title: `Item ${getRandomString(4)}` }).then(r => r.data);
      -
      -// add a link
      -await sp.web.relatedItems.addSingleLink(sourceListName, sourceItem.Id, webUrl, targetListName, targetItem2.Id, webUrl);
      -
      -const items: IRelatedItem[] = await sp.web.relatedItems.getPageOneRelatedItems(sourceListName, sourceItem.Id);
      -
      -// items.length === 2
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/search/index.html b/docs/v3/v2/sp/search/index.html deleted file mode 100644 index 64a74ae05..000000000 --- a/docs/v3/v2/sp/search/index.html +++ /dev/null @@ -1,2474 +0,0 @@ - - - - - - - - - - - - - - - - - - Search - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/search

      -

      Using search you can access content throughout your organization in a secure and consistent manner. The library provides support for searching and suggest - as well as some interfaces and helper classes to make building your queries and processing responses easier.

      - -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/search";
      import { ISearchQuery, SearchResults } from "@pnp/sp/search";
      Preset: Allimport { sp, ISearchQuery, SearchResults } from "@pnp/sp/presets/all";
      -

      Search is accessed directly from the root sp object and can take either a string representing the query text, a plain object matching the ISearchQuery interface, or a SearchQueryBuilder instance.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/search";
      -import { ISearchQuery, SearchResults, SearchQueryBuilder } from "@pnp/sp/search";
      -
      -// text search using SharePoint default values for other parameters
      -const results: SearchResults = await sp.search("test");
      -
      -console.log(results.ElapsedTime);
      -console.log(results.RowCount);
      -console.log(results.PrimarySearchResults);
      -
      -
      -// define a search query object matching the ISearchQuery interface
      -const results2: SearchResults = await sp.search(<ISearchQuery>{
      -    Querytext: "test",
      -    RowLimit: 10,
      -    EnableInterleaving: true,
      -});
      -
      -console.log(results2.ElapsedTime);
      -console.log(results2.RowCount);
      -console.log(results2.PrimarySearchResults);
      -
      -// define a query using a builder
      -const builder = SearchQueryBuilder("test").rowLimit(10).enableInterleaving.enableQueryRules.processPersonalFavorites;
      -const results3 = await sp.search(builder);
      -
      -console.log(results3.ElapsedTime);
      -console.log(results3.RowCount);
      -console.log(results3.PrimarySearchResults);
      -
      -

      Search Result Caching

      -

      You can use the searchWithCaching method to enable cache support for your search results this option works with any of the options for providing a query, just replace "search" with "searchWithCaching" in your method chain and gain all the benefits of caching. The second parameter is optional and allows you to specify the cache options

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/search";
      -import { ISearchQuery, SearchResults, SearchQueryBuilder } from "@pnp/sp/search";
      -
      -sp.searchWithCaching({
      -    Querytext: "test",
      -    RowLimit: 10,
      -    EnableInterleaving: true,
      -} as ISearchQuery).then((r: SearchResults) => {
      -
      -    console.log(r.ElapsedTime);
      -    console.log(r.RowCount);
      -    console.log(r.PrimarySearchResults);
      -});
      -
      -// use a query builder
      -const builder = SearchQueryBuilder("test").rowLimit(3);
      -
      -// supply a search query builder and caching options
      -const results2 = await sp.searchWithCaching(builder, { key: "mykey", expiration: dateAdd(new Date(), "month", 1) });
      -
      -console.log(results2.TotalRows);
      -
      -

      Paging with SearchResults.getPage

      -

      Paging is controlled by a start row and page size parameter. You can specify both arguments in your initial query however you can use the getPage method to jump to any page. The second parameter page size is optional and will use the previous RowLimit or default to 10.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/search";
      -import { SearchResults, SearchQueryBuilder } from "@pnp/sp/search";
      -
      -// this will hold our current results
      -let currentResults: SearchResults = null;
      -let page = 1;
      -
      -// triggered on page load or through some other means
      -function onStart() {
      -
      -    // construct our query that will be used throughout the paging process, likely from user input
      -    const q = SearchQueryBuilder("test").rowLimit(5);
      -    const results = await sp.search(q);
      -    currentResults = results; // set the current results
      -    page = 1; // reset page counter
      -    // update UI...
      -}
      -
      -// triggered by an event
      -async function next() {
      -
      -    currentResults = await currentResults.getPage(++page);
      -    // update UI...
      -}
      -
      -// triggered by an event
      -async function prev() {
      -
      -    currentResults = await currentResults.getPage(--page);
      -    // update UI...
      -}
      -
      -

      SearchQueryBuilder

      -

      The SearchQueryBuilder allows you to build your queries in a fluent manner. It also accepts constructor arguments for query text and a base query plain object, should you have a shared configuration for queries in an application you can define them once. The methods and properties match those on the SearchQuery interface. Boolean properties add the flag to the query while methods require that you supply one or more arguments. Also arguments supplied later in the chain will overwrite previous values.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/search";
      -import { SearchQueryBuilder, SearchResults, ISearchQuery } from "@pnp/sp/search";
      -
      -// basic usage
      -let q = SearchQueryBuilder().text("test").rowLimit(4).enablePhonetic;
      -
      -sp.search(q).then(h => { /* ... */ });
      -
      -// provide a default query text at creation
      -let q2 = SearchQueryBuilder("text").rowLimit(4).enablePhonetic;
      -
      -const results: SearchResults = await sp.search(q2);
      -
      -// provide query text and a template for
      -// shared settings across queries that can
      -// be overwritten by individual builders
      -const appSearchSettings: ISearchQuery = {
      -    EnablePhonetic: true,
      -    HiddenConstraints: "reports"
      -};
      -
      -let q3 = SearchQueryBuilder("test", appSearchSettings).enableQueryRules;
      -let q4 = SearchQueryBuilder("financial data", appSearchSettings).enableSorting.enableStemming;
      -const results2 = await sp.search(q3);
      -const results3 = sp.search(q4);
      -
      -

      Search Suggest

      -

      Search suggest works in much the same way as search, except against the suggest end point. It takes a string or a plain object that matches ISuggestQuery.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/search";
      -import { ISuggestQuery, ISuggestResult } from "@pnp/sp/search";
      -
      -const results = await sp.searchSuggest("test");
      -
      -const results2 = await sp.searchSuggest({
      -    querytext: "test",
      -    count: 5,
      -} as ISuggestQuery);
      -
      -

      Search Factory

      -

      You can also configure a search or suggest query against any valid SP url using the factory methods.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/search";
      -import { Search, Suggest } from "@pnp/sp/search";
      -
      -// set the url for search
      -const searcher = Search("https://mytenant.sharepoint.com/sites/dev");
      -
      -// this can accept any of the query types (text, ISearchQuery, or SearchQueryBuilder)
      -const results = await searcher("test");
      -
      -// you can reuse the ISearch instance
      -const results2 = await searcher("another query");
      -
      -// same process works for Suggest
      -const suggester = Suggest("https://mytenant.sharepoint.com/sites/dev");
      -
      -const suggestions = await suggester({ querytext: "test" });
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/security/index.html b/docs/v3/v2/sp/security/index.html deleted file mode 100644 index 3ce6057f5..000000000 --- a/docs/v3/v2/sp/security/index.html +++ /dev/null @@ -1,2432 +0,0 @@ - - - - - - - - - - - - - - - - - - Security - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/security

      -

      There are four levels where you can break inheritance and assign security: Site, Web, List, Item. All four of these objects share a common set of methods. Because of this we are showing in the examples below usage of these methods for an IList instance, but they apply across all four securable objects. In addition to the shared methods, some types have unique methods which are listed below.

      -
      -

      Site permissions are managed on the root web of the site collection.

      -
      -

      A Note on Selective Imports for Security

      -

      Because the method are shared you can opt to import only the methods for one of the instances.

      -
      import "@pnp/sp/security/web";
      -import "@pnp/sp/security/list";
      -import "@pnp/sp/security/item";
      -
      -

      Possibly useful if you are trying to hyper-optimize for bundle size but it is just as easy to import the whole module:

      -
      import "@pnp/sp/security";
      -
      -

      Securable Methods

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/security/list";
      -import "@pnp/sp/site-users/web";
      -import { IList } from "@pnp/sp/lists";
      -import { PermissionKind } from "@pnp/sp/security";
      -
      -// ensure we have a list
      -const ler = await sp.web.lists.ensure("SecurityTestingList");
      -const list: IList = ler.list;
      -
      -// role assignments (see section below)
      -await list.roleAssignments();
      -
      -// data will represent one of the possible parents Site, Web, or List
      -const data = await list.firstUniqueAncestorSecurableObject();
      -
      -// getUserEffectivePermissions
      -const users = await sp.web.siteUsers.top(1).select("LoginName")();
      -const perms = await list.getUserEffectivePermissions(users[0].LoginName);
      -
      -// getCurrentUserEffectivePermissions
      -const perms2 = list.getCurrentUserEffectivePermissions();
      -
      -// userHasPermissions
      -const v: boolean = list.userHasPermissions(users[0].LoginName, PermissionKind.AddListItems)
      -
      -// currentUserHasPermissions
      -const v2: boolean = list.currentUserHasPermissions(PermissionKind.AddListItems)
      -
      -// breakRoleInheritance
      -await list.breakRoleInheritance();
      -// copy existing permissions
      -await list.breakRoleInheritance(true);
      -// copy existing permissions and reset all child securables to the new permissions
      -await list.breakRoleInheritance(true, true);
      -
      -// resetRoleInheritance
      -await list.resetRoleInheritance();
      -
      -

      Web Specific methods

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/security/web";
      -
      -// role definitions (see section below)
      -const defs = await sp.web.roleDefinitions();
      -
      -

      Role Assignments

      -

      Allows you to list and manipulate the set of role assignments for the given securable. Again we show usage using list, but the examples apply to web and item as well.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/security/web";
      -import "@pnp/sp/site-users/web";
      -import { IList } from "@pnp/sp/lists";
      -import { PermissionKind } from "@pnp/sp/security";
      -
      -// ensure we have a list
      -const ler = await sp.web.lists.ensure("SecurityTestingList");
      -const list: IList = ler.list;
      -
      -// list role assignments
      -const assignments = await list.roleAssignments();
      -
      -// add a role assignment
      -const defs = await sp.web.roleDefinitions();
      -const user = await sp.web.currentUser();
      -const r = await list.roleAssignments.add(user.Id, defs[0].Id);
      -
      -// remove a role assignment
      -const ras = await list.roleAssignments();
      -// filter/find the role assignment you want to remove
      -// here we just grab the first
      -const ra = ras.find(v => true);
      -const r = await list.roleAssignments.remove(ra.Id);
      -
      -// read role assignment info
      -const info = await list.roleAssignments.getById(ra.Id)();
      -
      -// get the groups
      -const info2 = await list.roleAssignments.getById(ra.Id).groups();
      -
      -// get the bindings
      -const info3 = await list.roleAssignments.getById(ra.Id).bindings();
      -
      -// delete a role assignment (same as remove)
      -const ras = await list.roleAssignments();
      -// filter/find the role assignment you want to remove
      -// here we just grab the first
      -const ra = ras.find(v => true);
      -
      -// delete it
      -await list.roleAssignments.getById(ra.Id).delete();
      -
      -

      Role Definitions

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/security/web";
      -
      -// read role definitions
      -const defs = await sp.web.roleDefinitions();
      -
      -// get by id
      -const def = await sp.web.roleDefinitions.getById(5)();
      -const def = await sp.web.roleDefinitions.getById(5).select("Name", "Order")();
      -
      -// get by name
      -const def = await sp.web.roleDefinitions.getByName("Full Control")();
      -const def = await sp.web.roleDefinitions.getByName("Full Control").select("Name", "Order")();
      -
      -// get by type
      -const def = await sp.web.roleDefinitions.getByName(5)();
      -const def = await sp.web.roleDefinitions.getByName(5).select("Name", "Order")();
      -
      -// add
      -// name The new role definition's name
      -// description The new role definition's description
      -// order The order in which the role definition appears
      -// basePermissions The permissions mask for this role definition
      -const rdar = await sp.web.roleDefinitions.add("title", "description", 99, { High: 1, Low: 2 });
      -
      -
      -
      -// the following methods work on a single role def, you can use any of the three getBy methods, here we use getById as an example
      -
      -// delete
      -await sp.web.roleDefinitions.getById(5).delete();
      -
      -// update
      -const res = sp.web.roleDefinitions.getById(5).update({ Name: "New Name" });
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/sharing/index.html b/docs/v3/v2/sp/sharing/index.html deleted file mode 100644 index 8b41aadeb..000000000 --- a/docs/v3/v2/sp/sharing/index.html +++ /dev/null @@ -1,2589 +0,0 @@ - - - - - - - - - - - - - - - - - - Sharing - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      - -
      - - -
      -
      - - - - - - - -

      @pnp/sp/sharing

      -
      -

      Note: This API is still considered "beta" meaning it may change and some behaviors may differ across tenants by version. It is also supported only in SharePoint Online.

      -
      -

      One of the newer abilities in SharePoint is the ability to share webs, files, or folders with both internal and external folks. It is important to remember that these settings are managed at the tenant level and ? override anything you may supply as an argument to these methods. If you receive an InvalidOperationException when using these methods please check your tenant sharing settings to ensure sharing is not blocked before ?submitting an issue.

      -

      Imports

      -

      In previous versions of this library the sharing methods were part of the inheritance stack for SharePointQueryable objects. Starting with v2 this is no longer the case and they are now selectively importable. There are four objects within the SharePoint hierarchy that support sharing: Item, File, Folder, and Web. You can import the sharing methods for all of them, or for individual objects.

      -

      Import All

      -

      To import and attach the sharing methods to all four of the sharable types include all of the sharing sub module:

      -
      import "@pnp/sp/sharing";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-users/web";
      -import { sp } from "@pnp/sp";
      -
      -const user = await sp.web.siteUsers.getByEmail("user@site.com")();
      -const r = await sp.web.shareWith(user.LoginName);
      -
      -

      Selective Import

      -

      Import only the web's sharing methods into the library

      -
      import "@pnp/sp/sharing/web";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-users/web";
      -import { sp } from "@pnp/sp";
      -
      -const user = await sp.web.siteUsers.getByEmail("user@site.com")();
      -const r = await sp.web.shareWith(user.LoginName);
      -
      - -

      Applies to: Item, Folder, File

      -

      Creates a sharing link for the given resource with an optional expiration.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/sharing";
      -import { SharingLinkKind, IShareLinkResponse } from "@pnp/sp/sharing";
      -import { dateAdd } from "@pnp/core";
      -
      -const result = await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/folder1").getShareLink(SharingLinkKind.AnonymousView);
      -
      -console.log(JSON.stringify(result, null, 2));
      -
      -
      -const result2 = await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/folder1").getShareLink(SharingLinkKind.AnonymousView, dateAdd(new Date(), "day", 5));
      -
      -console.log(JSON.stringify(result2, null, 2));
      -
      -

      shareWith

      -

      Applies to: Item, Folder, File, Web

      -

      Shares the given resource with the specified permissions (View or Edit) and optionally sends an email to the users. You can supply a single string for the loginnames parameter or an array of loginnames. The folder method takes an optional parameter "shareEverything" which determines if the shared permissions are pushed down to all items in the folder, even those with unique permissions.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/sharing";
      -import "@pnp/sp/folders/web";
      -import "@pnp/sp/files/web";
      -import { ISharingResult, SharingRole } from "@pnp/sp/sharing";
      -
      -const result = await sp.web.shareWith("i:0#.f|membership|user@site.com");
      -
      -console.log(JSON.stringify(result, null, 2));
      -
      -// Share and allow editing
      -const result2 = await sp.web.shareWith("i:0#.f|membership|user@site.com", SharingRole.Edit);
      -
      -console.log(JSON.stringify(result2, null, 2));
      -
      -
      -// share folder
      -const result3 = await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/folder1").shareWith("i:0#.f|membership|user@site.com");
      -
      -// Share folder with edit permissions, and provide params for requireSignin and propagateAcl (apply to all children)
      -await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").shareWith("i:0#.f|membership|user@site.com", SharingRole.Edit, true, true);
      -
      -// Share a file
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/Shared Documents/test.txt").shareWith("i:0#.f|membership|user@site.com");
      -
      -// Share a file with edit permissions
      -await sp.web.getFileByServerRelativeUrl("/sites/dev/Shared Documents/test.txt").shareWith("i:0#.f|membership|user@site.com", SharingRole.Edit);
      -
      -

      shareObject & shareObjectRaw

      -

      Applies to: Web

      -

      Allows you to share any shareable object in a web by providing the appropriate parameters. These two methods differ in that shareObject will try and fix up your query based on the supplied parameters where shareObjectRaw will send your supplied json object directly to the server. The later method is provided for the greatest amount of flexibility.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/sharing";
      -import { ISharingResult, SharingRole } from "@pnp/sp/sharing";
      -
      -// Share an object in this web
      -const result = await sp.web.shareObject("https://mysite.sharepoint.com/sites/dev/Docs/test.txt", "i:0#.f|membership|user@site.com", SharingRole.View);
      -
      -// Share an object with all settings available
      -await sp.web.shareObjectRaw({
      -    url: "https://mysite.sharepoint.com/sites/dev/Docs/test.txt",
      -    peoplePickerInput: [{ Key: "i:0#.f|membership|user@site.com" }],
      -    roleValue: "role: 1973741327",
      -    groupId: 0,
      -    propagateAcl: false,
      -    sendEmail: true,
      -    includeAnonymousLinkInEmail: false,
      -    emailSubject: "subject",
      -    emailBody: "body",
      -    useSimplifiedRoles: true,
      -});
      -
      -

      unshareObject

      -

      Applies to: Web

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/sharing";
      -import { ISharingResult } from "@pnp/sp/sharing";
      -
      -const result = await sp.web.unshareObject("https://mysite.sharepoint.com/sites/dev/Docs/test.txt");
      -
      -

      checkSharingPermissions

      -

      Applies to: Item, Folder, File

      -

      Checks Permissions on the list of Users and returns back role the users have on the Item.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/sharing/folders";
      -import "@pnp/sp/folders/web";
      -import { SharingEntityPermission } from "@pnp/sp/sharing";
      -
      -// check the sharing permissions for a folder
      -const perms = await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").checkSharingPermissions([{ alias: "i:0#.f|membership|user@site.com" }]);
      -
      -

      getSharingInformation

      -

      Applies to: Item, Folder, File

      -

      Get Sharing Information.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/sharing";
      -import "@pnp/sp/folders";
      -import { ISharingInformation } from "@pnp/sp/sharing";
      -
      -// Get the sharing information for a folder
      -const info = await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").getSharingInformation();
      -
      -

      getObjectSharingSettings

      -

      Applies to: Item, Folder, File

      -

      Gets the sharing settings

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/sharing";
      -import "@pnp/sp/folders";
      -import { IObjectSharingSettings } from "@pnp/sp/sharing";
      -
      -// Gets the sharing object settings
      -const settings: IObjectSharingSettings = await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").getObjectSharingSettings();
      -
      -

      unshare

      -

      Applies to: Item, Folder, File

      -

      Unshares a given resource

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/sharing";
      -import "@pnp/sp/folders";
      -import { ISharingResult } from "@pnp/sp/sharing";
      -
      -const result: ISharingResult = await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").unshare();
      -
      -

      deleteSharingLinkByKind

      -

      Applies to: Item, Folder, File

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/sharing";
      -import "@pnp/sp/folders";
      -import { ISharingResult, SharingLinkKind } from "@pnp/sp/sharing";
      -
      -const result: ISharingResult = await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").deleteSharingLinkByKind(SharingLinkKind.AnonymousEdit);
      -
      - -

      Applies to: Item, Folder, File

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/sharing";
      -import "@pnp/sp/folders";
      -import { SharingLinkKind } from "@pnp/sp/sharing";
      -
      -await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").unshareLink(SharingLinkKind.AnonymousEdit);
      -
      -// specify the sharing link id if available
      -await sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").unshareLink(SharingLinkKind.AnonymousEdit, "12345");
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/site-designs/index.html b/docs/v3/v2/sp/site-designs/index.html deleted file mode 100644 index 031f075fa..000000000 --- a/docs/v3/v2/sp/site-designs/index.html +++ /dev/null @@ -1,2414 +0,0 @@ - - - - - - - - - - - - - - - - - - Site Designs - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/site-designs

      -

      You can create site designs to provide reusable lists, themes, layouts, pages, or custom actions so that your users can quickly build new SharePoint sites with the features they need. -Check out SharePoint site design and site script overview for more information.

      -

      Site Designs

      -

      Selective Imports Banner

      - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/site-designs";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      Create a new site design

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/site-designs";
      -
      -// WebTemplate: 64 Team site template, 68 Communication site template
      -const siteDesign = await sp.siteDesigns.createSiteDesign({
      -    SiteScriptIds: ["884ed56b-1aab-4653-95cf-4be0bfa5ef0a"],
      -    Title: "SiteDesign001",
      -    WebTemplate: "64",
      -});
      -
      -console.log(siteDesign.Title);
      -
      -

      Applying a site design to a site

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/site-designs";
      -
      -// Limited to 30 actions in a site script, but runs synchronously
      -await sp.siteDesigns.applySiteDesign("75b9d8fe-4381-45d9-88c6-b03f483ae6a8","https://contoso.sharepoint.com/sites/teamsite-pnpjs001");
      -
      -// Better use the following method for 300 actions in a site script
      -const task = await sp.web.addSiteDesignTask("75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
      -
      -

      Retrieval

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/site-designs";
      -
      -// Retrieving all site designs
      -const allSiteDesigns = await sp.siteDesigns.getSiteDesigns();
      -console.log(`Total site designs: ${allSiteDesigns.length}`);
      -
      -// Retrieving a single site design by Id
      -const siteDesign = await sp.siteDesigns.getSiteDesignMetadata("75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
      -console.log(siteDesign.Title);
      -
      -

      Update and delete

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/site-designs";
      -
      -// Update
      -const updatedSiteDesign = await sp.siteDesigns.updateSiteDesign({ Id: "75b9d8fe-4381-45d9-88c6-b03f483ae6a8", Title: "SiteDesignUpdatedTitle001" });
      -
      -// Delete
      -await sp.siteDesigns.deleteSiteDesign("75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
      -
      -

      Setting Rights/Permissions

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/site-designs";
      -
      -// Get
      -const rights = await sp.siteDesigns.getSiteDesignRights("75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
      -console.log(rights.length > 0 ? rights[0].PrincipalName : "");
      -
      -// Grant
      -await sp.siteDesigns.grantSiteDesignRights("75b9d8fe-4381-45d9-88c6-b03f483ae6a8", ["user@contoso.onmicrosoft.com"]);
      -
      -// Revoke
      -await sp.siteDesigns.revokeSiteDesignRights("75b9d8fe-4381-45d9-88c6-b03f483ae6a8", ["user@contoso.onmicrosoft.com"]);
      -
      -// Reset all view rights
      -const rights = await sp.siteDesigns.getSiteDesignRights("75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
      -await sp.siteDesigns.revokeSiteDesignRights("75b9d8fe-4381-45d9-88c6-b03f483ae6a8", rights.map(u => u.PrincipalName));
      -
      -

      Get a history of site designs that have run on a web

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/site-designs";
      -
      -const runs = await sp.web.getSiteDesignRuns();
      -const runs2 = await sp.siteDesigns.getSiteDesignRun("https://TENANT.sharepoint.com/sites/mysite");
      -
      -// Get runs specific to a site design
      -const runs3 = await sp.web.getSiteDesignRuns("75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
      -const runs4 = await sp.siteDesigns.getSiteDesignRun("https://TENANT.sharepoint.com/sites/mysite", "75b9d8fe-4381-45d9-88c6-b03f483ae6a8");
      -
      -// For more information about the site script actions
      -const runStatus = await sp.web.getSiteDesignRunStatus(runs[0].ID);
      -const runStatus2 = await sp.siteDesigns.getSiteDesignRunStatus("https://TENANT.sharepoint.com/sites/mysite", runs[0].ID);
      -
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/site-groups/index.html b/docs/v3/v2/sp/site-groups/index.html deleted file mode 100644 index 24bed7a48..000000000 --- a/docs/v3/v2/sp/site-groups/index.html +++ /dev/null @@ -1,2504 +0,0 @@ - - - - - - - - - - - - - - - - - - Site Groups - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/site-groups

      -

      The site groups module provides methods to manage groups for a sharepoint site.

      -

      ISiteGroups

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/site-groups";
      Selective 3import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/site-groups/web";
      Preset: Allimport {sp, SiteGroups } from "@pnp/sp/presets/all";
      -

      Get all site groups

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-groups/web";
      -
      -// gets all site groups of the web
      -const groups = await sp.web.siteGroups();
      -
      -

      Get the associated groups of a web

      -

      You can get the associated Owner, Member and Visitor groups of a web

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-groups/web";
      -
      -// Gets the associated visitors group of a web
      -const visitorGroup = await sp.web.associatedVisitorGroup();
      -
      -// Gets the associated members group of a web
      -const memberGroup = await sp.web.associatedMemberGroup();
      -
      -// Gets the associated owners group of a web
      -const ownerGroup = await sp.web.associatedOwnerGroup();
      -
      -
      -

      Create the default associated groups for a web

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-groups/web";
      -
      -// Breaks permission inheritance and creates the default associated groups for the web
      -
      -// Login name of the owner
      -const owner1 = "owner@example.onmicrosoft.com";
      -
      -// Specify true, the permissions should be copied from the current parent scope, else false
      -const copyRoleAssignments = false;
      -
      -// Specify true to make all child securable objects inherit role assignments from the current object
      -const clearSubScopes = true;
      -
      -await sp.web.createDefaultAssociatedGroups("PnP Site", owner1, copyRoleAssignments, clearSubScopes);
      -
      -

      Create a new site group

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-groups/web";
      -
      -// Creates a new site group with the specified title
      -await sp.web.siteGroups.add({"Title":"new group name"});
      -
      -

      ISiteGroup

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/site-groups";
      Selective 3import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/site-groups/web";
      Preset: Allimport {sp, SiteGroups, SiteGroup } from "@pnp/sp/presets/all";
      -

      Getting and updating the groups of a sharepoint web

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-groups";
      -
      -// get the group using a group id
      -const groupID = 33;
      -let grp = await sp.web.siteGroups.getById(groupID)();
      -
      -// get the group using the group's name
      -const groupName = "ClassicTeam Visitors";
      -grp = await sp.web.siteGroups.getByName(groupName)();
      -
      -// update a group
      -await sp.web.siteGroups.getById(groupID).update({"Title": "New Group Title"});
      -
      -// delete a group from the site using group id
      -await sp.web.siteGroups.removeById(groupID);
      -
      -// delete a group from the site using group name
      -await sp.web.siteGroups.removeByLoginName(groupName);
      -
      -

      Getting all users of a group

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-groups";
      -
      -// get all users of group
      -const groupID = 7;
      -const users = await sp.web.siteGroups.getById(groupID).users();
      -
      -

      Updating the owner of a site group

      -

      Unfortunately for now setting the owner of a group as another or same SharePoint group is currently unsupported in REST. Setting the owner as a user is supported.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-groups";
      -
      -// Update the owner with a user id
      -await sp.web.siteGroups.getById(7).setUserAsOwner(4);
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/site-scripts/index.html b/docs/v3/v2/sp/site-scripts/index.html deleted file mode 100644 index 96c986f93..000000000 --- a/docs/v3/v2/sp/site-scripts/index.html +++ /dev/null @@ -1,2433 +0,0 @@ - - - - - - - - - - - - - - - - - - Site Scripts - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/site-scripts

      -

      Selective Imports Banner

      - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/site-scripts";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      Create a new site script

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/site-scripts";
      -
      -const sitescriptContent = {
      -    "$schema": "schema.json",
      -    "actions": [
      -        {
      -            "themeName": "Theme Name 123",
      -            "verb": "applyTheme",
      -        },
      -    ],
      -    "bindata": {},
      -    "version": 1,
      -};
      -
      -const siteScript = await sp.siteScripts.createSiteScript("Title", "description", sitescriptContent);
      -
      -console.log(siteScript.Title);
      -
      -

      Retrieval

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/site-scripts";
      -
      -// Retrieving all site scripts
      -const allSiteScripts = await sp.siteScripts.getSiteScripts();
      -console.log(allSiteScripts.length > 0 ? allSiteScripts[0].Title : "");
      -
      -// Retrieving a single site script by Id
      -const siteScript = await sp.siteScripts.getSiteScriptMetadata("884ed56b-1aab-4653-95cf-4be0bfa5ef0a");
      -console.log(siteScript.Title);
      -
      -

      Update and delete

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/site-scripts";
      -
      -// Update
      -const updatedSiteScript = await sp.siteScripts.updateSiteScript({ Id: "884ed56b-1aab-4653-95cf-4be0bfa5ef0a", Title: "New Title" });
      -console.log(updatedSiteScript.Title);
      -
      -// Delete
      -await sp.siteScripts.deleteSiteScript("884ed56b-1aab-4653-95cf-4be0bfa5ef0a");
      -
      -

      Get site script from a list

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/site-scripts";
      -
      -// Using the absolute URL of the list
      -const ss = await sp.siteScripts.getSiteScriptFromList("https://TENANT.sharepoint.com/Lists/mylist");
      -
      -// Using the PnPjs web object to fetch the site script from a specific list
      -const ss2 = await sp.web.lists.getByTitle("mylist").getSiteScript();
      -
      -

      Get site script from a web

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/site-scripts";
      -
      -const extractInfo = {
      -    IncludeBranding: true,
      -    IncludeLinksToExportedItems: true,
      -    IncludeRegionalSettings: true,
      -    IncludeSiteExternalSharingCapability: true,
      -    IncludeTheme: true,
      -    IncludedLists: ["Lists/MyList"]
      -};
      -
      -const ss = await sp.siteScripts.getSiteScriptFromWeb("https://TENANT.sharepoint.com/sites/mysite", extractInfo);
      -
      -// Using the PnPjs web object to fetch the site script from a specific web
      -const ss2 = await sp.web.getSiteScript(extractInfo);
      -
      -

      Execute Site Script Action

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/site-scripts";
      -
      -const siteScript = "your site script action...";
      -
      -const ss = await sp.siteScripts.executeSiteScriptAction(siteScript);
      -
      -

      Execute site script for a specific web

      -
      import { sp } from "@pnp/sp";
      -import { SiteScripts } "@pnp/sp/site-scripts";
      -
      -const siteScript = "your site script action...";
      -
      -const scriptService = SiteScripts("https://absolute/url/to/web");
      -
      -const ss = await scriptService.executeSiteScriptAction(siteScript);
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/site-users/index.html b/docs/v3/v2/sp/site-users/index.html deleted file mode 100644 index d2b35af84..000000000 --- a/docs/v3/v2/sp/site-users/index.html +++ /dev/null @@ -1,2641 +0,0 @@ - - - - - - - - - - - - - - - - - - Site Users - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/site-users

      -

      The site users module provides methods to manage users for a sharepoint site.

      -

      ISiteUsers

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/site-users";
      Selective 3import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/site-users/web";
      Preset: Allimport {sp, SiteUsers } from "@pnp/sp/presets/all";
      -

      Get all site user

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-users/web";
      -
      -const users = await sp.web.siteUsers();
      -
      -

      Get Current user

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-users/web";
      -
      -let user = await sp.web.currentUser();
      -
      -

      Get user by id

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-users/web";
      -
      -const id = 6;
      -user = await sp.web.getUserById(id);
      -
      -

      Ensure user

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-users/web";
      -
      -const username = "usernames@microsoft.com";
      -result = await sp.web.ensureUser(username);
      -
      -

      ISiteUser

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/site-users";
      Selective 3import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/site-users/web";
      Preset: Allimport {sp, SiteUsers, SiteUser } from "@pnp/sp/presets/all";
      -

      Get user Groups

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-users/web";
      -
      -let groups = await sp.web.currentUser.groups();
      -
      -

      Add user to Site collection

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-users/web";
      -
      -const user = await sp.web.ensureUser("userLoginname")
      -const users = await sp.web.siteUsers;
      -
      -await users.add(user.data.LoginName);
      -
      -

      Get user

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-users/web";
      -
      -// get user object by id
      -const user = await sp.web.siteUsers.getById(6);
      -
      -//get user object by Email
      -const user = await sp.web.siteUsers.getByEmail("user@mail.com");
      -
      -//get user object by LoginName
      -const user = await sp.web.siteUsers.getByLoginName("userLoginName");
      -
      -

      Update user

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-users/web";
      -
      -let userProps = await sp.web.currentUser();
      -userProps.Title = "New title";
      -await sp.web.currentUser.update(userProps);
      -
      -

      Remove user

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/site-users/web";
      -
      -// remove user by id
      -await sp.web.siteUsers.removeById(6);
      -
      -// remove user by LoginName
      -await sp.web.siteUsers.removeByLoginName(6);
      -
      -

      ISiteUserProps

      -

      User properties:

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      Property NameTypeDescription
      EmailstringContains Site user email
      IdNumberContains Site user Id
      IsHiddenInUIBooleanSite user IsHiddenInUI
      IsShareByEmailGuestUserbooleanSite user is external user
      IsSiteAdminBooleanDescribes if Site user Is Site Admin
      LoginNamestringSite user LoginName
      PrincipalTypenumberSite user Principal type
      TitlestringSite user Title
      -
      interface ISiteUserProps {
      -
      -    /**
      -     * Contains Site user email
      -     *
      -     */
      -    Email: string;
      -
      -    /**
      -     * Contains Site user Id
      -     *
      -     */
      -    Id: number;
      -
      -    /**
      -     * Site user IsHiddenInUI
      -     *
      -     */
      -    IsHiddenInUI: boolean;
      -
      -    /**
      -     * Site user IsShareByEmailGuestUser
      -     *
      -     */
      -    IsShareByEmailGuestUser: boolean;
      -
      -    /**
      -     * Describes if Site user Is Site Admin
      -     *
      -     */
      -    IsSiteAdmin: boolean;
      -
      -    /**
      -     * Site user LoginName
      -     *
      -     */
      -    LoginName: string;
      -
      -    /**
      -     * Site user Principal type
      -     *
      -     */
      -    PrincipalType: number | PrincipalType;
      -
      -    /**
      -     * Site user Title
      -     *
      -     */
      -    Title: string;
      -}
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/sites/index.html b/docs/v3/v2/sp/sites/index.html deleted file mode 100644 index 6c61ef4bc..000000000 --- a/docs/v3/v2/sp/sites/index.html +++ /dev/null @@ -1,2727 +0,0 @@ - - - - - - - - - - - - - - - - - - Sites - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/site - Site properties

      -

      Site collection are one of the fundamental entry points while working with SharePoint. Sites serve as container for webs, lists, features and other entity types.

      -

      Get context information for the current site collection

      -

      Using the library, you can get the context information of the current site collection

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/sites";
      -import { IContextInfo } from "@pnp/sp/sites";
      -
      -const oContext: IContextInfo = await sp.site.getContextInfo();
      -console.log(oContext.FormDigestValue);
      -
      -

      Get document libraries of a web

      -

      Using the library, you can get a list of the document libraries present in the a given web.

      -

      Note: Works only in SharePoint online

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/sites";
      -import { IDocumentLibraryInformation } from "@pnp/sp/sites";
      -
      -const docLibs: IDocumentLibraryInformation[] = await sp.site.getDocumentLibraries("https://tenant.sharepoint.com/sites/test/subsite");
      -
      -//we got the array of document library information
      -docLibs.forEach((docLib: IDocumentLibraryInformation) => {
      -    // do something with each library
      -});
      -
      -

      Open Web By Id

      -

      Because this method is a POST request you can chain off it directly. You will get back the full web properties in the data property of the return object. You can also chain directly off the returned Web instance on the web property.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/sites";
      -
      -const w = await sp.site.openWebById("111ca453-90f5-482e-a381-cee1ff383c9e");
      -
      -//we got all the data from the web as well
      -console.log(w.data);
      -
      -// we can chain
      -const w2 = await w.web.select("Title")();
      -
      -

      Get site collection url from page

      -

      Using the library, you can get the site collection url by providing a page url

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/sites";
      -
      -const d: string = await sp.site.getWebUrlFromPageUrl("https://tenant.sharepoint.com/sites/test/Pages/test.aspx");
      -
      -console.log(d); //https://tenant.sharepoint.com/sites/test
      -
      -

      Access the root web

      -

      There are two methods to access the root web. The first, using the rootWeb property, is best for directly accessing information about that web. If you want to chain multiple operations off of the web, better to use the getRootWeb method that will ensure the web instance is created using its own Url vs. "_api/sites/rootweb" which does not work for all operations.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/sites";
      -
      -// use for rootweb information access
      -const rootwebData = await sp.site.rootWeb();
      -
      -// use for chaining
      -const rootweb = await sp.site.getRootWeb();
      -const listData = await rootWeb.lists.getByTitle("MyList")();
      -
      -

      Create a modern communication site

      -

      Note: Works only in SharePoint online

      -

      Creates a modern communication site.

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      PropertyTypeRequiredDescription
      TitlestringyesThe title of the site to create.
      lcidnumberyesThe default language to use for the site.
      shareByEmailEnabledbooleanyesIf set to true, it will enable sharing files via Email. By default it is set to false
      urlstringyesThe fully qualified URL (e.g. https://yourtenant.sharepoint.com/sites/mysitecollection) of the site.
      descriptionstringnoThe description of the communication site.
      classificationstringnoThe Site classification to use. For instance "Contoso Classified". See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information
      siteDesignIdstringnoThe Guid of the site design to be used.
      You can use the below default OOTB GUIDs:
      Topic: null
      Showcase: 6142d2a0-63a5-4ba0-aede-d9fefca2c767
      Blank: f6cc5403-0d63-442e-96c0-285923709ffc
      hubSiteIdstringnoThe Guid of the already existing Hub site
      OwnerstringnoRequired when using app-only context. Owner principal name e.g. user@tenant.onmicrosoft.com
      -
      
      -import { sp } from "@pnp/sp";
      -import "@pnp/sp/sites";
      -
      -const result = await sp.site.createCommunicationSite(
      -            "Title",
      -            1033,
      -            true,
      -            "https://tenant.sharepoint.com/sites/commSite",
      -            "Description",
      -            "HBI",
      -            "f6cc5403-0d63-442e-96c0-285923709ffc",
      -            "a00ec589-ea9f-4dba-a34e-67e78d41e509",
      -            "user@TENANT.onmicrosoft.com");
      -
      -
      -

      Create from Props

      -

      You may need to supply additional parameters such as WebTemplate, to do so please use the createCommunicationSiteFromProps method.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/sites";
      -
      -// in this case you supply a single struct deinfing the creation props
      -const result = await sp.site.createCommunicationSiteFromProps({
      -  Owner: "patrick@three18studios.com",
      -  Title: "A Test Site",
      -  Url: "https://{tenant}.sharepoint.com/sites/commsite2",
      -  WebTemplate: "STS#3",
      -});
      -
      -

      Create a modern team site

      -

      Note: Works only in SharePoint online. It wont work with App only tokens

      -

      Creates a modern team site backed by O365 group.

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      PropertyTypeRequiredDescription
      displayNamestringyesThe title/displayName of the site to be created.
      aliasstringyesAlias of the underlying Office 365 Group.
      isPublicbooleanyesDefines whether the Office 365 Group will be public (default), or private.
      lcidnumberyesThe language to use for the site. If not specified will default to English (1033).
      descriptionstringnoThe description of the modern team site.
      classificationstringnoThe Site classification to use. For instance "Contoso Classified". See https://www.youtube.com/watch?v=E-8Z2ggHcS0 for more information
      ownersstring array (string[])noThe Owners of the site to be created
      hubSiteIdstringnoThe Guid of the already existing Hub site
      siteDesignIdstringnoThe Guid of the site design to be used.
      You can use the below default OOTB GUIDs:
      Topic: null
      Showcase: 6142d2a0-63a5-4ba0-aede-d9fefca2c767
      Blank: f6cc5403-0d63-442e-96c0-285923709ffc
      -
      
      -import { sp } from "@pnp/sp";
      -import "@pnp/sp/sites";
      -
      -const result = await sp.site.createModernTeamSite(
      -        "displayName",
      -        "alias",
      -        true,
      -        1033,
      -        "description",
      -        "HBI",
      -        ["user1@tenant.onmicrosoft.com","user2@tenant.onmicrosoft.com","user3@tenant.onmicrosoft.com"],
      -        "a00ec589-ea9f-4dba-a34e-67e78d41e509",
      -        "f6cc5403-0d63-442e-96c0-285923709ffc"
      -        );
      -
      -console.log(d);
      -
      -

      Create from Props

      -

      You may need to supply additional parameters, to do so please use the createModernTeamSiteFromProps method.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/sites";
      -
      -// in this case you supply a single struct deinfing the creation props
      -const result = await sp.site.createModernTeamSiteFromProps({
      -  alias: "JenniferGarner",
      -  displayName: "A Test Site",
      -  owners: ["patrick@three18studios.com"],
      -});
      -
      -

      Delete a site collection

      -

      Using the library, you can delete a specific site collection

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/sites";
      -import { Site } from "@pnp/sp/sites";
      -
      -// Delete the current site
      -await sp.site.delete();
      -
      -// Specify which site to delete
      -const siteUrl = "https://tenant.sharepoint.com/sites/subsite";
      -const site2 = Site(siteUrl);
      -await site2.delete();
      -
      -

      Check if a Site Collection Exists

      -

      Using the library, you can check if a specific site collection exist or not on your tenant

      -
      import { sp } from "@pnp/sp";
      -
      -// Specify which site to verify
      -const siteUrl = "https://tenant.sharepoint.com/sites/subsite";
      -const exists = sp.site.exists(siteUrl);
      -console.log(exists);
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/social/index.html b/docs/v3/v2/sp/social/index.html deleted file mode 100644 index 9c01b9177..000000000 --- a/docs/v3/v2/sp/social/index.html +++ /dev/null @@ -1,2517 +0,0 @@ - - - - - - - - - - - - - - - - - - Social - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/ - social

      -

      Selective Imports Banner

      - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import "@pnp/sp/social";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      The social API allows you to track followed sites, people, and docs. Note, many of these methods only work with the context of a logged in user, and not -with app-only permissions.

      -

      getFollowedSitesUri

      -

      Gets a URI to a site that lists the current user's followed sites.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/social";
      -
      -const uri = await sp.social.getFollowedSitesUri();
      -
      -

      getFollowedDocumentsUri

      -

      Gets a URI to a site that lists the current user's followed documents.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/social";
      -
      -const uri = await sp.social.getFollowedDocumentsUri();
      -
      -

      follow

      -

      Makes the current user start following a user, document, site, or tag

      -
      import { sp } from "@pnp/sp";
      -import { SocialActorType } from "@pnp/sp/social";
      -
      -// follow a site
      -const r1 = await sp.social.follow({
      -    ActorType: SocialActorType.Site,
      -    ContentUri: "htts://tenant.sharepoint.com/sites/site",
      -});
      -
      -// follow a person
      -const r2 = await sp.social.follow({
      -    AccountName: "i:0#.f|membership|person@tenant.com",
      -    ActorType: SocialActorType.User,
      -});
      -
      -// follow a doc
      -const r3 = await sp.social.follow({
      -    ActorType: SocialActorType.Document,
      -    ContentUri: "https://tenant.sharepoint.com/sites/dev/SitePages/Test.aspx",
      -});
      -
      -// follow a tag
      -// You need the tag GUID to start following a tag.
      -// You can't get the GUID by using the REST service, but you can use the .NET client object model or the JavaScript object model.
      -// See How to get a tag's GUID based on the tag's name by using the JavaScript object model.
      -// https://docs.microsoft.com/en-us/sharepoint/dev/general-development/follow-content-in-sharepoint#bk_getTagGuid
      -const r4 = await sp.social.follow({
      -    ActorType: SocialActorType.Tag,
      -    TagGuid: "19a4a484-c1dc-4bc5-8c93-bb96245ce928",
      -});
      -
      -

      isFollowed

      -

      Indicates whether the current user is following a specified user, document, site, or tag

      -
      import { sp } from "@pnp/sp";
      -import { SocialActorType } from "@pnp/sp/social";
      -
      -// pass the same social actor struct as shown in follow example for each type
      -const r = await sp.social.isFollowed({
      -    AccountName: "i:0#.f|membership|person@tenant.com",
      -    ActorType: SocialActorType.User,
      -});
      -
      -

      stopFollowing

      -

      Makes the current user stop following a user, document, site, or tag

      -
      import { sp } from "@pnp/sp";
      -import { SocialActorType } from "@pnp/sp/social";
      -
      -// pass the same social actor struct as shown in follow example for each type
      -const r = await sp.social.stopFollowing({
      -    AccountName: "i:0#.f|membership|person@tenant.com",
      -    ActorType: SocialActorType.User,
      -});
      -
      -

      my

      -

      get

      -

      Gets this user's social information

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/social";
      -
      -const r = await sp.social.my();
      -
      -

      followed

      -

      Gets users, documents, sites, and tags that the current user is following based on the supplied flags.

      -
      import { sp } from "@pnp/sp";
      -import { SocialActorType } from "@pnp/sp/social";
      -
      -// get all the followed documents
      -const r1 = await sp.social.my.followed(SocialActorTypes.Document);
      -
      -// get all the followed documents and sites
      -const r2 = await sp.social.my.followed(SocialActorTypes.Document | SocialActorTypes.Site);
      -
      -// get all the followed sites updated in the last 24 hours
      -const r3 = await sp.social.my.followed(SocialActorTypes.Site | SocialActorTypes.WithinLast24Hours);
      -
      -

      followedCount

      -

      Works as followed but returns on the count of actors specified by the query

      -
      import { sp } from "@pnp/sp";
      -import { SocialActorType } from "@pnp/sp/social";
      -
      -// get the followed documents count
      -const r = await sp.social.my.followedCount(SocialActorTypes.Document);
      -
      -

      followers

      -

      Gets the users who are following the current user.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/social";
      -
      -// get the followed documents count
      -const r = await sp.social.my.followers();
      -
      -

      suggestions

      -

      Gets users who the current user might want to follow.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/social";
      -
      -// get the followed documents count
      -const r = await sp.social.my.suggestions();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/sp-utilities-utility/index.html b/docs/v3/v2/sp/sp-utilities-utility/index.html deleted file mode 100644 index 571ffbbe8..000000000 --- a/docs/v3/v2/sp/sp-utilities-utility/index.html +++ /dev/null @@ -1,2460 +0,0 @@ - - - - - - - - - - - - - - - - - - SP.Utilities.Utility - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/utilities

      -

      Through the REST api you are able to call a subset of the SP.Utilities.Utility methods. We have explicitly defined some of these methods and provided a method to call any others in a generic manner. These methods are exposed on pnp.sp.utility and support batching and caching.

      -

      sendEmail

      -

      This methods allows you to send an email based on the supplied arguments. The method takes a single argument, a plain object defined by the EmailProperties interface (shown below).

      -

      EmailProperties

      -
      export interface EmailProperties {
      -
      -    To: string[];
      -    CC?: string[];
      -    BCC?: string[];
      -    Subject: string;
      -    Body: string;
      -    AdditionalHeaders?: TypedHash<string>;
      -    From?: string;
      -}
      -
      -

      Usage

      -

      You must define the To, Subject, and Body values - the remaining are optional.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/sputilities";
      -import { IEmailProperties } from "@pnp/sp/sputilities";
      -
      -const emailProps: IEmailProperties = {
      -    To: ["user@site.com"],
      -    CC: ["user2@site.com", "user3@site.com"],
      -    BCC: ["user4@site.com", "user5@site.com"],
      -    Subject: "This email is about...",
      -    Body: "Here is the body. <b>It supports html</b>",
      -    AdditionalHeaders: {
      -        "content-type": "text/html"
      -    }
      -};
      -
      -await sp.utility.sendEmail(emailProps);
      -console.log("Email Sent!");
      -
      -

      getCurrentUserEmailAddresses

      -

      This method returns the current user's email addresses known to SharePoint.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/sputilities";
      -
      -let addressString: string = await sp.utility.getCurrentUserEmailAddresses();
      -
      -// and use it with sendEmail
      -await sp.utility.sendEmail({
      -    To: [addressString],
      -    Subject: "This email is about...",
      -    Body: "Here is the body. <b>It supports html</b>",
      -    AdditionalHeaders: {
      -        "content-type": "text/html"
      -    },
      -});
      -
      -

      resolvePrincipal

      -

      Gets information about a principal that matches the specified Search criteria

      -
      import { sp, IPrincipalInfo, PrincipalType, PrincipalSource } from "@pnp/sp";
      -import "@pnp/sp/sputilities";
      -
      -let principal : IPrincipalInfo = await sp.utility.resolvePrincipal("user@site.com", PrincipalType.User, PrincipalSource.All, true, false, true);
      -
      -console.log(principal);
      -
      -

      searchPrincipals

      -

      Gets information about the principals that match the specified Search criteria.

      -
      import { sp, IPrincipalInfo, PrincipalType, PrincipalSource } from "@pnp/sp";
      -import "@pnp/sp/sputilities";
      -
      -let principals : IPrincipalInfo[] = await sp.utility.searchPrincipals("john", PrincipalType.User, PrincipalSource.All,"", 10);
      -
      -console.log(principals);
      -
      -

      createEmailBodyForInvitation

      -

      Gets the external (outside the firewall) URL to a document or resource in a site.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/sputilities";
      -
      -let url : string = await sp.utility.createEmailBodyForInvitation("https://contoso.sharepoint.com/sites/dev/SitePages/DevHome.aspx");
      -console.log(url);
      -
      -

      expandGroupsToPrincipals

      -

      Resolves the principals contained within the supplied groups

      -
      import { sp, IPrincipalInfo } from "@pnp/sp";
      -import "@pnp/sp/sputilities";
      -
      -let principals : IPrincipalInfo[] = await sp.utility.expandGroupsToPrincipals(["Dev Owners", "Dev Members"]);
      -console.log(principals);
      -
      -// optionally supply a max results count. Default is 30.
      -let principals : IPrincipalInfo[] = await sp.utility.expandGroupsToPrincipals(["Dev Owners", "Dev Members"], 10);
      -console.log(principals);
      -
      -

      createWikiPage

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/sputilities";
      -import { ICreateWikiPageResult } from "@pnp/sp/sputilities";
      -
      -let newPage : ICreateWikiPageResult = await sp.utility.createWikiPage({
      -    ServerRelativeUrl: "/sites/dev/SitePages/mynewpage.aspx",
      -    WikiHtmlContent: "This is my <b>page</b> content. It supports rich html.",
      -});
      -
      -// newPage contains the raw data returned by the service
      -console.log(newPage.data);
      -
      -// newPage contains a File instance you can use to further update the new page
      -let file = await newPage.file();
      -console.log(file);
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/subscriptions/index.html b/docs/v3/v2/sp/subscriptions/index.html deleted file mode 100644 index daac7a95b..000000000 --- a/docs/v3/v2/sp/subscriptions/index.html +++ /dev/null @@ -1,2395 +0,0 @@ - - - - - - - - - - - - - - - - - - Subscriptions - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/subscriptions

      -

      Webhooks on a SharePoint list are used to notify any change in the list, to other applications using a push model. This module provides methods to add, update or delete webhooks on a particular SharePoint list or library.

      -

      ISubscriptions

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selectiveimport { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/lists";
      import { Subscriptions, ISubscriptions} from "@pnp/sp/subscriptions";
      import "@pnp/sp/subscriptions/list"
      Preset: Allimport {sp, Webs, IWebs, Lists, ILists, Subscriptions, ISubscriptions, Subscription, ISubscription} from "@pnp/sp/presets/all";
      -

      Add a webhook

      -

      Using this library, you can add a webhook to a specified list within the SharePoint site.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -
      -import { Subscriptions, ISubscriptions} from "@pnp/sp/subscriptions";
      -import "@pnp/sp/subscriptions/list";
      -
      -// This is the URL which will be called by SharePoint when there is a change in the list
      -const notificationUrl = "<notification-url>";
      -
      -// Set the expiry date to 180 days from now, which is the maximum allowed for the webhook expiry date.
      -const expiryDate = dateAdd(new Date(), "day" , 180).toISOString();
      -
      -// Adds a webhook to the Documents library
      -var res = await sp.web.lists.getByTitle("Documents").subscriptions.add(notificationUrl,expiryDate);
      -
      -

      Get all webhooks added to a list

      -

      Read all the webhooks' details which are associated to the list

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/subscriptions";
      -
      -const res = await sp.web.lists.getByTitle("Documents").subscriptions();
      -
      -

      ISubscription

      -

      This interface provides the methods for managing a particular webhook.

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selectiveimport { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      import "@pnp/sp/lists";
      import { Subscriptions, ISubscriptions, Subscription, ISubscription} from "@pnp/sp/subscriptions";
      import "@pnp/sp/subscriptions/list"
      Preset: Allimport { sp, Webs, IWebs, Lists, ILists, Subscriptions, ISubscriptions, Subscription, ISubscription } from "@pnp/sp/presets/all";
      -

      Managing a webhook

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/subscriptions";
      -
      -// Get details of a webhook based on its ID
      -const webhookId = "1f029e5c-16e4-4941-b46f-67895118763f";
      -const webhook = await sp.web.lists.getByTitle("Documents").subscriptions.getById(webhookId)();
      -
      -// Update a webhook
      -const newDate = dateAdd(new Date(), "day" , 150).toISOString();
      -const updatedWebhook = await sp.web.lists.getByTitle("Documents").subscriptions.getById(webhookId).update(newDate);
      -
      -// Delete a webhook
      -await sp.web.lists.getByTitle("Documents").subscriptions.getById(webhookId).delete();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/taxonomy/index.html b/docs/v3/v2/sp/taxonomy/index.html deleted file mode 100644 index 72cd16f12..000000000 --- a/docs/v3/v2/sp/taxonomy/index.html +++ /dev/null @@ -1,2565 +0,0 @@ - - - - - - - - - - - - - - - - - - Taxonomy - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/taxonomy

      -

      Provides access to the v2.1 api term store

      -

      Docs updated with v2.0.9 release as the underlying API changed.

      -
      -

      NOTE: This API may change so please be aware updates to the taxonomy module will not trigger a major version bump in PnPjs even if they are breaking. Once things stabalize this note will be removed.

      -
      -

      Invokable Banner Selective Imports Banner

      -

      Term Store

      -

      Access term store data from the root sp object as shown below.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/taxonomy";
      -import { ITermStoreInfo } from "@pnp/sp/taxonomy";
      -
      -// get term store data
      -const info: ITermStoreInfo = await sp.termStore();
      -
      -

      Term Groups

      -

      Access term group information

      -

      List

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/taxonomy";
      -import { ITermGroupInfo } from "@pnp/sp/taxonomy";
      -
      -// get term groups
      -const info: ITermGroupInfo[] = await sp.termStore.groups();
      -
      -

      Get By Id

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/taxonomy";
      -import { ITermGroupInfo } from "@pnp/sp/taxonomy";
      -
      -// get term groups data
      -const info: ITermGroupInfo = await sp.termStore.groups.getById("338666a8-1111-2222-3333-f72471314e72")();
      -
      -

      Term Sets

      -

      Access term set information

      -

      List

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/taxonomy";
      -import { ITermSetInfo } from "@pnp/sp/taxonomy";
      -
      -// get get set info
      -const info: ITermSetInfo[] = await sp.termStore.groups.getById("338666a8-1111-2222-3333-f72471314e72").sets();
      -
      -

      Get By Id

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/taxonomy";
      -import { ITermSetInfo } from "@pnp/sp/taxonomy";
      -
      -// get term set data
      -const info: ITermSetInfo = await sp.termStore.groups.getById("338666a8-1111-2222-3333-f72471314e72").sets.getById("338666a8-1111-2222-3333-f72471314e72")();
      -
      -

      getAllChildrenAsOrderedTree

      -

      Added in 2.0.13

      -

      This method will get all of a set's child terms in an ordered array. It is a costly method in terms of requests so we suggest you cache the results as taxonomy trees seldom change.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/taxonomy";
      -import { ITermInfo } from "@pnp/sp/taxonomy";
      -import { dateAdd, PnPClientStorage } from "@pnp/core";
      -
      -// here we get all the children of a given set
      -const childTree = await sp.termStore.groups.getById("338666a8-1111-2222-3333-f72471314e72").sets.getById("338666a8-1111-2222-3333-f72471314e72").getAllChildrenAsOrderedTree();
      -
      -// here we show caching the results using the PnPClientStorage class, there are many caching libraries and options available
      -const store = new PnPClientStorage();
      -
      -// our tree likely doesn't change much in 30 minutes for most applications
      -// adjust to be longer or shorter as needed
      -const cachedTree = await store.local.getOrPut("myKey", () => {
      -    return sp.termStore.groups.getById("338666a8-1111-2222-3333-f72471314e72").sets.getById("338666a8-1111-2222-3333-f72471314e72").getAllChildrenAsOrderedTree();
      -}, dateAdd(new Date(), "minute", 30));
      -
      -

      Terms

      -

      Access term set information

      -

      List

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/taxonomy";
      -import { ITermInfo } from "@pnp/sp/taxonomy";
      -
      -// list all the terms that are direct children of this set
      -const infos: ITermInfo[] = await sp.termStore.groups.getById("338666a8-1111-2222-3333-f72471314e72").sets.getById("338666a8-1111-2222-3333-f72471314e72").children();
      -
      -

      List (terms)

      -

      Added in 2.0.13

      -

      You can use the terms property to get a flat list of all terms in the set. These terms do not contain parent/child relationship information.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/taxonomy";
      -import { ITermInfo } from "@pnp/sp/taxonomy";
      -
      -// list all the terms that are direct children of this set
      -const infos: ITermInfo[] = await sp.termStore.groups.getById("338666a8-1111-2222-3333-f72471314e72").sets.getById("338666a8-1111-2222-3333-f72471314e72").terms();
      -
      -

      Get By Id

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/taxonomy";
      -import { ITermInfo } from "@pnp/sp/taxonomy";
      -
      -// get term set data
      -const info: ITermInfo = await sp.termStore.groups.getById("338666a8-1111-2222-3333-f72471314e72").sets.getById("338666a8-1111-2222-3333-f72471314e72").getTermById("338666a8-1111-2222-3333-f72471314e72")();
      -
      -

      Get Term Parent

      -

      Behavior Change in 2.1.0

      -

      The server API changed again, resulting in the removal of the "parent" property from ITerm as it is not longer supported as a path property. You now must use "expand" to load a term's parent information. The side affect of this is that the parent is no longer chainable, meaning you need to load a new term instance to work with the parent term. An approach for this is shown below.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/taxonomy";
      -
      -// get a ref to the set
      -const set = sp.termStore.groups.getById("338666a8-1111-2222-3333-f72471314e72").sets.getById("338666a8-1111-2222-3333-f72471314e72");
      -
      -// get a term's information and expand parent to get the parent info as well
      -const w = await set.getTermById("338666a8-1111-2222-3333-f72471314e72").expand("parent")();
      -
      -// get a ref to the parent term
      -const parent = set.getTermById(w.parent.id);
      -
      -// make a request for the parent term's info - this data currently match the results in the expand call above, but this
      -// is to demonstrate how to gain a ref to the parent and select its data
      -const parentInfo = await parent.select("Id", "Descriptions")();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/tenant-properties/index.html b/docs/v3/v2/sp/tenant-properties/index.html deleted file mode 100644 index d213d2fa8..000000000 --- a/docs/v3/v2/sp/tenant-properties/index.html +++ /dev/null @@ -1,2286 +0,0 @@ - - - - - - - - - - - - - - - - - - Tenant Properties - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/web - tenant properties

      -

      You can set, read, and remove tenant properties using the methods shown below:

      -

      setStorageEntity

      -

      This method MUST be called in the context of the app catalog web or you will get an access denied message.

      -
      import { Web } from "@pnp/sp/webs";
      -
      -const w = Web("https://tenant.sharepoint.com/sites/appcatalog/");
      -
      -// specify required key and value
      -await w.setStorageEntity("Test1", "Value 1");
      -
      -// specify optional description and comments
      -await w.setStorageEntity("Test2", "Value 2", "description", "comments");
      -
      -

      getStorageEntity

      -

      This method can be used from any web to retrieve values previously set.

      -
      import { sp, IStorageEntity } from "@pnp/sp/presets/all";
      -
      -const prop: IStorageEntity = await sp.web.getStorageEntity("Test1");
      -
      -console.log(prop.Value);
      -
      -

      removeStorageEntity

      -

      This method MUST be called in the context of the app catalog web or you will get an access denied message.

      -
      import { Web } from "@pnp/sp/webs";
      -
      -const w = Web("https://tenant.sharepoint.com/sites/appcatalog/");
      -
      -await w.removeStorageEntity("Test1");
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/user-custom-actions/index.html b/docs/v3/v2/sp/user-custom-actions/index.html deleted file mode 100644 index 6d622a78f..000000000 --- a/docs/v3/v2/sp/user-custom-actions/index.html +++ /dev/null @@ -1,2424 +0,0 @@ - - - - - - - - - - - - - - - - - - User custom actions - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      @pnp/sp/user-custom-actions

      -

      Represents a custom action associated with a SharePoint list, web or site collection.

      -

      IUserCustomActions

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import { IUserCustomActions, IUserCustomAction } from "@pnp/sp/user-custom-actions";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/user-custom-actions";
      Preset: Allimport { sp, IUserCustomActions, IUserCustomAction } from "@pnp/sp/presents/all";
      -

      Get a collection of User Custom Actions from a web

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/user-custom-actions";
      -
      -const userCustomActions = sp.web.userCustomActions();
      -
      -

      Add a new User Custom Action

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/user-custom-actions";
      -import { IUserCustomActionAddResult } from '@pnp/sp/user-custom-actions';
      -
      -const newValues: TypedHash<string> = {
      -    "Title": "New Title",
      -    "Description": "New Description",
      -    "Location": "ScriptLink",
      -    "ScriptSrc": "https://..."
      -};
      -
      -const response : IUserCustomActionAddResult = await sp.web.userCustomActions.add(newValues);
      -
      -

      Get a User Custom Action by ID

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/user-custom-actions";
      -
      -const uca: IUserCustomAction = sp.web.userCustomActions.getById("00000000-0000-0000-0000-000000000000");
      -
      -const ucaData = await uca();
      -
      -

      Clear the User Custom Action collection

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/user-custom-actions";
      -
      -// Site collection level
      -await sp.site.userCustomActions.clear();
      -
      -// Site (web) level
      -await sp.web.userCustomActions.clear();
      -
      -// List level
      -await sp.web.lists.getByTitle("Documents").userCustomActions.clear();
      -
      -

      IUserCustomAction

      -

      Update an existing User Custom Action

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/user-custom-actions";
      -import { IUserCustomActionUpdateResult } from '@pnp/sp/user-custom-actions';
      -
      -const uca = sp.web.userCustomActions.getById("00000000-0000-0000-0000-000000000000");
      -
      -const newValues: TypedHash<string> = {
      -    "Title": "New Title",
      -    "Description": "New Description",
      -    "ScriptSrc": "https://..."
      -};
      -
      -const response: IUserCustomActionUpdateResult = uca.update(newValues);
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/views/index.html b/docs/v3/v2/sp/views/index.html deleted file mode 100644 index 590d433ff..000000000 --- a/docs/v3/v2/sp/views/index.html +++ /dev/null @@ -1,2652 +0,0 @@ - - - - - - - - - - - - - - - - - - Views - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/views

      -

      Views define the columns, ordering, and other details we see when we look at a list. You can have multiple views for a list, including private views - and one default view.

      -

      IViews

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import { Views, IViews } from "@pnp/sp/views";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/views";
      Preset: Allimport { sp, Views, IViews } from "@pnp/sp/presets/all";
      -

      Get views in a list

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/views";
      -
      -const list = sp.web.lists.getByTitle("My List");
      -
      -// get all the views and their properties
      -const views1 = await list.views();
      -
      -// you can use odata select operations to get just a set a fields
      -const views2 = await list.views.select("Id", "Title")();
      -
      -// get the top three views
      -const views3 = await list.views.top(3)();
      -
      -

      Add a View

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/views";
      -
      -const list = sp.web.lists.getByTitle("My List");
      -
      -// create a new view with default fields and properties
      -const result = await list.views.add("My New View");
      -
      -// create a new view with specific properties
      -const result2 = await list.views.add("My New View 2", false, {
      -    RowLimit: 10,
      -    ViewQuery: "<OrderBy><FieldRef Name='Modified' Ascending='False' /></OrderBy>",
      -});
      -
      -// manipulate the view's fields
      -await result2.view.fields.removeAll();
      -
      -await Promise.all([
      -    result2.view.fields.add("Title"),
      -    result2.view.fields.add("Modified"),
      -]);
      -
      -

      IView

      -

      Get a View's Information

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/views";
      -
      -const list = sp.web.lists.getByTitle("My List");
      -
      -const result = await list.views.getById("{GUID view id}")();
      -
      -const result2 = await list.views.getByTitle("My View")();
      -
      -const result3 = await list.views.getByTitle("My View").select("Id", "Title")();
      -
      -const result4 = await list.defaultView();
      -
      -const result5 = await list.getView("{GUID view id}")();
      -
      -

      fields

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/views";
      -
      -const list = sp.web.lists.getByTitle("My List");
      -
      -const result = await list.views.getById("{GUID view id}").fields();
      -
      -

      update

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/views";
      -
      -const list = sp.web.lists.getByTitle("My List");
      -
      -const result = await list.views.getById("{GUID view id}").update({
      -    RowLimit: 20,
      -});
      -
      -

      renderAsHtml

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/views";
      -
      -const result = await sp.web.lists.getByTitle("My List").views.getById("{GUID view id}").renderAsHtml();
      -
      -

      setViewXml

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/views";
      -
      -const viewXml = "...";
      -
      -await sp.web.lists.getByTitle("My List").views.getById("{GUID view id}").setViewXml(viewXml);
      -
      -

      delete

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/views";
      -
      -const viewXml = "...";
      -
      -await sp.web.lists.getByTitle("My List").views.getById("{GUID view id}").delete();
      -
      -

      ViewFields

      -

      getSchemaXml

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/views";
      -
      -const xml = await sp.web.lists.getByTitle("My List").defaultView.fields.getSchemaXml();
      -
      -

      add

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/views";
      -
      -await sp.web.lists.getByTitle("My List").defaultView.fields.add("Created");
      -
      -

      move

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/views";
      -
      -await sp.web.lists.getByTitle("My List").defaultView.fields.move("Created", 0);
      -
      -

      remove

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/views";
      -
      -await sp.web.lists.getByTitle("My List").defaultView.fields.remove("Created");
      -
      -

      removeAll

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/lists";
      -import "@pnp/sp/views";
      -
      -await sp.web.lists.getByTitle("My List").defaultView.fields.removeAll();
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/sp/webs/index.html b/docs/v3/v2/sp/webs/index.html deleted file mode 100644 index 029248cf6..000000000 --- a/docs/v3/v2/sp/webs/index.html +++ /dev/null @@ -1,4190 +0,0 @@ - - - - - - - - - - - - - - - - - - Webs - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - -

      @pnp/sp/webs

      -

      Webs are one of the fundamental entry points when working with SharePoint. Webs serve as a container for lists, features, sub-webs, and all of the entity types.

      -

      IWebs

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import { Webs, IWebs } from "@pnp/sp/webs";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      Preset: Allimport { sp, Webs, IWebs } from "@pnp/sp/presets/all";
      Preset: Coreimport { sp, Webs, IWebs } from "@pnp/sp/presets/core";
      -

      Add Web

      -

      Using the library you can add a web to another web's collection of subwebs. The simplest usage requires only a title and url. This will result in a team site with all of the default settings. You can also provide other settings such as description, template, language, and inherit permissions.

      -
      import { sp } from "@pnp/sp";
      -import { IWebAddResult } from "@pnp/sp/webs";
      -
      -const result = await sp.web.webs.add("title", "subweb1");
      -
      -// show the response from the server when adding the web
      -console.log(result.data);
      -
      -// we can immediately operate on the new web
      -result.web.select("Title")().then((w: IWebAddResult)  => {
      -
      -    // show our title
      -    console.log(w.Title);
      -});
      -
      -
      import { sp } from "@pnp/sp";
      -import { IWebAddResult } from "@pnp/sp/webs";
      -
      -// create a German language wiki site with title, url, description, which does not inherit permissions
      -sp.web.webs.add("wiki", "subweb2", "a wiki web", "WIKI#0", 1031, false).then((w: IWebAddResult) => {
      -
      -  // ...
      -});
      -
      -

      IWeb

      -

      Invokable Banner Selective Imports Banner

      - - - - - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import { sp } from "@pnp/sp";
      import { Web, IWeb } from "@pnp/sp/webs";
      Selective 2import { sp } from "@pnp/sp";
      import "@pnp/sp/webs";
      Preset: Allimport { sp, Web, IWeb } from "@pnp/sp/presets/all";
      Preset: Coreimport { sp, Web, IWeb } from "@pnp/sp/presets/core";
      -

      Access a Web

      -

      There are several ways to access a web instance, each of these methods is equivalent in that you will have an IWeb instance to work with. All of the examples below use a variable named "web" which represents an IWeb instance - regardless of how it was initially accessed.

      -

      Access the web from the imported "sp" object using selective import:

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -
      -const r = await sp.web();
      -
      -

      Access the web from the imported "sp" using the 'all' preset

      -
      import { sp } from "@pnp/sp/presets/all";
      -
      -const r = await sp.web();
      -
      -

      Access the web from the imported "sp" using the 'core' preset

      -
      import { sp } from "@pnp/sp/presets/core";
      -
      -const r = await sp.web();
      -
      -

      Create a web instance using the factory function

      -
      import { Web } from "@pnp/sp/webs";
      -
      -const web = Web("https://something.sharepoint.com/sites/dev");
      -const r = await web();
      -
      -

      webs

      -

      Access the child webs collection of this web

      -
      const webs = web.webs();
      -
      -

      Get A Web's properties

      -
      // basic get of the webs properties
      -const props = await web();
      -
      -// use odata operators to get specific fields
      -const props2 = await web.select("Title")();
      -
      -// type the result to match what you are requesting
      -const props3 = await web.select("Title")<{ Title: string }>();
      -
      -

      getParentWeb

      -

      Get the data and IWeb instance for the parent web for the given web instance

      -
      import { IOpenWebByIdResult } from "@pnp/sp/sites";
      -const web: IOpenWebByIdResult = web.getParentWeb();
      -
      -

      getSubwebsFilteredForCurrentUser

      -

      Returns a collection of objects that contain metadata about subsites of the current site in which the current user is a member.

      -
      const subWebs = await web.getSubwebsFilteredForCurrentUser()();
      -
      -// apply odata operations to the collection
      -const subWebs2 = await sp.web.getSubwebsFilteredForCurrentUser().select("Title", "Language").orderBy("Created", true)();
      -
      -
      -

      Note: getSubwebsFilteredForCurrentUser returns IWebInfosData which is a subset of all the available fields on IWebInfo.

      -
      -

      allProperties

      -

      Allows access to the web's all properties collection. This is readonly in REST.

      -
      const props = await web.allProperties();
      -
      -// select certain props
      -const props2 = await web.allProperties.select("prop1", "prop2")();
      -
      -

      webinfos

      -

      Gets a collection of WebInfos for this web's subwebs

      -
      const infos = await web.webinfos();
      -
      -// or select certain fields
      -const infos2 = await web.webinfos.select("Title", "Description")();
      -
      -// or filter
      -const infos3 = await web.webinfos.filter("Title eq 'MyWebTitle'")();
      -
      -// or both
      -const infos4 = await web.webinfos.select("Title", "Description").filter("Title eq 'MyWebTitle'")();
      -
      -// get the top 4 ordered by Title
      -const infos5 = await web.webinfos.top(4).orderBy("Title")();
      -
      -
      -

      Note: webinfos returns IWebInfosData which is a subset of all the available fields on IWebInfo.

      -
      -

      update

      -

      Updates this web instance with the supplied properties

      -
      
      -// update the web's title and description
      -const result = await web.update({
      -    Title: "New Title",
      -    Description: "My new description",
      -});
      -
      -// a project implementation could wrap the update to provide type information for your expected fields:
      -import { IWebUpdateResult } from "@pnp/sp/webs";
      -
      -interface IWebUpdateProps {
      -    Title: string;
      -    Description: string;
      -}
      -
      -function updateWeb(props: IWebUpdateProps): Promise<IWebUpdateResult> {
      -    web.update(props);
      -}
      -
      -

      Delete a Web

      -
      await web.delete();
      -
      -

      applyTheme

      -

      Applies the theme specified by the contents of each of the files specified in the arguments to the site

      -
      import { combine } from "@pnp/core";
      -
      -// we are going to apply the theme to this sub web as an example
      -const web = Web("https://{tenant}.sharepoint.com/sites/dev/subweb");
      -
      -// the urls to the color and font need to both be from the catalog at the root
      -// these urls can be constants or calculated from existing urls
      -const colorUrl =  combine("/", "sites/dev", "_catalogs/theme/15/palette011.spcolor");
      -// this gives us the same result
      -const fontUrl = "/sites/dev/_catalogs/theme/15/fontscheme007.spfont";
      -
      -// apply the font and color, no background image, and don't share this theme
      -await web.applyTheme(colorUrl, fontUrl, "", false);
      -
      -

      applyWebTemplate & availableWebTemplates

      -

      Applies the specified site definition or site template to the Web site that has no template applied to it. This is seldom used outside provisioning scenarios.

      -
      const templates = (await web.availableWebTemplates().select("Name")<{ Name: string }[]>()).filter(t => /ENTERWIKI#0/i.test(t.Name));
      -
      -// apply the wiki template
      -const template = templates.length > 0 ? templates[0].Name : "STS#0";
      -
      -await web.applyWebTemplate(template);
      -
      -

      getChanges

      -

      Returns the collection of changes from the change log that have occurred within the web, based on the specified query.

      -
      // get the web changes including add, update, and delete
      -const changes = await web.getChanges({
      -        Add: true,
      -        ChangeTokenEnd: null,
      -        ChangeTokenStart: null,
      -        DeleteObject: true,
      -        Update: true,
      -        Web: true,
      -    });
      -
      -

      mapToIcon

      -

      Returns the name of the image file for the icon that is used to represent the specified file

      -
      import { combine } from "@pnp/core";
      -
      -const iconFileName = await web.mapToIcon("test.docx");
      -// iconPath === "icdocx.png"
      -// which you can need to map to a real url
      -const iconFullPath = `https://{tenant}.sharepoint.com/sites/dev/_layouts/images/${iconFileName}`;
      -
      -// OR dynamically
      -const webData = await sp.web.select("Url")();
      -const iconFullPath2 = combine(webData.Url, "_layouts", "images", iconFileName);
      -
      -// OR within SPFx using the context
      -const iconFullPath3 = combine(this.context.pageContext.web.absoluteUrl, "_layouts", "images", iconFileName);
      -
      -// You can also set size
      -// 16x16 pixels = 0, 32x32 pixels = 1
      -const icon32FileName = await web.mapToIcon("test.docx", 1);
      -
      -

      storage entities

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/appcatalog";
      -import { IStorageEntity } from "@pnp/sp/webs";
      -
      -// needs to be unique, GUIDs are great
      -const key = "my-storage-key";
      -
      -// read an existing entity
      -const entity: IStorageEntity = await web.getStorageEntity(key);
      -
      -// setStorageEntity and removeStorageEntity must be called in the context of the tenant app catalog site
      -// you can get the tenant app catalog using the getTenantAppCatalogWeb
      -const tenantAppCatalogWeb = await sp.getTenantAppCatalogWeb();
      -
      -tenantAppCatalogWeb.setStorageEntity(key, "new value");
      -
      -// set other properties
      -tenantAppCatalogWeb.setStorageEntity(key, "another value", "description", "comments");
      -
      -const entity2: IStorageEntity = await web.getStorageEntity(key);
      -/*
      -entity2 === {
      -    Value: "another value",
      -    Comment: "comments";
      -    Description: "description",
      -};
      -*/
      -
      -// you can also remove a storage entity
      -await tenantAppCatalogWeb.removeStorageEntity(key);
      -
      -

      appcatalog imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/appcatalog";
      Selective 2import "@pnp/sp/appcatalog/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      getAppCatalog

      -

      Returns this web as an IAppCatalog instance or creates a new IAppCatalog instance from the provided url.

      -
      import { IApp } from "@pnp/sp/appcatalog";
      -
      -const appWeb = web.getAppCatalog();
      -// appWeb url === web url
      -
      -const app: IApp = appWeb.getAppById("{your app id}");
      -
      -const appWeb2 = web.getAppCatalog("https://tenant.sharepoing.com/sites/someappcatalog");
      -// appWeb2 url === "https://tenant.sharepoing.com/sites/someappcatalog"
      -
      -

      client-side-pages imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/client-side-pages";
      Selective 2import "@pnp/sp/client-side-pages/web";
      Preset: Allimport { sp, Web, IWeb } from "@pnp/sp/presets/all";
      -

      You can create and load clientside page instances directly from a web. More details on working with clientside pages are available in the dedicated article.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -import "@pnp/sp/clientside-pages/web";
      -
      -// simplest add a page example
      -const page = await sp.web.addClientsidePage("mypage1");
      -
      -// simplest load a page example
      -const page = await sp.web.loadClientsidePage("/sites/dev/sitepages/mypage3.aspx");
      -
      -

      content-type imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/content-types";
      Selective 2import "@pnp/sp/content-types/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      contentTypes

      -

      Allows access to the collection of content types in this web.

      -
      const cts = await web.contentTypes();
      -
      -// you can also select fields and use other odata operators
      -const cts2 = await web.contentTypes.select("Name")();
      -
      -

      features imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/features";
      Selective 2import "@pnp/sp/features/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      features

      -

      Allows access to the collection of content types in this web.

      -
      const features = await web.features();
      -
      -

      fields imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/fields";
      Selective 2import "@pnp/sp/fields/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      fields

      -

      Allows access to the collection of fields in this web.

      -
      const fields = await web.fields();
      -
      -

      files imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/files";
      Selective 2import "@pnp/sp/files/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      getFileByServerRelativeUrl

      -

      Gets a file by server relative url

      -
      import { IFile } from "@pnp/sp/files";
      -
      -const file: IFile = web.getFileByServerRelativeUrl("/sites/dev/library/myfile.docx");
      -
      -

      getFileByServerRelativePath

      -

      Gets a file by server relative url if your file name contains # and % characters

      -
      import { IFile } from "@pnp/sp/files";
      -
      -const file: IFile = web.getFileByServerRelativePath("/sites/dev/library/my # file%.docx");
      -
      -

      folders imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/folders";
      Selective 2import "@pnp/sp/folders/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      folders

      -

      Gets the collection of folders in this web

      -
      const folders = await web.folders();
      -
      -// you can also filter and select as with any collection
      -const folders2 = await web.folders.select("ServerRelativeUrl", "TimeLastModified").filter("ItemCount gt 0")();
      -
      -// or get the most recently modified folder
      -const folders2 = await web.folders.orderBy("TimeLastModified").top(1)();
      -
      -

      rootFolder

      -

      Gets the root folder of the web

      -
      const folder = await web.rootFolder();
      -
      -

      getFolderByServerRelativeUrl

      -

      Gets a folder by server relative url

      -
      import { IFolder } from "@pnp/sp/folders";
      -
      -const folder: IFolder = web.getFolderByServerRelativeUrl("/sites/dev/library");
      -
      -

      getFolderByServerRelativePath

      -

      Gets a folder by server relative url if your folder name contains # and % characters

      -
      import { IFolder } from "@pnp/sp/folders";
      -
      -const folder: IFolder = web.getFolderByServerRelativePath("/sites/dev/library/my # folder%/");
      -
      -

      hubsites imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/hubsites";
      Selective 2import "@pnp/sp/hubsites/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      hubSiteData

      -

      Gets hub site data for the current web

      -
      import { IHubSiteWebData } from "@pnp/sp/hubsites";
      -
      -// get the data and force a refresh
      -const data: IHubSiteWebData = await web.hubSiteData(true);
      -
      -

      syncHubSiteTheme

      -

      Applies theme updates from the parent hub site collection

      -
      await web.syncHubSiteTheme();
      -
      -

      lists imports

      - - - - - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/lists";
      Selective 2import "@pnp/sp/lists/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      Preset: Coreimport { sp } from "@pnp/sp/presets/core";
      -

      lists

      -

      Gets the collection of all lists that are contained in the Web site

      -
      import { ILists } from "@pnp/sp/lists";
      -
      -const lists: ILists = web.lists;
      -
      -// you can always order the lists and select properties
      -const data = await lists.select("Title").orderBy("Title")();
      -
      -// and use other odata operators as well
      -const data2 = await web.lists.top(3).orderBy("LastItemModifiedDate")();
      -
      -

      siteUserInfoList

      -

      Gets the UserInfo list of the site collection that contains the Web site

      -
      import { IList } from "@pnp/sp/lists";
      -
      -const list: IList = web.siteUserInfoList;
      -
      -const data = await list();
      -
      -// or chain off that list to get additional details
      -const items = await list.items.top(2)();
      -
      -

      defaultDocumentLibrary

      -

      Get a reference the default documents library of a web

      -
      import { IList } from "@pnp/sp/lists";
      -
      -const list: IList = web.defaultDocumentLibrary;
      -
      -

      customListTemplates

      -

      Gets the collection of all list definitions and list templates that are available

      -
      import { IList } from "@pnp/sp/lists";
      -
      -const templates = await web.customListTemplates();
      -
      -// odata operators chain off the collection as expected
      -const templates2 = await web.customListTemplates.select("Title")();
      -
      -

      getList

      -

      Gets a list by server relative url (list's root folder)

      -
      import { IList } from "@pnp/sp/lists";
      -
      -const list: IList = web.getList("/sites/dev/lists/test");
      -
      -const listData = list();
      -
      -

      getCatalog

      -

      Returns the list gallery on the site

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      NameValue
      WebTemplateCatalog111
      WebPartCatalog113
      ListTemplateCatalog114
      MasterPageCatalog116
      SolutionCatalog121
      ThemeCatalog123
      DesignCatalog124
      AppDataCatalog125
      -
      import { IList } from "@pnp/sp/lists";
      -
      -const templateCatalog: IList = await web.getCatalog(111);
      -
      -const themeCatalog: IList = await web.getCatalog(123);
      -
      - - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/navigation";
      Selective 2import "@pnp/sp/navigation/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      - -

      Gets a navigation object that represents navigation on the Web site, including the Quick Launch area and the top navigation bar

      -
      import { INavigation } from "@pnp/sp/navigation";
      -
      -const nav: INavigation = web.navigation;
      -
      -const navData = await nav();
      -
      -

      regional-settings imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/regional-settings";
      Selective 2import "@pnp/sp/regional-settings/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -
      import { IRegionalSettings } from "@pnp/sp/navigation";
      -
      -const settings: IRegionalSettings = web.regionalSettings;
      -
      -const settingsData = await settings();
      -
      - - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/related-items";
      Selective 2import "@pnp/sp/related-items/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -
      import { IRelatedItemManager, IRelatedItem } from "@pnp/sp/related-items";
      -
      -const manager: IRelatedItemManager = web.relatedItems;
      -
      -const data: IRelatedItem[] = await manager.getRelatedItems("{list name}", 4);
      -
      -

      security imports

      -

      Please see information around the available security methods in the security article.

      -

      sharing imports

      -

      Please see information around the available sharing methods in the sharing article.

      -

      site-groups imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/site-groups";
      Selective 2import "@pnp/sp/site-groups/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      siteGroups

      -

      The site groups

      -
      const groups = await web.siteGroups();
      -
      -const groups2 = await web.siteGroups.top(2)();
      -
      -

      associatedOwnerGroup

      -

      The web's owner group

      -
      const group = await web.associatedOwnerGroup();
      -
      -const users = await web.associatedOwnerGroup.users();
      -
      -

      associatedMemberGroup

      -

      The web's member group

      -
      const group = await web.associatedMemberGroup();
      -
      -const users = await web.associatedMemberGroup.users();
      -
      -

      associatedVisitorGroup

      -

      The web's visitor group

      -
      const group = await web.associatedVisitorGroup();
      -
      -const users = await web.associatedVisitorGroup.users();
      -
      -

      createDefaultAssociatedGroups

      -

      Creates the default associated groups (Members, Owners, Visitors) and gives them the default permissions on the site. The target site must have unique permissions and no associated members / owners / visitors groups

      -
      await web.createDefaultAssociatedGroups("Contoso", "{first owner login}");
      -
      -// copy the role assignments
      -await web.createDefaultAssociatedGroups("Contoso", "{first owner login}", true);
      -
      -// don't clear sub assignments
      -await web.createDefaultAssociatedGroups("Contoso", "{first owner login}", false, false);
      -
      -// specify secondary owner, don't copy permissions, clear sub scopes
      -await web.createDefaultAssociatedGroups("Contoso", "{first owner login}", false, true, "{second owner login}");
      -
      -

      site-users imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/site-users";
      Selective 2import "@pnp/sp/site-users/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      siteUsers

      -

      The site users

      -
      const users = await web.siteUsers();
      -
      -const users2 = await web.siteUsers.top(5)();
      -
      -const users3 = await web.siteUsers.filter(`startswith(LoginName, '${encodeURIComponent("i:0#.f|m")}')`)();
      -
      -

      currentUser

      -

      Information on the current user

      -
      const user = await web.currentUser();
      -
      -// check the login name of the current user
      -const user2 = await web.currentUser.select("LoginName")();
      -
      -

      ensureUser

      -

      Checks whether the specified login name belongs to a valid user in the web. If the user doesn't exist, adds the user to the web

      -
      import { IWebEnsureUserResult } from "@pnp/sp/site-users/";
      -
      -const result: IWebEnsureUserResult = await web.ensureUser("i:0#.f|membership|user@domain.onmicrosoft.com");
      -
      -

      getUserById

      -

      Returns the user corresponding to the specified member identifier for the current web

      -
      import { ISiteUser } from "@pnp/sp/site-users/";
      -
      -const user: ISiteUser = web.getUserById(23);
      -
      -const userData = await user();
      -
      -const userData2 = await user.select("LoginName")();
      -
      -

      user-custom-actions imports

      - - - - - - - - - - - - - - - - - - - - - -
      ScenarioImport Statement
      Selective 1import "@pnp/sp/user-custom-actions";
      Selective 2import "@pnp/sp/user-custom-actions/web";
      Preset: Allimport { sp } from "@pnp/sp/presets/all";
      -

      userCustomActions

      -

      Gets a newly refreshed collection of the SPWeb's SPUserCustomActionCollection

      -
      import { IUserCustomActions } from "@pnp/sp/user-custom-actions";
      -
      -const actions: IUserCustomActions = web.userCustomActions;
      -
      -const actionsData = await actions();
      -
      -

      IWebInfosData

      -

      Some web operations return a subset of web information defined by the IWebInfosData interface, shown below. In those cases only these fields are available for select, orderby, and other odata operations.

      -
      interface IWebInfosData {
      -    Configuration: number;
      -    Created: string;
      -    Description: string;
      -    Id: string;
      -    Language: number;
      -    LastItemModifiedDate: string;
      -    LastItemUserModifiedDate: string;
      -    ServerRelativeUrl: string;
      -    Title: string;
      -    WebTemplate: string;
      -    WebTemplateId: number;
      -}
      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file diff --git a/docs/v3/v2/transition-guide/index.html b/docs/v3/v2/transition-guide/index.html deleted file mode 100644 index d51768093..000000000 --- a/docs/v3/v2/transition-guide/index.html +++ /dev/null @@ -1,2435 +0,0 @@ - - - - - - - - - - - - - - - - - - Transition Guide - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - spacer - - - - - - - - - - - - - - - - - -
      - -
      - -
      - -
      - -
      - - - - -
      -
      - - -
      -
      -
      - -
      -
      -
      - - - - - -
      -
      - - - - - - - -

      Transition Guide

      -

      We have worked to make moving from @pnp library 1. to 2. as painless as possible, however there are some changes to how things work. The below guide we have provided an overview of what it takes to transition between the libraries. If we missed something, please let us know in the issues list so we can update the guide. Thanks!

      -

      Installing @pnp libraries

      -

      In version 1.* the libraries were setup as peer dependencies of each other requiring you to install each of them separately. We continue to believe this correctly describes the relationship, but recognize that basically nothing in the world accounts for peer dependencies. So we have updated the libraries to be dependencies. This makes it easier to install into your projects as you only need to install a single library:

      -

      npm i --save @pnp/sp

      -

      Selective Imports

      -

      Another big change in v2 is the ability to selectively import the pieces you need from the libraries. This allows you to have smaller bundles and works well with tree-shaking. It does require you to have more import statements, which can potentially be a bit confusing at first. The selective imports apply to the sp and graph libraries.

      -

      To help explain let's take the example of the Web object. In v1 Web includes a reference to pretty much everything else in the entire sp library. Meaning that if you use web (and you pretty much have to) you hold a ref to all the other pieces (like Fields, Lists, ContentTypes) even if you aren't using them. Because of that tree shaking can't do anything to reduce the bundle size because it "thinks" you are using them simply because they have been imported. To solve this in v2 the Web object no longer contains references to anything, it is a bare object with a few methods. If you look at the source you will see that, for example, there is no longer a "lists" property. These properties and methods are now added through selectively importing the functionality you need:

      -

      Selectively Import Web lists functionality

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -// this imports the functionality for lists associated only with web
      -import "@pnp/sp/lists/web";
      -
      -const r = await sp.web.lists();
      -
      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -// this imports all the functionality for lists
      -import "@pnp/sp/lists";
      -
      -const r = await sp.web.lists();
      -
      -

      Each of the docs pages shows the selective import paths for each sub-module (lists, items, etc.).

      -

      Presets

      -

      In addition to the ability to selectively import functionality you can import presets. This allows you to import an entire set of functionality in a single line. At launch the sp library will support two presets "all" and "core" with the graph library supporting "all". Using the "all" preset will match the functionality of v1. This can save you time in transitioning your projects so you can update to selective imports later. For new projects we recommend using the selective imports from day 1.

      -

      To update your V1 projects to V2 you can replace all instances of "@pnp/sp" with "@pnp/sp/presets/all" and things should work as before (though some class names or other things may have changed, please review the change log and the rest of this guide).

      -
      // V1 way of doing things:
      -import {
      -    sp,
      -    ClientSideWebpart,
      -    ClientSideWebpartPropertyTypes,
      -} from "@pnp/sp";
      -
      -// V2 way with selective imports
      -import { sp } from "@pnp/sp";
      -import { ClientSideWebpart, ClientSideWebpartPropertyTypes } from "@pnp/sp/clientside-pages";
      -
      -// V2 way with preset "all"
      -import { sp, ClientSideWebpart, ClientSideWebpartPropertyTypes } from "@pnp/sp/presets/all";
      -
      -

      Invokable Objects

      -

      Another new feature is the addition of invokable objects. Previously where you used "get()" to invoke a request you can now leave it off. We have left the .get method in place so everyone's code wasn't broken immediately upon transitioning.

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -
      -// old way (still works)
      -const r1 = sp.web();
      -
      -// invokable
      -const r2 = sp.web();
      -
      -

      The benefit is that objects can now support default actions that are not "get" but might be "post". And you save typing a few extra characters. This still work the same as with select or any of the other odata methods:

      -
      import { sp } from "@pnp/sp";
      -import "@pnp/sp/webs";
      -
      -// invokable
      -const r = sp.web.select("Title", "Url")();
      -
      -

      Factory Functions & Interfaces

      -

      Another change in the library is in the structure of exports. We are no longer exporting the objects themselves, rather we are only exposing factory functions and interfaces. This allows us to decouple what developers use from our internal implementation. For folks using the fluent chain starting with sp you shouldn't need to update your code. If you are using any of the v1 classes directly you should just need to remove the "new" keyword and update the import path. The factory functions signature matches the constructor signature of the v1 objects.

      -
      // v1
      -import { Web } from "@pnp/sp";
      -
      -const web: Web = new Web("some absolute url");
      -
      -const r1 = web();
      -
      -// v2
      -import { Web, IWeb } from "@pnp/sp/webs";
      -
      -const web: IWeb = Web("some absolute url");
      -
      -const r2 = web();
      -
      -

      Extension Methods

      -

      Another new capability in v2 is the ability to extend objects and factories. This allows you to easily add methods or properties on a per-object basis. Please see the full article on extension methods describing this great new capability.

      -

      CDN publishing

      -

      Starting with v2 we will no longer create bundles for each of the packages. Historically these are not commonly used, don't work perfectly for everyone (there are a lot of ways to bundle things), and another piece we need to maintain. Instead we encourage folks to create their own bundles, optimized for their particular scenario. This will result in smaller overall bundle size and allow folks to bundle things to match their scenario. Please review the article on creating your custom bundles to see how to tailor bundles to your needs.

      -

      The PnPjs bundle will remain, though it is designed only for backwards compatibility and we strongly recommend creating your own bundles, or directly importing the libraries into your projects using selective imports.

      -

      Drop client-svc and sp-taxonomy libraries

      -

      These libraries were created to allow folks to access and manage SharePoint taxonomy and manage metadata. Given that there is upcoming support for taxonomy via a supported REST API we will drop these two libraries. If working with taxonomy remains a core requirement of your application and we do not yet have support for the new apis, please remain on v1 for the time being.

      -
      -

      As of 2.0.6 we support reading the modern taxonomy API. Docs here

      -
      - - - - - - - -
      -
      -
      -
      - - - - -
      - - - - - - - - - \ No newline at end of file From 7977cc8f81874537cc6a1a8d8284ad65449a09fb Mon Sep 17 00:00:00 2001 From: Julie Turner Date: Tue, 1 Oct 2024 15:00:30 +0000 Subject: [PATCH 3/3] Fixed font sizing; rotate 45deg --- docs/v1/documentation/css/extra.css | 6 ++++-- docs/v2/css/extra.css | 8 +++++--- docs/v3/css/extra.css | 7 +++++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/v1/documentation/css/extra.css b/docs/v1/documentation/css/extra.css index 4d693ca20..4fe95a2c7 100644 --- a/docs/v1/documentation/css/extra.css +++ b/docs/v1/documentation/css/extra.css @@ -45,11 +45,10 @@ body { left: 0; right: 0; content: "V1 \A Deprecated"; - white-space: pre-wrap; display: block; box-sizing: border-box; padding: 20vh; - font-size: clamp(4rem, 20vh, 10rem); + font-size: clamp(3rem, 20vw, 8rem); height: 100vh; width: 100vw; text-align: center; @@ -58,5 +57,8 @@ body { font-weight: 900; font-family: sans-serif; text-transform: uppercase; + transform: rotate(-45deg); + transform-origin: center; + white-space: pre-wrap; } } \ No newline at end of file diff --git a/docs/v2/css/extra.css b/docs/v2/css/extra.css index 81c8ef41b..ab9c76f93 100644 --- a/docs/v2/css/extra.css +++ b/docs/v2/css/extra.css @@ -44,12 +44,11 @@ body { bottom: 0; left: 0; right: 0; - content: "V2 \A Deprecated"; - white-space: pre-wrap; + content: "V2 \A Deprecated"; display: block; box-sizing: border-box; padding: 20vh; - font-size: clamp(4rem, 20vh, 10rem); + font-size: clamp(1rem, 10vw, 5rem); height: 100vh; width: 100vw; text-align: center; @@ -58,5 +57,8 @@ body { font-weight: 900; font-family: sans-serif; text-transform: uppercase; + transform: rotate(-45deg); + transform-origin: center; + white-space: pre-wrap; } } \ No newline at end of file diff --git a/docs/v3/css/extra.css b/docs/v3/css/extra.css index 0193615dd..ad63ece2e 100644 --- a/docs/v3/css/extra.css +++ b/docs/v3/css/extra.css @@ -45,11 +45,11 @@ body { left: 0; right: 0; content: "V3 \A Deprecated"; - white-space: pre-wrap; + display: block; box-sizing: border-box; padding: 20vh; - font-size: clamp(4rem, 20vh, 10rem); + font-size: clamp(1rem, 10vw, 5rem); height: 100vh; width: 100vw; text-align: center; @@ -58,5 +58,8 @@ body { font-weight: 900; font-family: sans-serif; text-transform: uppercase; + transform: rotate(-45deg); + transform-origin: center; + white-space: pre-wrap; } } \ No newline at end of file

    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 z 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 diff --git a/docs/v3/v1/documentation/img/office365-header-icon.png b/docs/v3/v1/documentation/img/office365-header-icon.png deleted file mode 100644 index 529191ea65d5d78f1964a4ff675c064e3324873c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 312 zcmeAS@N?(olHy`uVBq!ia0vp^DImwho^t@)Y*6k#k0@(X5g zcy=QV#7XjYcVXyYmGuB}I14-?iy0WWg+Z8+Vb&Z8pdfpRr>`sfBQ{AMOSK2Hc=iK@ zR(rZQhFF|_du1cZLh;FKR%UL^VQEdTT15*_xaf=PkVMessuWl!PC{xWt~$(699F$b}j$_ diff --git a/docs/v3/v1/documentation/img/pnpjs-common-uml.svg b/docs/v3/v1/documentation/img/pnpjs-common-uml.svg deleted file mode 100644 index 4eb24604e..000000000 --- a/docs/v3/v1/documentation/img/pnpjs-common-uml.svg +++ /dev/null @@ -1,220 +0,0 @@ - - - - -Gyuml_util - -Util - -getCtxCallback -dateAdd -combinePaths -getRandomString -getGUID -isFunc -objectDefinedNotNull -isArray -extend -isUrlAbsolute -stringIsNullOrEmpty -getAttrValueFromString -sanitizeGuid - - -yuml_pnpclientstore - -PnPClientStore - -enabled - -get() -put() -delete() -getOrPut() -deleteExpired() -yuml_pnpclientstoragewrapper - -PnPClientStorageWrapper - -store -defaultTimeoutMinutes -enabled - -get() -put() -delete() -getOrPut() -deleteExpired() -test() -createPersistable() -cacheExpirationHandler() -yuml_pnpclientstore->yuml_pnpclientstoragewrapper - - -yuml_pnpclientstorage - -PnPClientStorage - -_local -_session -local -session - - -yuml_ispfxcontext - -ISPFXContext - -graphHttpClient -pageContext - - -yuml_ispfxgraphhttpclient - -ISPFXGraphHttpClient - - - -fetch() -yuml_fetchclient - -FetchClient - - - -fetch() -yuml_bearertokenfetchclient - -BearerTokenFetchClient - -_token -token - -fetch() -yuml_fetchclient->yuml_bearertokenfetchclient - - -yuml_adalclient - -AdalClient - -clientId -tenant -redirectUri -_authContext -_displayCallback -_loginPromise - -fromSPFxContext() -fetch() -getToken() -ensureAuthContext() -login() -getResource() -yuml_bearertokenfetchclient->yuml_adalclient - - -yuml_httpclientimpl - -HttpClientImpl - - - -fetch() -yuml_httpclientimpl->yuml_fetchclient - - -yuml_requestclient - -RequestClient - - - -fetch() -fetchRaw() -get() -post() -patch() -delete() -yuml_fetchoptions - -FetchOptions - -method -body - - -yuml_configoptions - -ConfigOptions - -headers -mode -credentials -cache - - -yuml_libraryconfiguration - -LibraryConfiguration - -globalCacheDisable -defaultCachingStore -defaultCachingTimeoutSeconds -enableCacheExpiration -cacheExpirationIntervalMilliseconds -spfxContext - - -yuml_runtimeconfigimpl - -RuntimeConfigImpl - -_v -defaultCachingStore -defaultCachingTimeoutSeconds -globalCacheDisable -enableCacheExpiration -cacheExpirationIntervalMilliseconds -spfxContext - -extend() -get() -yuml_error - -Error -yuml_urlexception - -UrlException - - - - -yuml_error->yuml_urlexception - - -yuml_typedhash - -TypedHash - - - - -yuml_dictionary - -Dictionary - -keys -values -count - -get() -add() -merge() -remove() -getKeys() -getValues() -clear() - - diff --git a/docs/v3/v1/documentation/img/pnpjs-config-store-uml.svg b/docs/v3/v1/documentation/img/pnpjs-config-store-uml.svg deleted file mode 100644 index 533057b6a..000000000 --- a/docs/v3/v1/documentation/img/pnpjs-config-store-uml.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - -Gyuml_iconfigurationprovider - -IConfigurationProvider - - - -getConfiguration() -yuml_default - -default - -wrappedProvider -cacheKey -store - -getWrappedProvider() -getConfiguration() -selectPnPCache() -yuml_iconfigurationprovider->yuml_default - - -yuml_iconfigurationprovider->yuml_default - - -yuml_error - -Error -yuml_nocacheavailableexception - -NoCacheAvailableException - - - - -yuml_error->yuml_nocacheavailableexception - - -yuml_settings - -Settings - -_settings - -add() -addJSON() -apply() -load() -get() -getJSON() - - diff --git a/docs/v3/v1/documentation/img/pnpjs-graph-uml.svg b/docs/v3/v1/documentation/img/pnpjs-graph-uml.svg deleted file mode 100644 index 99b260e53..000000000 --- a/docs/v3/v1/documentation/img/pnpjs-graph-uml.svg +++ /dev/null @@ -1,602 +0,0 @@ - - - - -Gyuml_requestclient - -RequestClient -yuml_graphhttpclient - -GraphHttpClient - -_impl - -fetch() -fetchRaw() -get() -post() -patch() -delete() -yuml_requestclient->yuml_graphhttpclient - - -yuml_error - -Error -yuml_nographclientavailableexception - -NoGraphClientAvailableException - - - - -yuml_error->yuml_nographclientavailableexception - - -yuml_graphbatchparseexception - -GraphBatchParseException - - - - -yuml_error->yuml_graphbatchparseexception - - -yuml_graphconfiguration - -GraphConfiguration - - - - -yuml_graphconfigurationpart - -GraphConfigurationPart - -graph - - -yuml_graphruntimeconfigimpl - -GraphRuntimeConfigImpl - -headers -fetchClientFactory - - -yuml_graphqueryableinstance - -GraphQueryableInstance - - - -select() -expand() -yuml_user - -User - - - - -yuml_graphqueryableinstance->yuml_user - - -yuml_team - -Team - - - -update() -get() -yuml_graphqueryableinstance->yuml_team - - -yuml_plan - -Plan - - - - -yuml_graphqueryableinstance->yuml_plan - - -yuml_photo - -Photo - - - -getBlob() -getBuffer() -setContent() -yuml_graphqueryableinstance->yuml_photo - - -yuml_section - -Section - - - - -yuml_graphqueryableinstance->yuml_section - - -yuml_notebook - -Notebook - -sections - - -yuml_graphqueryableinstance->yuml_notebook - - -yuml_onenote - -OneNote - -notebooks -sections -pages - - -yuml_graphqueryableinstance->yuml_onenote - - -yuml_member - -Member - - - - -yuml_graphqueryableinstance->yuml_member - - -yuml_me - -Me - -onenote - - -yuml_graphqueryableinstance->yuml_me - - -yuml_group - -Group - -calendar -events -owners -plans -members -conversations -acceptedSenders -rejectedSenders -photo -team - -addFavorite() -createTeam() -getMemberGroups() -delete() -update() -removeFavorite() -resetUnseenCount() -subscribeByMail() -unsubscribeByMail() -getCalendarView() -yuml_graphqueryableinstance->yuml_group - - -yuml_post - -Post - -attachments - -delete() -forward() -reply() -yuml_graphqueryableinstance->yuml_post - - -yuml_thread - -Thread - -posts - -reply() -delete() -yuml_graphqueryableinstance->yuml_thread - - -yuml_conversation - -Conversation - -threads - -update() -delete() -yuml_graphqueryableinstance->yuml_conversation - - -yuml_event - -Event - - - -update() -delete() -yuml_graphqueryableinstance->yuml_event - - -yuml_calendar - -Calendar - -events - - -yuml_graphqueryableinstance->yuml_calendar - - -yuml_attachment - -Attachment - - - - -yuml_graphqueryableinstance->yuml_attachment - - -yuml_graphqueryablecollection - -GraphQueryableCollection - -count - -filter() -select() -expand() -orderBy() -top() -skip() -skipToken() -yuml_users - -Users - - - -getById() -yuml_graphqueryablecollection->yuml_users - - -yuml_plans - -Plans - - - -getById() -yuml_graphqueryablecollection->yuml_plans - - -yuml_pages - -Pages - - - - -yuml_graphqueryablecollection->yuml_pages - - -yuml_sections - -Sections - - - -getById() -add() -yuml_graphqueryablecollection->yuml_sections - - -yuml_notebooks - -Notebooks - - - -getById() -add() -yuml_graphqueryablecollection->yuml_notebooks - - -yuml_members - -Members - - - -add() -getById() -yuml_graphqueryablecollection->yuml_members - - -yuml_groups - -Groups - - - -getById() -add() -yuml_graphqueryablecollection->yuml_groups - - -yuml_graphqueryablesearchablecollection - -GraphQueryableSearchableCollection - - - -search() -yuml_graphqueryablecollection->yuml_graphqueryablesearchablecollection - - -yuml_senders - -Senders - - - -add() -remove() -yuml_graphqueryablecollection->yuml_senders - - -yuml_posts - -Posts - - - -getById() -add() -yuml_graphqueryablecollection->yuml_posts - - -yuml_threads - -Threads - - - -getById() -add() -yuml_graphqueryablecollection->yuml_threads - - -yuml_conversations - -Conversations - - - -add() -getById() -yuml_graphqueryablecollection->yuml_conversations - - -yuml_events - -Events - - - -getById() -add() -yuml_graphqueryablecollection->yuml_events - - -yuml_calendars - -Calendars - - - - -yuml_graphqueryablecollection->yuml_calendars - - -yuml_attachments - -Attachments - - - -getById() -addFile() -yuml_graphqueryablecollection->yuml_attachments - - -yuml_teamproperties - -TeamProperties - -memberSettings -guestSettings -messagingSettings -funSettings - - -yuml_graphendpoints - -GraphEndpoints - -Beta -V1 - -ensure() -yuml_teamcreateresult - -TeamCreateResult - -data -group -team - - -yuml_teamupdateresult - -TeamUpdateResult - -data -team - - -yuml_teams - -Teams - - - -create() -yuml_graphqueryable - -GraphQueryable - - - -as() -toUrlAndQuery() -getParent() -clone() -setEndpoint() -toRequestContext() -yuml_graphqueryable->yuml_graphqueryableinstance - - -yuml_graphqueryable->yuml_graphqueryablecollection - - -yuml_graphrest - -GraphRest - -groups -teams -me -users - -setup() -yuml_graphqueryable->yuml_graphrest - - -yuml_onenotemethods - -OneNoteMethods - -notebooks -sections -pages - - -yuml_onenotemethods->yuml_onenote - - -yuml_sectionaddresult - -SectionAddResult - -data -section - - -yuml_notebookaddresult - -NotebookAddResult - -data -notebook - - -yuml_owners - -Owners - - - - -yuml_members->yuml_owners - - -yuml_groupaddresult - -GroupAddResult - -group -data - - -yuml_odataqueryable - -ODataQueryable -yuml_odataqueryable->yuml_graphqueryable - - -yuml_graphqueryableconstructor - -GraphQueryableConstructor - - - - -yuml_postforwardinfo - -PostForwardInfo - -comment -toRecipients - - -yuml_eventaddresult - -EventAddResult - -data -event - - -yuml_odatabatch - -ODataBatch -yuml_graphbatch - -GraphBatch - -batchUrl - -executeImpl() -formatRequests() -_parseResponse() -yuml_odatabatch->yuml_graphbatch - - - - diff --git a/docs/v3/v1/documentation/img/pnpjs-logging-uml.svg b/docs/v3/v1/documentation/img/pnpjs-logging-uml.svg deleted file mode 100644 index 3a16c31ef..000000000 --- a/docs/v3/v1/documentation/img/pnpjs-logging-uml.svg +++ /dev/null @@ -1,59 +0,0 @@ - - - - -Gyuml_logger - -Logger - -_instance -activeLogLevel -instance -count - -subscribe() -clearSubscribers() -write() -writeJSON() -log() -error() -yuml_logentry - -LogEntry - -message -level -data - - -yuml_loglistener - -LogListener - - - -log() -yuml_functionlistener - -FunctionListener - -method - -log() -yuml_loglistener->yuml_functionlistener - - -yuml_consolelistener - -ConsoleListener - - - -log() -format() -yuml_loglistener->yuml_consolelistener - - - - diff --git a/docs/v3/v1/documentation/img/pnpjs-nodejs-uml.svg b/docs/v3/v1/documentation/img/pnpjs-nodejs-uml.svg deleted file mode 100644 index 2cd7c11da..000000000 --- a/docs/v3/v1/documentation/img/pnpjs-nodejs-uml.svg +++ /dev/null @@ -1,85 +0,0 @@ - - - - -Gyuml_httpclientimpl - -HttpClientImpl -yuml_spfetchclient - -SPFetchClient - -siteUrl -_clientId -_clientSecret -authEnv -_realm -SharePointServicePrincipal -token - -fetch() -getAddInOnlyAccessToken() -getAuthHostUrl() -getRealm() -getAuthUrl() -getFormattedPrincipal() -toDate() -yuml_httpclientimpl->yuml_spfetchclient - - -yuml_adalfetchclient - -AdalFetchClient - -_tenant -_clientId -_secret -_resource -_authority -authContext - -fetch() -acquireToken() -yuml_httpclientimpl->yuml_adalfetchclient - - -yuml_authtoken - -AuthToken - -token_type -expires_in -not_before -expires_on -resource -access_token - - -yuml_aadtoken - -AADToken - -accessToken -expiresIn -expiresOn -isMRRT -resource -tokenType - - -yuml_error - -Error -yuml_authurlexception - -AuthUrlException - - - - -yuml_error->yuml_authurlexception - - - - diff --git a/docs/v3/v1/documentation/img/pnpjs-odata-uml.svg b/docs/v3/v1/documentation/img/pnpjs-odata-uml.svg deleted file mode 100644 index fd1726a34..000000000 --- a/docs/v3/v1/documentation/img/pnpjs-odata-uml.svg +++ /dev/null @@ -1,253 +0,0 @@ - - - - -Gyuml_queryable - -Queryable - -_options -_query -_url -_parentUrl -_useCaching -_cachingOptions -query -parentUrl - -toUrlAndQuery() -toUrl() -concat() -configure() -configureFrom() -usingCaching() -getCore() -postCore() -patchCore() -deleteCore() -putCore() -append() -extend() -toRequestContext() -yuml_odataqueryable - -ODataQueryable - -_batch -hasBatch -batch - -inBatch() -toUrl() -get() -getCore() -postCore() -patchCore() -deleteCore() -putCore() -addBatchDependency() -yuml_queryable->yuml_odataqueryable - - -yuml_error - -Error -yuml_alreadyinbatchexception - -AlreadyInBatchException - - - - -yuml_error->yuml_alreadyinbatchexception - - -yuml_processhttpclientresponseexception - -ProcessHttpClientResponseException - -status -statusText -data - - -yuml_error->yuml_processhttpclientresponseexception - - -yuml_requestcontext - -RequestContext - -batch -batchDependency -cachingOptions -hasResult -isBatched -isCached -options -parser -pipeline -requestAbsoluteUrl -requestId -result -verb -clientFactory - - -yuml_pipelinemethods - -PipelineMethods - - - -logStart() -caching() -send() -logEnd() -yuml_odataparser - -ODataParser - -hydrate - -parse() -yuml_lambdaparser - -LambdaParser - -parser - -parse() -yuml_odataparser->yuml_lambdaparser - - -yuml_odataparserbase - -ODataParserBase - - - -parse() -parseImpl() -handleError() -parseODataJSON() -yuml_odataparser->yuml_odataparserbase - - -yuml_cachingparserwrapper - -CachingParserWrapper - -parser -cacheOptions - -parse() -cacheData() -yuml_odataparser->yuml_cachingparserwrapper - - -yuml_bufferparser - -BufferParser - - - -parseImpl() -yuml_odataparserbase->yuml_bufferparser - - -yuml_jsonparser - -JSONParser - - - -parseImpl() -yuml_odataparserbase->yuml_jsonparser - - -yuml_blobparser - -BlobParser - - - -parseImpl() -yuml_odataparserbase->yuml_blobparser - - -yuml_textparser - -TextParser - - - -parseImpl() -yuml_odataparserbase->yuml_textparser - - -yuml_odatadefaultparser - -ODataDefaultParser - - - - -yuml_odataparserbase->yuml_odatadefaultparser - - -yuml_odatabatchrequestinfo - -ODataBatchRequestInfo - -url -method -options -parser -resolve -reject -id - - -yuml_odatabatch - -ODataBatch - -_batchId -_dependencies -_requests -_resolveBatchDependencies -batchId -requests - -add() -addDependency() -addResolveBatchDependency() -execute() -executeImpl() -yuml_icachingoptions - -ICachingOptions - -expiration -storeName -key - - -yuml_cachingoptions - -CachingOptions - -key -storage -expiration -storeName -store - - -yuml_icachingoptions->yuml_cachingoptions - - - - diff --git a/docs/v3/v1/documentation/img/pnpjs-sp-addinhelpers-uml.svg b/docs/v3/v1/documentation/img/pnpjs-sp-addinhelpers-uml.svg deleted file mode 100644 index 23ce95ab5..000000000 --- a/docs/v3/v1/documentation/img/pnpjs-sp-addinhelpers-uml.svg +++ /dev/null @@ -1,48 +0,0 @@ - - - - -Gyuml_sprest - -SPRest -yuml_sprestaddin - -SPRestAddIn - - - -crossDomainSite() -crossDomainWeb() -_cdImpl() -yuml_sprest->yuml_sprestaddin - - -yuml_httpclientimpl - -HttpClientImpl -yuml_sprequestexecutorclient - -SPRequestExecutorClient - -convertToResponse - -fetch() -yuml_httpclientimpl->yuml_sprequestexecutorclient - - -yuml_error - -Error -yuml_sprequestexecutorundefinedexception - -SPRequestExecutorUndefinedException - - - - -yuml_error->yuml_sprequestexecutorundefinedexception - - - - diff --git a/docs/v3/v1/documentation/img/pnpjs-sp-clientsvc-uml.svg b/docs/v3/v1/documentation/img/pnpjs-sp-clientsvc-uml.svg deleted file mode 100644 index 5d4be5a9b..000000000 --- a/docs/v3/v1/documentation/img/pnpjs-sp-clientsvc-uml.svg +++ /dev/null @@ -1,167 +0,0 @@ - - - - -Gyuml_processqueryparser - -ProcessQueryParser - -op - -parse() -findResult() -getParsedResultById() -yuml_imethodparamsbuilder - -IMethodParamsBuilder - - - -string() -number() -boolean() -objectPath() -toArray() -yuml_methodparams - -MethodParams - -_p - -build() -string() -number() -boolean() -objectPath() -toArray() -a() -yuml_imethodparamsbuilder->yuml_methodparams - - -yuml_iobjectpath - -IObjectPath - -path -actions -id - - -yuml_objectpath - -ObjectPath - -path -actions -id -replaceAfter - - -yuml_iobjectpath->yuml_objectpath - - -yuml_objectpathqueue - -ObjectPathQueue - -_paths -_relationships -_xml -_contextIndex -_siteIndex -_webIndex -last -lastIndex -siteIndex -webIndex -contextIndex - -add() -addChildRelationship() -getChildRelationship() -getChildRelationships() -appendAction() -appendActionToLast() -clone() -toArray() -hash() -toBody() -toIndexedTree() -dirty() -yuml_iclientsvcqueryable - -IClientSvcQueryable - - - -select() -usingCaching() -inBatch() -yuml_clientsvcqueryable - -ClientSvcQueryable - -_objectPaths -_selects -_batch -hasBatch -batch - -select() -inBatch() -toUrlAndQuery() -getSelects() -getChild() -getChildProperty() -send() -sendGet() -sendGetCollection() -invokeMethod() -invokeNonQuery() -invokeMethodCollection() -invokeUpdate() -toRequestContext() -addBatchDependency() -invokeMethodImpl() -yuml_iclientsvcqueryable->yuml_clientsvcqueryable - - -yuml_queryable - -Queryable -yuml_queryable->yuml_clientsvcqueryable - - -yuml_clientsvcqueryableconstructor - -ClientSvcQueryableConstructor - - - - -yuml_iobjectpathbatch - -IObjectPathBatch - - - - -yuml_objectpathbatch - -ObjectPathBatch - -parentUrl - -executeImpl() -yuml_iobjectpathbatch->yuml_objectpathbatch - - -yuml_odatabatch - -ODataBatch -yuml_odatabatch->yuml_objectpathbatch - - - - diff --git a/docs/v3/v1/documentation/img/pnpjs-sp-taxonomy-uml.svg b/docs/v3/v1/documentation/img/pnpjs-sp-taxonomy-uml.svg deleted file mode 100644 index 5944a31d3..000000000 --- a/docs/v3/v1/documentation/img/pnpjs-sp-taxonomy-uml.svg +++ /dev/null @@ -1,458 +0,0 @@ - - - - -Gyuml_changeinformation - -ChangeInformation - -ItemType -OperationType -StartTime -WithinTimeSpan - - -yuml_changeditem - -ChangedItem - -ChangedBy -ChangedTime -Id -ItemType -Operation -SiteId -TermId -ChangedCustomProperties -ChangedLocalCustomProperties -LcidsForChangedDescriptions -LcidsForChangedLabels -TermSetId -FromGroupId -GroupId -ChangedLanguage -IsDefaultLanguageChanged -IsFullFarmRestore - - -yuml_ilabelmatchinfo - -ILabelMatchInfo - -DefaultLabelOnly -ExcludeKeyword -Lcid -ResultCollectionSize -StringMatchOption -TermLabel -TrimDeprecated -TrimUnavailable - - -yuml_timespan - -TimeSpan - -Days -Hours -Milliseconds -Minutes -Seconds -Ticks -TotalDays -TotalHours -TotalMilliseconds -TotalMinutes -TotalSeconds - - -yuml_itermstore - -ITermStore - -hashTagsTermSet -keywordsTermSet -orphanedTermsTermSet -systemGroup - -addGroup() -addLanguage() -commitAll() -deleteLanguage() -get() -getChanges() -getSiteCollectionGroup() -getTermById() -getTermInTermSet() -getTermGroupById() -getTerms() -getTermSetById() -getTermSetsByName() -rollbackAll() -update() -updateCache() -updateUsedTermsOnSite() -yuml_termstore - -TermStore - -hashTagsTermSet -keywordsTermSet -orphanedTermsTermSet -systemGroup - -get() -getTermSetsByName() -getTermSetById() -getTermById() -getTermInTermSet() -getTermGroupById() -getTerms() -getSiteCollectionGroup() -addLanguage() -addGroup() -commitAll() -deleteLanguage() -rollbackAll() -updateCache() -update() -updateUsedTermsOnSite() -getChanges() -yuml_itermstore->yuml_termstore - - -yuml_clientsvcqueryable - -ClientSvcQueryable -yuml_clientsvcqueryable->yuml_termstore - - -yuml_termstores - -TermStores - - - -get() -getByName() -getById() -yuml_clientsvcqueryable->yuml_termstores - - -yuml_termset - -TermSet - -group -terms - -addStakeholder() -deleteStakeholder() -get() -getTermById() -addTerm() -copy() -update() -yuml_clientsvcqueryable->yuml_termset - - -yuml_termsets - -TermSets - - - -get() -getById() -getByName() -yuml_clientsvcqueryable->yuml_termsets - - -yuml_term - -Term - -labels -parent -pinSourceTermSet -reusedTerms -sourceTerm -termSet -termSets - -createLabel() -deprecate() -get() -setDescription() -setLocalCustomProperty() -update() -yuml_clientsvcqueryable->yuml_term - - -yuml_terms - -Terms - - - -get() -getById() -getByName() -yuml_clientsvcqueryable->yuml_terms - - -yuml_termgroup - -TermGroup - -store -termSets - -addContributor() -addGroupManager() -createTermSet() -get() -update() -yuml_clientsvcqueryable->yuml_termgroup - - -yuml_session - -Session - -termStores - -setup() -createBatch() -getDefaultKeywordTermStore() -getDefaultSiteCollectionTermStore() -yuml_clientsvcqueryable->yuml_session - - -yuml_label - -Label - - - -get() -setAsDefaultForLanguage() -delete() -yuml_clientsvcqueryable->yuml_label - - -yuml_labels - -Labels - - - -getByValue() -get() -yuml_clientsvcqueryable->yuml_labels - - -yuml_itermstores - -ITermStores - - - -get() -getByName() -getById() -yuml_itermstores->yuml_termstores - - -yuml_itermstoredata - -ITermStoreData - -DefaultLanguage -Id -IsOnline -Languages -Name -WorkingLanguage - - -yuml_itermset - -ITermSet - -terms -group - -copy() -get() -getTermById() -addTerm() -update() -yuml_itermset->yuml_termset - - -yuml_itermsets - -ITermSets - - - -getById() -getByName() -get() -yuml_itermsets->yuml_termsets - - -yuml_itermsetdata - -ITermSetData - -Contact -CreatedDate -CustomProperties -CustomSortOrder -Description -Id -IsAvailableForTagging -IsOpenForTermCreation -LastModifiedDate -Name -Names -Owner -Stakeholders - - -yuml_iterm - -ITerm - -labels -parent -pinSourceTermSet -reusedTerms -sourceTerm -termSet -termSets - -createLabel() -deprecate() -get() -setDescription() -setLocalCustomProperty() -update() -yuml_iterm->yuml_term - - -yuml_iterms - -ITerms - - - -get() -getById() -getByName() -yuml_iterms->yuml_terms - - -yuml_itermdata - -ITermData - -CustomProperties -CustomSortOrder -Description -Id -IsAvailableForTagging -IsDeprecated -IsKeyword -IsPinned -IsPinnedRoot -IsReused -IsRoot -IsSourceTerm -LastModifiedDate -LocalCustomProperties -MergedTermIds -Name -Owner -PathOfTerm -TermsCount - - -yuml_itermgroup - -ITermGroup - -store -termSets - -addContributor() -addGroupManager() -createTermSet() -get() -update() -yuml_itermgroup->yuml_termgroup - - -yuml_itermgroupdata - -ITermGroupData - -CreatedDate -Description -Id -IsSiteCollectionGroup -IsSystemGroup -LastModifiedDate -Name - - -yuml_itaxonomysession - -ITaxonomySession - -termStores - -setup() -createBatch() -getDefaultKeywordTermStore() -getDefaultSiteCollectionTermStore() -yuml_itaxonomysession->yuml_session - - -yuml_ilabel - -ILabel - - - -get() -setAsDefaultForLanguage() -delete() -yuml_ilabel->yuml_label - - -yuml_ilabels - -ILabels - - - -getByValue() -get() -yuml_ilabels->yuml_labels - - -yuml_ilabeldata - -ILabelData - -IsDefaultForLanguage -Language -Value - - - - diff --git a/docs/v3/v1/documentation/img/pnpjs-sp-uml.svg b/docs/v3/v1/documentation/img/pnpjs-sp-uml.svg deleted file mode 100644 index f71d83e0a..000000000 --- a/docs/v3/v1/documentation/img/pnpjs-sp-uml.svg +++ /dev/null @@ -1,2833 +0,0 @@ - - - - -Gyuml_requestclient - -RequestClient -yuml_sphttpclient - -SPHttpClient - -_digestCache -_impl - -fetch() -fetchRaw() -get() -post() -patch() -delete() -yuml_requestclient->yuml_sphttpclient - - -yuml_digestcache - -DigestCache - -_httpClient -_digests - -getDigest() -clear() -yuml_cacheddigest - -CachedDigest - -expiration -value - - -yuml_spconfiguration - -SPConfiguration - - - - -yuml_spconfigurationpart - -SPConfigurationPart - -sp - - -yuml_spruntimeconfigimpl - -SPRuntimeConfigImpl - -headers -baseUrl -fetchClientFactory - - -yuml_sharepointqueryableshareableweb - -SharePointQueryableShareableWeb - - - -shareWith() -shareObject() -shareObjectRaw() -unshareObject() -yuml_web - -Web - -webs -allProperties -webinfos -contentTypes -lists -fields -features -availablefields -navigation -siteUsers -siteGroups -siteUserInfoList -regionalSettings -currentUser -folders -userCustomActions -roleDefinitions -relatedItems -rootFolder -associatedOwnerGroup -associatedMemberGroup -associatedVisitorGroup -customListTemplate - -fromUrl() -getParentWeb() -getSubwebsFilteredForCurrentUser() -createBatch() -getFolderByServerRelativeUrl() -getFolderByServerRelativePath() -getFileByServerRelativeUrl() -getFileByServerRelativePath() -getList() -update() -delete() -applyTheme() -applyWebTemplate() -ensureUser() -availableWebTemplates() -getCatalog() -getChanges() -getUserById() -mapToIcon() -getStorageEntity() -setStorageEntity() -removeStorageEntity() -getAppCatalog() -getClientSideWebParts() -addClientSidePage() -addClientSidePageByPath() -yuml_sharepointqueryableshareableweb->yuml_web - - -yuml_sharepointqueryablecollection - -SharePointQueryableCollection - - - -filter() -select() -expand() -orderBy() -skip() -top() -yuml_webinfos - -WebInfos - - - - -yuml_sharepointqueryablecollection->yuml_webinfos - - -yuml_webs - -Webs - - - -add() -yuml_sharepointqueryablecollection->yuml_webs - - -yuml_webpartdefinitions - -WebPartDefinitions - - - -getById() -getByControlId() -yuml_sharepointqueryablecollection->yuml_webpartdefinitions - - -yuml_viewfields - -ViewFields - - - -getSchemaXml() -add() -move() -removeAll() -remove() -yuml_sharepointqueryablecollection->yuml_viewfields - - -yuml_views - -Views - - - -getById() -getByTitle() -add() -yuml_sharepointqueryablecollection->yuml_views - - -yuml_usercustomactions - -UserCustomActions - - - -getById() -add() -clear() -yuml_sharepointqueryablecollection->yuml_usercustomactions - - -yuml_subscriptions - -Subscriptions - - - -getById() -add() -yuml_sharepointqueryablecollection->yuml_subscriptions - - -yuml_siteusers - -SiteUsers - - - -getByEmail() -getById() -getByLoginName() -removeById() -removeByLoginName() -add() -yuml_sharepointqueryablecollection->yuml_siteusers - - -yuml_sitegroups - -SiteGroups - - - -add() -getByName() -getById() -removeById() -removeByLoginName() -yuml_sharepointqueryablecollection->yuml_sitegroups - - -yuml_roledefinitionbindings - -RoleDefinitionBindings - - - - -yuml_sharepointqueryablecollection->yuml_roledefinitionbindings - - -yuml_roledefinitions - -RoleDefinitions - - - -getById() -getByName() -getByType() -add() -yuml_sharepointqueryablecollection->yuml_roledefinitions - - -yuml_roleassignments - -RoleAssignments - - - -add() -remove() -getById() -yuml_sharepointqueryablecollection->yuml_roleassignments - - -yuml_timezones - -TimeZones - - - -getById() -yuml_sharepointqueryablecollection->yuml_timezones - - -yuml_installedlanguages - -InstalledLanguages - - - - -yuml_sharepointqueryablecollection->yuml_installedlanguages - - -yuml_navigationnodes - -NavigationNodes - - - -getById() -add() -moveAfter() -yuml_sharepointqueryablecollection->yuml_navigationnodes - - -yuml_lists - -Lists - - - -getByTitle() -getById() -add() -ensure() -ensureSiteAssetsLibrary() -ensureSitePagesLibrary() -yuml_sharepointqueryablecollection->yuml_lists - - -yuml_itemversions - -ItemVersions - - - -getById() -yuml_sharepointqueryablecollection->yuml_itemversions - - -yuml_items - -Items - - - -getById() -getItemByStringId() -skip() -getPaged() -getAll() -add() -ensureListItemEntityTypeName() -yuml_sharepointqueryablecollection->yuml_items - - -yuml_forms - -Forms - - - -getById() -yuml_sharepointqueryablecollection->yuml_forms - - -yuml_folders - -Folders - - - -getByName() -add() -yuml_sharepointqueryablecollection->yuml_folders - - -yuml_versions - -Versions - - - -getById() -deleteAll() -deleteById() -recycleByID() -deleteByLabel() -recycleByLabel() -restoreByLabel() -yuml_sharepointqueryablecollection->yuml_versions - - -yuml_files - -Files - - - -getByName() -add() -addChunked() -addTemplateFile() -yuml_sharepointqueryablecollection->yuml_files - - -yuml_fields - -Fields - - - -getByTitle() -getByInternalNameOrTitle() -getById() -createFieldAsXml() -add() -addText() -addCalculated() -addDateTime() -addNumber() -addCurrency() -addMultilineText() -addUrl() -addUser() -addLookup() -addChoice() -addMultiChoice() -addBoolean() -yuml_sharepointqueryablecollection->yuml_fields - - -yuml_features - -Features - - - -getById() -add() -remove() -yuml_sharepointqueryablecollection->yuml_features - - -yuml_fieldlinks - -FieldLinks - - - -getById() -yuml_sharepointqueryablecollection->yuml_fieldlinks - - -yuml_contenttypes - -ContentTypes - - - -getById() -addAvailableContentType() -add() -yuml_sharepointqueryablecollection->yuml_contenttypes - - -yuml_replies - -Replies - - - -add() -yuml_sharepointqueryablecollection->yuml_replies - - -yuml_comments - -Comments - - - -getById() -add() -clear() -yuml_sharepointqueryablecollection->yuml_comments - - -yuml_attachmentfiles - -AttachmentFiles - - - -getByName() -add() -addMultiple() -deleteMultiple() -yuml_sharepointqueryablecollection->yuml_attachmentfiles - - -yuml_appcatalog - -AppCatalog - - - -getAppById() -add() -yuml_sharepointqueryablecollection->yuml_appcatalog - - -yuml_webensureuserresult - -WebEnsureUserResult - -data -user - - -yuml_getcatalogresult - -GetCatalogResult - -data -list - - -yuml_webupdateresult - -WebUpdateResult - -data -web - - -yuml_webaddresult - -WebAddResult - -data -web - - -yuml_sharepointqueryableinstance - -SharePointQueryableInstance - - - -select() -expand() -yuml_webpart - -WebPart - - - - -yuml_sharepointqueryableinstance->yuml_webpart - - -yuml_webpartdefinition - -WebPartDefinition - -webpart - -saveChanges() -moveTo() -close() -open() -delete() -yuml_sharepointqueryableinstance->yuml_webpartdefinition - - -yuml_view - -View - -fields - -update() -delete() -renderAsHtml() -yuml_sharepointqueryableinstance->yuml_view - - -yuml_userprofilequery - -UserProfileQuery - -clientPeoplePickerQuery -profileLoader -editProfileLink -isMyPeopleListPublic -myFollowers -myProperties -trendingTags -ownerUserProfile -userProfile - -amIFollowedBy() -amIFollowing() -getFollowedTags() -getFollowersFor() -getPeopleFollowedBy() -getPropertiesFor() -getUserProfilePropertyFor() -hideSuggestion() -isFollowing() -setMyProfilePic() -setSingleValueProfileProperty() -setMultiValuedProfileProperty() -createPersonalSiteEnqueueBulk() -createPersonalSite() -shareAllSocialData() -clientPeoplePickerResolveUser() -clientPeoplePickerSearchUser() -yuml_sharepointqueryableinstance->yuml_userprofilequery - - -yuml_usercustomaction - -UserCustomAction - - - -update() -delete() -yuml_sharepointqueryableinstance->yuml_usercustomaction - - -yuml_subscription - -Subscription - - - -update() -delete() -yuml_sharepointqueryableinstance->yuml_subscription - - -yuml_mysocialquery - -MySocialQuery - - - -followed() -followedCount() -followers() -suggestions() -yuml_sharepointqueryableinstance->yuml_mysocialquery - - -yuml_socialquery - -SocialQuery - -my - -getFollowedSitesUri() -getFollowedDocumentsUri() -follow() -isFollowed() -stopFollowing() -createSocialActorInfoRequestBody() -yuml_sharepointqueryableinstance->yuml_socialquery - - -yuml_currentuser - -CurrentUser - - - - -yuml_sharepointqueryableinstance->yuml_currentuser - - -yuml_siteuser - -SiteUser - -groups - -update() -delete() -yuml_sharepointqueryableinstance->yuml_siteuser - - -yuml_sitegroup - -SiteGroup - -users - -update() -yuml_sharepointqueryableinstance->yuml_sitegroup - - -yuml_site - -Site - -rootWeb -features -userCustomActions - -getRootWeb() -getContextInfo() -getDocumentLibraries() -getWebUrlFromPageUrl() -createBatch() -openWebById() -yuml_sharepointqueryableinstance->yuml_site - - -yuml_filefoldershared - -FileFolderShared - - - -getShareLink() -checkSharingPermissions() -getSharingInformation() -getObjectSharingSettings() -unshare() -deleteSharingLinkByKind() -unshareLink() -getShareable() -yuml_sharepointqueryableinstance->yuml_filefoldershared - - -yuml_sharepointqueryablesecurable - -SharePointQueryableSecurable - -roleAssignments -firstUniqueAncestorSecurableObject - -getUserEffectivePermissions() -getCurrentUserEffectivePermissions() -breakRoleInheritance() -resetRoleInheritance() -userHasPermissions() -currentUserHasPermissions() -hasPermissions() -yuml_sharepointqueryableinstance->yuml_sharepointqueryablesecurable - - -yuml_searchsuggest - -SearchSuggest - - - -execute() -mapQueryToQueryString() -yuml_sharepointqueryableinstance->yuml_searchsuggest - - -yuml_search - -Search - - - -execute() -fixupProp() -yuml_sharepointqueryableinstance->yuml_search - - -yuml_roledefinition - -RoleDefinition - - - -update() -delete() -yuml_sharepointqueryableinstance->yuml_roledefinition - - -yuml_roleassignment - -RoleAssignment - -groups -bindings - -delete() -yuml_sharepointqueryableinstance->yuml_roleassignment - - -yuml_timezone - -TimeZone - - - -utcToLocalTime() -localTimeToUTC() -yuml_sharepointqueryableinstance->yuml_timezone - - -yuml_regionalsettings - -RegionalSettings - -installedLanguages -globalInstalledLanguages -timeZone -timeZones - - -yuml_sharepointqueryableinstance->yuml_regionalsettings - - -yuml_navigationnode - -NavigationNode - -children - -delete() -yuml_sharepointqueryableinstance->yuml_navigationnode - - -yuml_itemversion - -ItemVersion - - - -delete() -yuml_sharepointqueryableinstance->yuml_itemversion - - -yuml_form - -Form - - - - -yuml_sharepointqueryableinstance->yuml_form - - -yuml_version - -Version - - - -delete() -yuml_sharepointqueryableinstance->yuml_version - - -yuml_field - -Field - - - -update() -delete() -setShowInDisplayForm() -setShowInEditForm() -setShowInNewForm() -yuml_sharepointqueryableinstance->yuml_field - - -yuml_feature - -Feature - - - -deactivate() -yuml_sharepointqueryableinstance->yuml_feature - - -yuml_fieldlink - -FieldLink - - - - -yuml_sharepointqueryableinstance->yuml_fieldlink - - -yuml_contenttype - -ContentType - -fieldLinks -fields -parent -workflowAssociations - -delete() -yuml_sharepointqueryableinstance->yuml_contenttype - - -yuml_comment - -Comment - -replies - -like() -unlike() -delete() -yuml_sharepointqueryableinstance->yuml_comment - - -yuml_attachmentfile - -AttachmentFile - - - -getText() -getBlob() -getBuffer() -getJSON() -setContent() -delete() -getParsed() -yuml_sharepointqueryableinstance->yuml_attachmentfile - - -yuml_app - -App - - - -deploy() -retract() -install() -uninstall() -upgrade() -remove() -yuml_sharepointqueryableinstance->yuml_app - - -yuml_sharepointqueryable - -SharePointQueryable - - - -as() -toUrlAndQuery() -getParent() -clone() -toRequestContext() -yuml_sharepointqueryable->yuml_sharepointqueryablecollection - - -yuml_sharepointqueryable->yuml_sharepointqueryableinstance - - -yuml_limitedwebpartmanager - -LimitedWebPartManager - -webparts - -export() -import() -yuml_sharepointqueryable->yuml_limitedwebpartmanager - - -yuml_utilitymethod - -UtilityMethod - - - -getBaseUrl() -excute() -sendEmail() -getCurrentUserEmailAddresses() -resolvePrincipal() -searchPrincipals() -createEmailBodyForInvitation() -expandGroupsToPrincipals() -createWikiPage() -yuml_sharepointqueryable->yuml_utilitymethod - - -yuml_sharepointqueryableshareable - -SharePointQueryableShareable - - - -getShareLink() -shareWith() -shareObject() -unshareObjectWeb() -checkPermissions() -getSharingInformation() -getObjectSharingSettings() -unshareObject() -deleteLinkByKind() -unshareLink() -getRoleValue() -getShareObjectWeb() -sendShareObjectRequest() -yuml_sharepointqueryable->yuml_sharepointqueryableshareable - - -yuml_relateditemmanagerimpl - -RelatedItemManagerImpl - - - -FromUrl() -getRelatedItems() -getPageOneRelatedItems() -addSingleLink() -addSingleLinkToUrl() -addSingleLinkFromUrl() -deleteSingleLink() -yuml_sharepointqueryable->yuml_relateditemmanagerimpl - - -yuml_navigationservice - -NavigationService - - - -getMenuState() -getMenuNodeKey() -yuml_sharepointqueryable->yuml_navigationservice - - -yuml_navigation - -Navigation - -quicklaunch -topNavigationBar - - -yuml_sharepointqueryable->yuml_navigation - - -yuml_viewupdateresult - -ViewUpdateResult - -view -data - - -yuml_viewaddresult - -ViewAddResult - -view -data - - -yuml_utilitymethods - -UtilityMethods - - - -usingCaching() -inBatch() -sendEmail() -getCurrentUserEmailAddresses() -resolvePrincipal() -searchPrincipals() -createEmailBodyForInvitation() -expandGroupsToPrincipals() -createWikiPage() -yuml_utilitymethods->yuml_utilitymethod - - -yuml_createwikipageresult - -CreateWikiPageResult - -data -file - - -yuml_usercustomactionupdateresult - -UserCustomActionUpdateResult - -data -action - - -yuml_usercustomactionaddresult - -UserCustomActionAddResult - -data -action - - -yuml_likedata - -LikeData - -name -loginName -id -email -creationDate - - -yuml_storageentity - -StorageEntity - -Value -Comment -Description - - -yuml_peoplepickerentitydata - -PeoplePickerEntityData - -AccountName -Department -Email -IsAltSecIdPresent -MobilePhone -ObjectId -OtherMails -PrincipalType -SPGroupID -SPUserID -Title - - -yuml_peoplepickerentity - -PeoplePickerEntity - -Description -DisplayText -EntityData -EntityType -IsResolved -Key -MultipleMatches -ProviderDisplayName -ProviderName - - -yuml_peoplepickerquerysettings - -PeoplePickerQuerySettings - -ExcludeAllUsersOnTenantClaim - - -yuml_clientpeoplepickerqueryparameters - -ClientPeoplePickerQueryParameters - -AllowEmailAddresses -AllowMultipleEntities -AllowOnlyEmailAddresses -AllUrlZones -EnabledClaimProviders -ForceClaims -MaximumEntitySuggestions -PrincipalSource -PrincipalType -QuerySettings -QueryString -SharePointGroupID -UrlZone -UrlZoneSpecified -WebApplicationID - - -yuml_fieldcreationproperties - -FieldCreationProperties - -DefaultFormula -Description -EnforceUniqueValues -FieldTypeKind -Group -Hidden -Indexed -Required -Title -ValidationFormula -ValidationMessage - - -yuml_menunodecollection - -MenuNodeCollection - -FriendlyUrlPrefix -Nodes -SimpleUrl -SPSitePrefix -SPWebPrefix -StartingNodeKey -StartingNodeTitle -Version - - -yuml_menunode - -MenuNode - -CustomProperties -FriendlyUrlSegment -IsDeleted -IsHidden -Key -Nodes -NodeType -SimpleUrl -Title - - -yuml_renderlistdataparameters - -RenderListDataParameters - -AllowMultipleValueFilterForTaxonomyFields -DatesInUtc -ExpandGroups -FirstGroupOnly -FolderServerRelativeUrl -ImageFieldsToTryRewriteToCdnUrls -OverrideViewXml -Paging -RenderOptions -ReplaceGroup -ViewXml - - -yuml_wikipagecreationinformation - -WikiPageCreationInformation - -ServerRelativeUrl -WikiHtmlContent - - -yuml_emailproperties - -EmailProperties - -To -CC -BCC -Subject -Body -AdditionalHeaders -From - - -yuml_sharinginformation - -SharingInformation - -canAddExternalPrincipal -canAddInternalPrincipal -canSendEmail -canUseSimplifiedRoles -hasUniquePermissions -currentRole -requiresAccessApproval -hasPendingAccessRequests -pendingAccessRequestsLink -sharedObjectType -directUrl -webUrl -defaultLinkKind -domainRestrictionMode -RestrictedDomains -anonymousLinkExpirationRestrictionDays -permissionsInformation -pickerSettings - - -yuml_objectsharingsettings - -ObjectSharingSettings - -WebUrl -ListId -ItemId -ItemName -ItemUrl -ObjectSharingInformation -AccessRequestMode -PermissionsOnlyMode -InheritingWebLink -ShareByEmailEnabled -IsGuestUser -HasEditRole -HasReadRole -IsPictureLibrary -CanShareFolder -CanSendEmail -DefaultShareLinkType -SupportsAclPropagation -CanCurrentUserShareInternally -CanCurrentUserShareExternally -CanCurrentUserRetrieveReadonlyLink -CanCurrentUserManageReadonlyLink -CanCurrentUserRetrieveReadWriteLink -CanCurrentUserManageReadWriteLink -CanCurrentUserRetrieveOrganizationReadonlyLink -CanCurrentUserManageOrganizationReadonlyLink -CanCurrentUserRetrieveOrganizationReadWriteLink -CanCurrentUserManageOrganizationReadWriteLink -CanSendLink -ShowExternalSharingWarning -SharingPermissions -SimplifiedRoles -GroupsList -Roles -SharePointSettings -IsUserSiteAdmin -RequiredAnonymousLinkExpirationInDays - - -yuml_sharinginformationrequest - -SharingInformationRequest - -maxPrincipalsToReturn -clientSupportedFeatures - - -yuml_sharingentitypermission - -SharingEntityPermission - -inputEntity -resolvedEntity -hasAccess -role - - -yuml_sharingrecipient - -SharingRecipient - -email -alias - - -yuml_spinvitationcreationresult - -SPInvitationCreationResult - -Succeeded -Email -InvitationLink - - -yuml_usersharingresult - -UserSharingResult - -IsUserKnown -Status -Message -User -DisplayName -Email -CurrentRole -AllowedRoles -InvitationLink - - -yuml_sharingresult - -SharingResult - -PermissionsPageRelativeUrl -UsersWithAccessRequests -StatusCode -ErrorMessage -UniquelyPermissionedUsers -GroupsSharedWith -GroupUsersAddedTo -UsersAddedToGroup -InvitedUsers -Name -Url -IconUrl - - -yuml_sharinglinkinfo - -SharingLinkInfo - -AllowsAnonymousAccess -Created -CreatedBy -Expiration -IsActive -IsEditLink -IsFormsLink -IsUnhealthy -LastModified -LastModifiedBy -LinkKind -ShareId -Url - - -yuml_sharelinkresponse - -ShareLinkResponse - -sharingLinkInfo - - -yuml_sharelinkrequest - -ShareLinkRequest - -peoplePickerInput -createLink -emailData -settings - - -yuml_sharelinksettings - -ShareLinkSettings - -shareId -linkKind -expiration -role -allowAnonymousAccess - - -yuml_sharingemaildata - -SharingEmailData - -subject -body - - -yuml_shareobjectoptions - -ShareObjectOptions - -url -loginNames -role -emailData -group -propagateAcl -includeAnonymousLinkInEmail -useSimplifiedRoles - - -yuml_listformdata - -ListFormData - -ContentType -Title -Author -Editor -Created -Modified -Attachments -ListSchema -FormControlMode -FieldControlModes -WebAttributes -ItemAttributes -ListAttributes -CSRCustomLayout -PostBackRequired -PreviousPostBackHandled -UploadMode -SubmitButtonID -ItemContentTypeName -ItemContentTypeId -JSLinks - - -yuml_renderlistdata - -RenderListData - -Row -FirstRow -FolderPermissions -LastRow -FilterLink -ForceNoHierarchy -HierarchyHasIndention - - -yuml_contextinfo - -ContextInfo - -FormDigestTimeoutSeconds -FormDigestValue -LibraryVersion -SiteFullUrl -SupportedSchemaVersions -WebFullUrl - - -yuml_documentlibraryinformation - -DocumentLibraryInformation - -AbsoluteUrl -Modified -ModifiedFriendlyDisplay -ServerRelativeUrl -Title - - -yuml_principalinfo - -PrincipalInfo - -Department -DisplayName -Email -JobTitle -LoginName -Mobile -PrincipalId -PrincipalType -SIPAddress - - -yuml_useridinfo - -UserIdInfo - -NameId -NameIdIssuer - - -yuml_hashtagcollection - -HashTagCollection - -Items - - -yuml_hashtag - -HashTag - -Name -UseCount - - -yuml_userprofile - -UserProfile - -FollowedContent -AccountName -DisplayName -O15FirstRunExperience -PersonalSite -PersonalSiteCapabilities -PersonalSiteFirstCreationError -PersonalSiteFirstCreationTime -PersonalSiteInstantiationState -PersonalSiteLastCreationTime -PersonalSiteNumberOfRetries -PictureImportEnabled -PublicUrl -UrlToCreatePersonalSite - - -yuml_followedcontent - -FollowedContent - -FollowedDocumentsUrl -FollowedSitesUrl - - -yuml_basepermissions - -BasePermissions - -Low -High - - -yuml_xmlschemafieldcreationinformation - -XmlSchemaFieldCreationInformation - -Options -SchemaXml - - -yuml_listitemformupdatevalue - -ListItemFormUpdateValue - -ErrorMessage -FieldName -FieldValue -HasException - - -yuml_changelogitemquery - -ChangeLogitemQuery - -ChangeToken -Contains -Query -QueryOptions -RowLimit -ViewFields -ViewName - - -yuml_listitemcollectionposition - -ListItemCollectionPosition - -PagingInfo - - -yuml_camlquery - -CamlQuery - -DatesInUtc -FolderServerRelativeUrl -ListItemCollectionPosition -ViewXml - - -yuml_changequery - -ChangeQuery - -Add -Alert -ChangeTokenEnd -ChangeTokenStart -ContentType -DeleteObject -Field -File -Folder -Group -GroupMembershipAdd -GroupMembershipDelete -Item -List -Move -Navigation -Rename -Restore -RoleAssignmentAdd -RoleAssignmentDelete -RoleDefinitionAdd -RoleDefinitionDelete -RoleDefinitionUpdate -SecurityPolicy -Site -SystemUpdate -Update -User -View -Web - - -yuml_changetoken - -ChangeToken - -StringValue - - -yuml_subscriptionupdateresult - -SubscriptionUpdateResult - -subscription -data - - -yuml_subscriptionaddresult - -SubscriptionAddResult - -subscription -data - - -yuml_mysocialquerymethods - -MySocialQueryMethods - - - -get() -followed() -followedCount() -followers() -suggestions() -yuml_mysocialquerymethods->yuml_mysocialquery - - -yuml_socialmethods - -SocialMethods - -my - -getFollowedSitesUri() -getFollowedDocumentsUri() -follow() -isFollowed() -stopFollowing() -yuml_socialmethods->yuml_socialquery - - -yuml_mysocialdata - -MySocialData - -SocialActor -MyFollowedDocumentsUri -MyFollowedSitesUri - - -yuml_socialactor - -SocialActor - -ActorType -Id -Uri -Name -IsFollowed -Status -CanFollow -ImageUri -AccountName -EmailAddress -Title -StatusText -PersonalSiteUri -FollowedContentUri -ContentUri -LibraryUri -TagGuid - - -yuml_socialactorinfo - -SocialActorInfo - -AccountName -ActorType -ContentUri -Id -TagGuid - - -yuml_siteuserprops - -SiteUserProps - -Email -Id -IsHiddenInUI -IsShareByEmailGuestUser -IsSiteAdmin -LoginName -PrincipalType -Title - - -yuml_userupdateresult - -UserUpdateResult - -user -data - - -yuml_sitegroupaddresult - -SiteGroupAddResult - -group -data - - -yuml_groupaddresult - -GroupAddResult - -group -data - - -yuml_groupupdateresult - -GroupUpdateResult - -group -data - - -yuml_openwebbyidresult - -OpenWebByIdResult - -data -web - - -yuml_sharepointqueryableshareablefolder - -SharePointQueryableShareableFolder - - - -shareWith() -yuml_filefoldershared->yuml_sharepointqueryableshareablefolder - - -yuml_sharepointqueryableshareablefile - -SharePointQueryableShareableFile - - - -shareWith() -yuml_filefoldershared->yuml_sharepointqueryableshareablefile - - -yuml_folder - -Folder - -contentTypeOrder -files -folders -listItemAllFields -parentFolder -properties -serverRelativeUrl -uniqueContentTypeOrder - -update() -delete() -recycle() -getItem() -moveTo() -yuml_sharepointqueryableshareablefolder->yuml_folder - - -yuml_file - -File - -listItemAllFields -versions - -approve() -cancelUpload() -checkin() -checkout() -copyTo() -delete() -deny() -getLimitedWebPartManager() -moveTo() -publish() -recycle() -undoCheckout() -unpublish() -getText() -getBlob() -getBuffer() -getJSON() -setContent() -getItem() -setContentChunked() -startUpload() -continueUpload() -finishUpload() -yuml_sharepointqueryableshareablefile->yuml_file - - -yuml_sharepointqueryablesecurable->yuml_sharepointqueryableshareableweb - - -yuml_sharepointqueryableshareableitem - -SharePointQueryableShareableItem - - - -getShareLink() -shareWith() -checkSharingPermissions() -getSharingInformation() -getObjectSharingSettings() -unshare() -deleteSharingLinkByKind() -unshareLink() -yuml_sharepointqueryablesecurable->yuml_sharepointqueryableshareableitem - - -yuml_list - -List - -contentTypes -items -views -fields -forms -defaultView -userCustomActions -effectiveBasePermissions -eventReceivers -relatedFields -informationRightsManagementSettings -subscriptions -rootFolder - -getView() -update() -delete() -getChanges() -getItemsByCAMLQuery() -getListItemChangesSinceToken() -recycle() -renderListData() -renderListDataAsStream() -renderListFormData() -reserveListItemId() -getListItemEntityTypeFullName() -addValidateUpdateItemUsingPath() -yuml_sharepointqueryablesecurable->yuml_list - - -yuml_item - -Item - -attachmentFiles -contentType -comments -effectiveBasePermissions -effectiveBasePermissionsForUI -fieldValuesAsHTML -fieldValuesAsText -fieldValuesForEdit -folder -file -versions - -update() -getLikedBy() -like() -unlike() -delete() -recycle() -getWopiFrameUrl() -validateUpdateListItem() -ensureListItemEntityTypeName() -yuml_sharepointqueryableshareableitem->yuml_item - - -yuml_odataqueryable - -ODataQueryable -yuml_odataqueryable->yuml_sharepointqueryable - - -yuml_sharepointqueryableconstructor - -SharePointQueryableConstructor - - - - -yuml_personalresultsuggestion - -PersonalResultSuggestion - -HighlightedTitle -IsBestBet -Title -TypeId -Url - - -yuml_searchsuggestquery - -SearchSuggestQuery - -querytext -count -personalCount -preQuery -hitHighlighting -capitalize -culture -stemming -includePeople -queryRules -prefixMatch - - -yuml_searchsuggestresult - -SearchSuggestResult - -PeopleNames -PersonalResults -Queries - - -yuml_reorderingrule - -ReorderingRule - -MatchValue -Boost -MatchType - - -yuml_searchpropertyvalue - -SearchPropertyValue - -StrVal -BoolVal -Intval -StrArray -QueryPropertyValueTypeIndex - - -yuml_searchproperty - -SearchProperty - -Name -Value - - -yuml_sort - -Sort - -Property -Direction - - -yuml_resulttable - -ResultTable - -GroupTemplateId -ItemTemplateId -Properties -Table -Refiners -ResultTitle -ResultTitleUrl -RowCount -TableType -TotalRows -TotalRowsIncludingDuplicates - - -yuml_resulttablecollection - -ResultTableCollection - -QueryErrors -QueryId -QueryRuleId -CustomResults -RefinementResults -RelevantResults -SpecialTermResults - - -yuml_searchresponse - -SearchResponse - -ElapsedTime -Properties -PrimaryQueryResult -SecondaryQueryResults -SpellingSuggestion -TriggeredRules - - -yuml_searchresult - -SearchResult - -Rank -DocId -WorkId -Title -Author -Size -Path -Description -Write -LastModifiedTime -CollapsingStatus -HitHighlightedSummary -HitHighlightedProperties -contentclass -PictureThumbnailURL -ServerRedirectedURL -ServerRedirectedEmbedURL -ServerRedirectedPreviewURL -FileExtension -ContentTypeId -ParentLink -ViewsLifeTime -ViewsRecent -SectionNames -SectionIndexes -SiteLogo -SiteDescription -importance -SiteName -IsDocument -FileType -IsContainer -WebTemplate -SPWebUrl -UniqueId -ProgId -OriginalPath -RenderTemplateId -PartitionId -UrlZone -Culture - - -yuml_searchquery - -SearchQuery - -Querytext -QueryTemplate -EnableInterleaving -EnableStemming -TrimDuplicates -EnableNicknames -EnableFQL -EnablePhonetic -BypassResultTypes -ProcessBestBets -EnableQueryRules -EnableSorting -GenerateBlockRankLog -SourceId -RankingModelId -StartRow -RowLimit -RowsPerPage -SelectProperties -Culture -RefinementFilters -Refiners -HiddenConstraints -SortList -Timeout -HitHighlightedProperties -ClientType -PersonalizationData -ResultsUrl -QueryTag -Properties -ProcessPersonalFavorites -QueryTemplatePropertiesUrl -ReorderingRules -HitHighlightedMultivaluePropertyLimit -EnableOrderingHitHighlightedProperty -CollapseSpecification -UIlanguage -DesiredSnippetLength -MaxSnippetLength -SummaryLength - - -yuml_searchbuiltinsourceid - -SearchBuiltInSourceId - -Documents -ItemsMatchingContentType -ItemsMatchingTag -ItemsRelatedToCurrentUser -ItemsWithSameKeywordAsThisItem -LocalPeopleResults -LocalReportsAndDataResults -LocalSharePointResults -LocalVideoResults -Pages -Pictures -Popular -RecentlyChangedItems -RecommendedItems -Wiki - - -yuml_searchresults - -SearchResults - -_url -_query -_raw -_primary -ElapsedTime -RowCount -TotalRows -TotalRowsIncludingDuplicates -RawSearchResults -PrimarySearchResults - -getPage() -formatSearchResults() -yuml_searchquerybuilder - -SearchQueryBuilder - -_query -enableInterleaving -enableStemming -trimDuplicates -enableNicknames -enableFql -enablePhonetic -bypassResultTypes -processBestBets -enableQueryRules -enableSorting -generateBlockRankLog -processPersonalFavorites -enableOrderingHitHighlightedProperty - -create() -text() -template() -sourceId() -trimDuplicatesIncludeId() -rankingModelId() -startRow() -rowLimit() -rowsPerPage() -selectProperties() -culture() -timeZoneId() -refinementFilters() -refiners() -hiddenConstraints() -sortList() -timeout() -hithighlightedProperties() -clientType() -personalizationData() -resultsURL() -queryTag() -properties() -queryTemplatePropertiesUrl() -reorderingRules() -hitHighlightedMultivaluePropertyLimit() -collapseSpecification() -uiLanguage() -desiredSnippetLength() -maxSnippetLength() -summaryLength() -toSearchQuery() -extendQuery() -yuml_roledefinitionaddresult - -RoleDefinitionAddResult - -definition -data - - -yuml_roledefinitionupdateresult - -RoleDefinitionUpdateResult - -definition -data - - -yuml_sprest - -SPRest - -_options -_baseUrl -site -web -profiles -social -navigation -utility - -configure() -setup() -searchSuggest() -search() -createBatch() -create() -yuml_relateditemmanger - -RelatedItemManger - - - -getRelatedItems() -getPageOneRelatedItems() -addSingleLink() -addSingleLinkToUrl() -addSingleLinkFromUrl() -deleteSingleLink() -yuml_relateditemmanger->yuml_relateditemmanagerimpl - - -yuml_relateditem - -RelatedItem - -ListId -ItemId -Url -Title -WebId -IconUrl - - -yuml_inavigationservice - -INavigationService - - - -getMenuState() -getMenuNodeKey() -yuml_inavigationservice->yuml_navigationservice - - -yuml_navigationnodeaddresult - -NavigationNodeAddResult - -data -node - - -yuml_listensureresult - -ListEnsureResult - -list -created -data - - -yuml_listupdateresult - -ListUpdateResult - -list -data - - -yuml_listaddresult - -ListAddResult - -list -data - - -yuml_itemupdateresultdata - -ItemUpdateResultData - -odata.etag - - -yuml_itemupdateresult - -ItemUpdateResult - -item -data - - -yuml_itemaddresult - -ItemAddResult - -item -data - - -yuml_pageditemcollection - -PagedItemCollection - -parent -nextUrl -results -hasNext - -getNext() -yuml_folderupdateresult - -FolderUpdateResult - -folder -data - - -yuml_folderaddresult - -FolderAddResult - -folder -data - - -yuml_clientsidepage - -ClientSidePage - -sections -commentsDisabled - -create() -fromFile() -jsonToEscapedString() -escapedStringToJson() -addSection() -toHtml() -fromHtml() -load() -save() -enableComments() -disableComments() -findControlById() -findControl() -setCommentsOn() -mergePartToTree() -mergeColumnToTree() -updateProperties() -yuml_file->yuml_clientsidepage - - -yuml_fileaddresult - -FileAddResult - -file -data - - -yuml_chunkedfileuploadprogressdata - -ChunkedFileUploadProgressData - -uploadId -stage -blockNumber -totalBlocks -chunkSize -currentPointer -fileSize - - -yuml_fieldupdateresult - -FieldUpdateResult - -data -field - - -yuml_fieldaddresult - -FieldAddResult - -data -field - - -yuml_featureaddresult - -FeatureAddResult - -data -feature - - -yuml_error - -Error -yuml_notsupportedinbatchexception - -NotSupportedInBatchException - - - - -yuml_error->yuml_notsupportedinbatchexception - - -yuml_maxcommentlengthexception - -MaxCommentLengthException - - - - -yuml_error->yuml_maxcommentlengthexception - - -yuml_spbatchparseexception - -SPBatchParseException - - - - -yuml_error->yuml_spbatchparseexception - - -yuml_contenttypeaddresult - -ContentTypeAddResult - -contentType -data - - -yuml_commentinfo - -CommentInfo - -text -mentions - - -yuml_identity - -Identity - -loginName -email -name - - -yuml_commentdata - -CommentData - -author -createdDate -id -isLikedByUser -isReply -itemId -likeCount -listId -mentions -parentId -replyCount -text - - -yuml_commentauthordata - -CommentAuthorData - -email -id -isActive -isExternal -jobTitle -loginName -name -principalType -userId - - -yuml_clientsidepart - -ClientSidePart - - - -remove() -yuml_clientsidewebpart - -ClientSideWebpart - -title -description -propertieJson -webPartId -htmlProperties -serverProcessedContent -canvasDataVersion - -fromComponentDef() -import() -setProperties() -getProperties() -toHtml() -fromHtml() -getControlData() -renderHtmlProperties() -parseJsonProperties() -yuml_clientsidepart->yuml_clientsidewebpart - - -yuml_clientsidetext - -ClientSideText - -_text -text - -getControlData() -toHtml() -fromHtml() -yuml_clientsidepart->yuml_clientsidetext - - -yuml_canvascontrol - -CanvasControl - -controlType -dataVersion -column -order -id -controlData -jsonData - -toHtml() -fromHtml() -getControlData() -yuml_canvascontrol->yuml_clientsidepart - - -yuml_canvascolumn - -CanvasColumn - -section -order -factor -controls - -addControl() -getControl() -toHtml() -fromHtml() -getControlData() -remove() -yuml_canvascontrol->yuml_canvascolumn - - -yuml_clientsidewebpartdata - -ClientSideWebpartData - -dataVersion -description -id -instanceId -properties -title -serverProcessedContent - - -yuml_clientsidecontroldata - -ClientSideControlData - -controlType -id -editorType -position -webPartId -displayMode - - -yuml_clientsidecontrolposition - -ClientSideControlPosition - -controlIndex -sectionFactor -sectionIndex -zoneIndex - - -yuml_serverprocessedcontent - -ServerProcessedContent - -searchablePlainTexts -imageSources -links - - -yuml_clientsidepagecomponent - -ClientSidePageComponent - -ComponentType -Id -Manifest -ManifestType -Name -Status - - -yuml_canvassection - -CanvasSection - -page -order -columns -_memId -defaultColumn - -addColumn() -addControl() -toHtml() -remove() -yuml_odatabatch - -ODataBatch -yuml_spbatch - -SPBatch - -baseUrl - -ParseResponse() -executeImpl() -yuml_odatabatch->yuml_spbatch - - -yuml_attachmentfileaddresult - -AttachmentFileAddResult - -file -data - - -yuml_attachmentfileinfo - -AttachmentFileInfo - -name -content - - -yuml_appaddresult - -AppAddResult - -data -file - - - - diff --git a/docs/v3/v1/documentation/package-structure/index.html b/docs/v3/v1/documentation/package-structure/index.html deleted file mode 100644 index dd0eff086..000000000 --- a/docs/v3/v1/documentation/package-structure/index.html +++ /dev/null @@ -1,1875 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Package Structure - PnP/PnPjs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Skip to content - - - -