diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 83b268a..fde3636 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -3,10 +3,12 @@ on: push: paths: - '**.php' + - '!build/**/*.php' pull_request: types: [opened, edited, reopened, ready_for_review] paths: - '**.php' + - '!build/**/*.php' workflow_dispatch: concurrency: diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..85aee5a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v20 \ No newline at end of file diff --git a/build/1.2.0-beta/dataAttrListener.asset.php b/build/1.2.0-beta/dataAttrListener.asset.php deleted file mode 100644 index a58a0a0..0000000 --- a/build/1.2.0-beta/dataAttrListener.asset.php +++ /dev/null @@ -1 +0,0 @@ - array('wp-api-fetch', 'wp-dom-ready'), 'version' => 'e882fc6f7a957b49c6b4'); diff --git a/build/1.2.0-beta/dataAttrListener.js b/build/1.2.0-beta/dataAttrListener.js deleted file mode 100644 index a20615c..0000000 --- a/build/1.2.0-beta/dataAttrListener.js +++ /dev/null @@ -1 +0,0 @@ -(()=>{"use strict";var t={n:e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return t.d(n,{a:n}),n},d:(e,n)=>{for(var l in n)t.o(n,l)&&!t.o(e,l)&&Object.defineProperty(e,l,{enumerable:!0,get:n[l]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)};const e=window.wp.domReady;var n=t.n(e);const l=window.wp.apiFetch;var r=t.n(l);const i=window.nfdInstaller?.restUrl,o=window.nfdInstaller?.pluginInstallHash,a=`${i}/newfold-installer/v1/plugins/install`;n()((()=>{new window.MutationObserver((t=>{for(const e of t)if("childList"===e.type)for(const t of e.addedNodes)"object"==typeof t&&"function"==typeof t.querySelectorAll&&t.querySelectorAll("[data-nfd-installer-plugin-provider]").forEach((t=>{t.addEventListener("click",(function(t){null!==t.target.getAttribute("data-nfd-installer-plugin-slug")&&r()({url:a,method:"POST",headers:{"X-NFD-INSTALLER":o},data:{plugin:this.getAttribute("data-nfd-installer-plugin-slug"),activate:"true"===this.getAttribute("data-nfd-installer-plugin-activate"),queue:!1,priority:0,premium:!0}})}))}))})).observe(document.body,{childList:!0,subtree:!0})})),((window.newfold=window.newfold||{}).Installer=window.newfold.Installer||{}).dataAttrListener={}})(); \ No newline at end of file diff --git a/build/1.2.0/dataAttrListener.asset.php b/build/1.2.0/dataAttrListener.asset.php new file mode 100644 index 0000000..4db50db --- /dev/null +++ b/build/1.2.0/dataAttrListener.asset.php @@ -0,0 +1 @@ + array('wp-dom-ready'), 'version' => 'b208b9a3938b304abbde'); diff --git a/build/1.2.0/dataAttrListener.js b/build/1.2.0/dataAttrListener.js new file mode 100644 index 0000000..e370b30 --- /dev/null +++ b/build/1.2.0/dataAttrListener.js @@ -0,0 +1 @@ +(()=>{"use strict";var t={n:e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return t.d(n,{a:n}),n},d:(e,n)=>{for(var l in n)t.o(n,l)&&!t.o(e,l)&&Object.defineProperty(e,l,{enumerable:!0,get:n[l]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)};const e=window.wp.domReady;t.n(e)()((()=>{function t(t){window.dispatchEvent(new CustomEvent("installerParamsSet",{detail:t}))}document.body.addEventListener("click",(e=>{const n=e.target;if(n.hasAttribute("data-nfd-installer-plugin-name")){e.preventDefault();const l=n.getAttribute("href")||n.getAttribute("data-nfd-installer-plugin-url");if(n.hasAttribute("data-nfd-installer-download-url"))return t({action:"installFreePlugin",pluginName:n.getAttribute("data-nfd-installer-plugin-name"),pluginDownloadUrl:n.getAttribute("data-nfd-installer-download-url"),pluginProvider:n.getAttribute("data-nfd-installer-pls-provider"),redirectUrl:l}),!1;if(n.hasAttribute("data-nfd-installer-pls-slug")&&n.hasAttribute("data-nfd-installer-pls-provider"))return t({action:"installPremiumPlugin",pluginName:n.getAttribute("data-nfd-installer-plugin-name"),pluginSlug:n.getAttribute("data-nfd-installer-pls-slug"),pluginProvider:n.getAttribute("data-nfd-installer-pls-provider"),redirectUrl:l}),!1;l&&(window.location.href=l)}}))})),((window.newfold=window.newfold||{}).Installer=window.newfold.Installer||{}).dataAttrListener={}})(); \ No newline at end of file diff --git a/build/1.2.0/installer.asset.php b/build/1.2.0/installer.asset.php new file mode 100644 index 0000000..730cf78 --- /dev/null +++ b/build/1.2.0/installer.asset.php @@ -0,0 +1 @@ + array('react', 'wp-api-fetch', 'wp-dom-ready', 'wp-element', 'wp-i18n'), 'version' => '0374e667fa1f111980fb'); diff --git a/build/1.2.0/installer.css b/build/1.2.0/installer.css new file mode 100644 index 0000000..c511d5d --- /dev/null +++ b/build/1.2.0/installer.css @@ -0,0 +1 @@ +.nfd-installer-modal{align-items:center;background:hsla(0,0%,100%,.5);display:flex;height:100vh;justify-content:center;left:0;position:fixed;top:0;width:100vw;z-index:99}@media(max-width:600px){.nfd-installer-modal{top:30px}}.nfd-installer-modal__content{background-color:#fff;border-radius:8px;box-shadow:5px 5px 10px rgba(0,0,0,.3);padding:72px;position:relative;text-align:center;width:40vw}@media(max-width:600px){.nfd-installer-modal__content{padding:24px;width:80vw}}.nfd-installer-modal__content-heading{color:#333;font-size:22px;font-weight:300;letter-spacing:1.1px;margin-bottom:60px}@media(max-width:600px){.nfd-installer-modal__content-heading{font-size:18px;margin-bottom:20px}}.nfd-installer-modal__content-section{align-items:center;display:flex;flex-direction:column}.nfd-installer-modal__content-image{margin-bottom:30px;width:200px}@media(max-width:600px){.nfd-installer-modal__content-image{width:100px}}.nfd-installer-modal__content-subheading{color:#333;font-size:16px;font-weight:300;margin-bottom:30px}@media(max-width:600px){.nfd-installer-modal__content-subheading{font-size:14px;margin-bottom:10px}}.nfd-installer-modal__content-error{align-items:center;display:flex;flex-direction:row;font-size:16px}@media(max-width:600px){.nfd-installer-modal__content-error{font-size:14px}}.nfd-installer-modal__content-error--icon{flex-shrink:0;margin-right:5px}.nfd-installer-modal__content-error--text{font-weight:300;line-height:20px}.nfd-installer-modal__content-error--text-link{all:unset;color:#1e90ff;cursor:pointer;font-weight:300}.nfd-installer-modal__loader{animation:spin 1s linear infinite;border:4px solid #3a3a3a;border-left-color:#f3f3f3;border-radius:50%;height:30px;width:30px}@media(max-width:600px){.nfd-installer-modal__loader{height:20px;width:20px}}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}*{box-sizing:border-box;margin:0;padding:0}body{font-family:Open Sans,sans-serif}#nfd-installer{display:none;transition:all 3s ease-in-out} diff --git a/build/1.2.0/installer.js b/build/1.2.0/installer.js new file mode 100644 index 0000000..4a4950a --- /dev/null +++ b/build/1.2.0/installer.js @@ -0,0 +1,2 @@ +(()=>{"use strict";var M={n:L=>{var I=L&&L.__esModule?()=>L.default:()=>L;return M.d(I,{a:I}),I},d:(L,I)=>{for(var j in I)M.o(I,j)&&!M.o(L,j)&&Object.defineProperty(L,j,{enumerable:!0,get:I[j]})},o:(M,L)=>Object.prototype.hasOwnProperty.call(M,L)};const L=window.React,I=window.wp.domReady;var j=M.n(I);const g=window.wp.element,u="nfd-installer",A=window.nfdInstaller?.restUrl,N=window.nfdInstaller?.pluginInstallHash,D=`${A}/newfold-installer/v1/plugins/install`,y=window.wp.apiFetch;var S=M.n(y);const T=window.wp.i18n,C=({action:M,pluginDownloadUrl:I,pluginName:j,pluginProvider:A,pluginSlug:y,redirectUrl:C})=>{const[i,t]=(0,g.useState)("unknown"),[E,x]=(0,g.useState)(!0),z=(0,g.useRef)(null);(0,g.useEffect)((()=>{document.getElementById(u).style.display=E?"block":"none"}),[E]),(0,g.useEffect)((()=>{switch(M){case"installFreePlugin":U();break;case"installPremiumPlugin":Q()}}),[M]);const w=M=>{"Escape"===M.key&&x(!1)},O=M=>{z.current&&!z.current.contains(M.target)&&x(!1)};(0,g.useEffect)((()=>(document.addEventListener("keydown",w),document.addEventListener("mousedown",O),()=>{document.removeEventListener("keydown",w),document.removeEventListener("mousedown",O)})),[i]);const Y=async()=>{try{"yith"===A?await S()({url:D,method:"POST",headers:{"X-NFD-INSTALLER":N},data:{activate:!0,queue:!1,priority:0,plugin:"woocommerce"}}):"yoast"===A&&await S()({url:D,method:"POST",headers:{"X-NFD-INSTALLER":N},data:{activate:!0,queue:!1,priority:0,plugin:"wordpress-seo"}})}catch(M){throw M}},Q=async()=>{try{t("installing"),await Y(),await S()({url:D,method:"POST",headers:{"X-NFD-INSTALLER":N},data:{activate:!0,queue:!1,priority:0,premium:!0,plugin:y,provider:A}}),t("completed"),x(!1),window.location.href=C}catch(M){t("failed")}},U=async()=>{try{t("installing"),await Y(),await S()({url:D,method:"POST",headers:{"X-NFD-INSTALLER":N},data:{activate:!0,queue:!1,priority:0,plugin:I}}),t("completed"),x(!1),window.location.href=C}catch(M){t("failed")}},c=`${window.NewfoldRuntime.adminUrl}admin.php?page=${window.NewfoldRuntime.plugin.brand}#/help`,e=(0,g.createInterpolateElement)((0,T.__)("Sorry, there was an error installing and activating the plugin. Please try again. If the problem persists, contact support.","wp-module-onboarding"),{a:(0,L.createElement)("a",{href:c,onClick:()=>x(!1)})});return(0,L.createElement)("div",{className:"nfd-installer-modal"},(0,L.createElement)("div",{ref:z,className:"nfd-installer-modal__content"},(0,L.createElement)("div",{className:"nfd-installer-modal__content-heading"},(0,T.__)("Hold on while we get things setup for you!","wp-module-installer")),(0,L.createElement)("div",{className:"nfd-installer-modal__content-section"},(0,L.createElement)("img",{src:"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBmaWxsPSJub25lIiB2aWV3Qm94PSIwIDAgODAwIDgwMCI+CiAgPGRlZnM+CiAgICA8cGF0aCBpZD0iYSIgZmlsbD0iIzAzMjQzYiIgZD0iTTQ5NS41IDM2Mi43Yy0xLjIgMC0yLjMtLjMtMy4zLTFMMzAxLjIgMjQ4YTYuNSA2LjUgMCAwIDEtMi4zLTguOWw4Mi41LTEzOC4zYTYuNSA2LjUgMCAwIDEgOC45LTIuM2wxOTEgMTEzLjhhNi41IDYuNSAwIDAgMSAyLjIgOWwtODIuNCAxMzguM2E2LjUgNi41IDAgMCAxLTUuNiAzLjJ6TTM4Ni45IDEwMC41YTMuNSAzLjUgMCAwIDAtMyAxLjdsLTgyLjQgMTM4LjRhMy41IDMuNSAwIDAgMCAxLjIgNC43bDE5MSAxMTMuOWEzLjUgMy41IDAgMCAwIDIuNi4zIDMuNCAzLjQgMCAwIDAgMi4yLTEuNWw4Mi40LTEzOC4zYTMuNSAzLjUgMCAwIDAtMS4yLTQuOEwzODguNyAxMDFjLS41LS4zLTEuMi0uNS0xLjgtLjV6Ii8+CiAgPC9kZWZzPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Ik00ODYgNjI1Yy01Mi41LTQuNC0xMDUuMS03LjItMTU3LjgtOC4yLTcuNy0uMS0xNy4zIDEtMjAgOC4yLTEuNiA0LjQuMyA5LjQtLjkgMTQtMiA4LTEyIDEwLjktMjAuNCAxMS43LTYyLjMgNi40LTEyOC0xNC4yLTE4Ni40IDguNi01LjYgMi4zLTExLjcgNS42LTEzLjIgMTEuNS0yLjQgOS42IDguOSAxNi43IDE4LjMgMTkuNyA2NC43IDIwLjcgMTM0LjMgMTUuNSAyMDIuMiAxNGExNzc3IDE3NzcgMCAwIDEgMTg1IDUuNGM1LjYuNSAxMi43IDAgMTQuNi01LjIgMi42LTctNy0xMi40LTguNi0xOS41LTEuNC02IDMuMy0xMi4yIDkuMS0xNC41czEyLjItMS42IDE4LjQtMWM0My4yIDQuMiA4Ni43IDcuOCAxMzAgNSAyMS0xLjIgNDMuNC00LjcgNTguNS0xOS4yLTIuOC02LTEwLjYtNy40LTE3LjItOC03NC01LjktMTM5LjQtMTYuNy0yMTMuMy0yMi42em0xMTAuOS0zNzkuOWEuNS41IDAgMCAwIC40LS41LjUuNSAwIDAgMC0uNC0uNC41LjUgMCAwIDAtLjQuNC41LjUgMCAwIDAgLjQuNXoiLz4KICA8cGF0aCBmaWxsPSIjZmZmIiBkPSJNNDc2LjcgMjAxLjRzLTEwLjYgMzAuNy0xNC4yIDM4Yy0xMy4yIDI2LjctMTguNiAzOS41LTQyLjcgNTEuNC0xNS40IDcuNy0xNyA4LjQtMjkuNyAxLjZDMzc5LjkgMjg3IDM3NSAyNzAuOCAzNzMgMjcxYy0uNiAwLS40LjctLjQuOC4yIDEuNSAyLjggOC44LjkgOC40IDAgMC0yLjItMi0zLjgtOC4zLTEtNC0xLjctNC44LTIuNS00LjQtMS4xLjYtLjQgNi45IDAgNy44bC42IDIuN2MtLjIgMC0xLjcuMi0zLjctNS4xLTEuMi0zLjItMi03LjMtMy02LjYtMS40IDEuMSAyLjIgMTAuOCAxIDExLjNzLTQuNC03LjQtNS44LTkuMWMtLjUtLjctMi4zLjgtLjMgNS45IDEuMSAyLjggMi43IDUuNCA0IDggLjIuNSAxLjQgMi41IDEgMy0xLjQgMi4yLTguMi0yLjMtOS4yIDAtLjcgMS41IDUuNSAzLjIgMTUuMiAxMy43YTU4LjIgNTguMiAwIDAgMCAzNC42IDE3LjhjMTguMyAxLjIgNTAuOC05IDgxLjItNjEuOSA3LjgtMTMuNiAxOS43LTQ3LjUtNi4xLTUzLjZ6Ii8+CiAgPHBhdGggZmlsbD0iIzAzMjQzYiIgZD0iTTQwNC4zIDMxOC41aC0yLjhjLTE3LjMtMS4yLTMxLjYtMTQuMy0zNS42LTE4LjMtMy44LTQuMS04LTcuOC0xMi42LTExLTIuMi0xLjUtMy44LTIuNi0zLTQuMyAxLTIuNCA0LTEuNiA2LjQtMWwyLjYuNy0uNi0xLjJ2LS4ybC0xLjQtMi41YTU2IDU2IDAgMCAxLTIuNy01LjdjLTEuOC00LjYtMS03IC40LTcuOWExLjkgMS45IDAgMCAxIDIuNS41Yy44IDEuMiAxLjYgMi41IDIuMiAzLjhsLjIuNGMtLjgtMy42LTEtNS42LjItNi42YTEuOCAxLjggMCAwIDEgMS43LS40YzEuMy40IDEuOSAyIDIuOCA1bC40IDEuM2MwLTIuMS4yLTQuMyAxLjUtNWEyLjIgMi4yIDAgMCAxIDEuOSAwYzEgLjUgMS43IDEuNyAyLjcgNS4yYTEuOSAxLjkgMCAwIDEgMS43LTEuOGMxLjgtLjIgMi43IDEuNiA0LjggNS41IDIuOCA1LjEgNyAxMi44IDEzLjIgMTYuMSAxMiA2LjUgMTMgNiAyOC4zLTEuNiAyMi42LTExLjIgMjguNS0yMy4zIDQwLjMtNDcuNGwxLjctMy40YzMuNS03LjEgMTQtMzcuNSAxNC4yLTM3LjhsLjQtMS4zIDEuMy4zYTIwLjEgMjAuMSAwIDAgMSAxNC44IDExLjdjNiAxMy44LTIuNCAzNS03LjcgNDQuMi0zMS44IDU1LjQtNjQuNiA2Mi43LTc5LjggNjIuN3ptLTQ5LjUtMzIgLjIuMmM0LjggMy4zIDkuMiA3LjEgMTMgMTEuNCAzLjggMy43IDE3LjQgMTYuMiAzMy42IDE3LjMgMTMgLjggNDYuNy0zLjQgNzkuOC02MS4yIDUuOS0xMC4yIDEyLjgtMjkuNyA3LjYtNDEuNS0yLTQuNy01LjgtOC0xMS40LTkuNS0yIDYtMTAuNiAzMC4zLTEzLjggMzYuOGwtMS43IDMuNGMtMTIuMSAyNC43LTE4LjIgMzctNDEuNyA0OC44LTcuNiAzLjctMTIuMiA2LTE2LjQgNi4zcy04LTEuMi0xNC43LTQuOC0xMS0xMS0xMy45LTE2LjRjLjQgMiAuMyAzLS40IDMuOGEyIDIgMCAwIDEtMiAuNWMtMS0uMi0zLjMtMy40LTQuOC05LjMgMCAuOC4xIDEuNi40IDIuNS4zLjcgMS4yIDMuMy4yIDQuM2wtLjcuNy0uOC0uM2MtMS0uMi0yLjItLjgtMy41LTMuNC4xIDEuNSAwIDIuNC0xLjEgMy0yIC44LTMuNC0xLjgtNS42LTZsLjMuOGMuOCAxLjggMS42IDMuNiAyLjYgNS40bDEuMyAyLjV2LjJjMSAxLjYgMS42IDMuMi45IDQuMy0xLjMgMi00IDEuMy02LjQuNnptMTMuNC0xMCAuMS4xeiIvPgogIDxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0yNTAgNjMwLjdjLTcgNi0xMC41IDE0LjktMTcuOSAyMC44LTEuNyAxLjMtMy4zIDIuOC01IDQtLjQuMy0yIC44LTIgMS4zIDAgNCA2LjQgOS4zIDkuMyAxMS4yIDEuMS43IDMuNSAxLjggNC43LjggMi41LTIuMiA0LTUuNSA1LjktOC4yIDIuNi0zLjggNi42LTYuOSAxMC4xLTkuNyAyLjItMS44IDQuNS0zLjMgNi44LTQuOS43LS40IDItMS4xIDIuNC0yIC44LTItLjgtMy4zLTItNC44YTM1IDM1IDAgMCAwLTcuNS02LjRjLTEuNC0uOS0zLjUtMy4zLTQuOC0yLjF6Ii8+CiAgPHBhdGggZmlsbD0iIzAzMjQzYiIgZD0iTTIzNy44IDY3MC43YTggOCAwIDAgMS00LjItMS41Yy0yLjgtMS43LTEwLTcuNC0xMC0xMi40IDAtMS4zIDEuNC0yIDIuMy0yLjNsLjMtLjIgMy0yLjQgMi0xLjZjMy42LTMgNi4zLTYuNiA5LTEwLjUgMi41LTMuOCA1LjUtNy4yIDguOC0xMC4zIDItMS43IDQuMy4yIDUuNyAxLjNsLjkuN2MyLjkgMS45IDUuNSA0LjEgNy44IDYuN2wuNS42YzEuMiAxLjMgMi44IDMgMS44IDUuN2E1IDUgMCAwIDEtMi41IDIuNWwtLjUuMy0xLjQgMWMtMS44IDEuMy0zLjYgMi40LTUuMiAzLjhsLS42LjVjLTMuMyAyLjYtNyA1LjUtOS4zIDguOWE0MyA0MyAwIDAgMC0xLjcgMi42IDIzLjQgMjMuNCAwIDAgMS00LjUgNS45Yy0uNi41LTEuNC43LTIuMi43em0tMTEtMTMuM2MuNSAyLjkgNS4xIDcuMiA4LjQgOS4zIDEuNSAxIDIuNyAxLjEgMi45IDFhMjQgMjQgMCAwIDAgMy44LTUuMmMuNi0xIDEuMS0xLjkgMS44LTIuOGE0OSA0OSAwIDAgMSA5LjgtOS41bC42LS41IDUuNS00IDEuNC0xIC42LS4zIDEuMy0xYy4zLS44IDAtMS4zLTEuMi0yLjZsLS42LS43YTMzIDMzIDAgMCAwLTguMy03Yy0uNi0uNS0xLjItMS0yLTEuMy0zIDMtNS43IDYuMi04IDkuNy0yLjkgMy45LTUuNyA3LjktOS44IDExLjFsLTEuOCAxLjVhNTAgNTAgMCAwIDEtNCAzeiIvPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Ik0yNDQgNjYxLjNjMy44LjMgMi40IDMuOSAxLjIgNi4yLTIgMy45LTYuOCAxNC0xMiAxMy45LTEzLS4zLTQwLTI2LjItNDIuNi0zMC41LTIuOC00LjctNy43LTEzLjEtNC4zLTE4LjcgMS41LTIuNSAzLjUtLjkgNS4zLjMgNCAzIDYuOCA2LjUgMTEuMSA5czEwLjUgNS4xIDE1LjcgNS42YzQgLjQgOC4zLTEgMTIgLjkgMiAxIC4yIDMuNS0uMSA0LjlhOCA4IDAgMCAwIDEgNmMzLjcgNS4zIDggMiAxMi43IDIuNHoiLz4KICA8cGF0aCBmaWxsPSIjMDMyNDNiIiBkPSJNMjMzLjIgNjgyLjljLTEzLjctLjMtNDEuMy0yNi44LTQ0LTMxLjNTMTgxIDYzOCAxODUgNjMxLjRhMy45IDMuOSAwIDAgMSAyLjUtMmMxLjgtLjMgMy41LjkgNC44IDEuOGwuMS4xIDQuNyA0YTM3LjcgMzcuNyAwIDAgMCAyMS4zIDEwLjNoNGMzLS4yIDUuOS0uNCA4LjcgMXMxLjUgNC4zIDEgNS43bC0uNCAxYTYgNiAwIDAgMCAuOSA0LjdjMiAyLjkgNCAyLjYgNyAyIDEuNS0uMiAzLS40IDQuNS0uM2E0IDQgMCAwIDEgMy40IDJjMS4xIDIgMCA0LjctMSA2LjVsLS40LjljLTIuNSA0LjgtNy4xIDEzLjgtMTIuOSAxMy44em0tNDUtNTAuNWMtLjIgMC0uNC4yLS42LjYtMyA1IDIgMTMuNCA0LjIgMTcgMi41IDQgMjkuMyAyOS42IDQxLjQgMjkuOSA0IDAgOC41LTguOSAxMC4yLTEyLjJsLjUtLjljMS40LTIuNyAxLTMuNSAxLTMuN3MtLjUtLjMtMS0uNGMtMS4zIDAtMi42LjEtMy44LjQtMy4xLjUtNi45IDEuMS0xMC0zLjNhOS41IDkuNSAwIDAgMS0xLjMtNy4zbC40LTEuM2MuMy0uNi41LTEuMi42LTItMi4xLTEtNC42LS44LTcuMS0uNmgtNC41YTQxLjggNDEuOCAwIDAgMS0yMy4yLTExLjEgNTEuMSA1MS4xIDAgMCAwLTQuNC0zLjdsLS4xLS4xYy0uNy0uNi0xLjUtMS0yLjMtMS4zeiIvPgogIDxwYXRoIGZpbGw9IiM2NmE4ZjciIGQ9Ik0yMzcuMiA2NzZjLS40IDAtMS4xLS4zLTIuOS0xLjNsLTIuNy0xLjQtMi4yLS44YTkzLjYgOTMuNiAwIDAgMS0zMi0xOWMtMi43LTIuNC05LjMtOC43LTkuNy0xNi45YS44LjggMCAwIDEgMS0uN2MuMSAwIC4yIDAgLjMuMmEuOC44IDAgMCAxIC4yLjVjLjMgNy41IDYuNiAxMy41IDkuMyAxNS43YTkyLjUgOTIuNSAwIDAgMCAzMS41IDE4LjhsMi4xLjggMyAxLjUgMS44IDFhLjcuNyAwIDAgMSAuNi4yLjguOCAwIDAgMSAuMiAxLjFjLS4yLjItLjMuNC0uNS40eiIvPgogIDxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik01NDUuNSA2MDkuOWMtLjYgMS45IDcuOCAxMSA4LjYgMTUuNSAxLjEgNS45IDQgMTEgNS43IDExLjQgMS40LjQgNS4xLTUuOSA1LjktNi44czIuNC0xLjggMi43LTIuOWMuNy0yLjQtMS45LTYuMi0zLjMtOC4zLTMtNC4xLTUtMTIuNC03LjMtMTQuMS0yLjYtMi0xMS41IDIuOC0xMi4zIDUuMnoiLz4KICA8cGF0aCBmaWxsPSIjMDMyNDNiIiBkPSJNNTYwIDYzOC4zaC0uNmMtMi45LS44LTUuOC03LjEtNi44LTEyLjYtLjMtMi0yLjgtNS43LTQuOC04LjYtMy00LjItNC4yLTYuMS0zLjctNy43LjgtMi40IDUuNi01LjEgOC44LTYuMSAyLjYtLjkgNC41LS44IDUuOC4xIDEuNSAxLjIgMi42IDMuOSA0IDcuMyAxIDIuNiAyLjIgNSAzLjYgNy4zIDIuMSAyLjkgNC40IDYuNiAzLjYgOS41LS41IDEtMS4yIDItMi4xIDIuNmwtMSAuOGMwIC4yLS4zLjYtLjcgMS4yLTIuOCA0LjItNC41IDYuMi02LjIgNi4yem0tMTMtMjhjMSAxLjggMiAzLjUgMy4yIDUuMSAyLjQgMy41IDQuOSA3IDUuNCA5LjggMSA1LjMgMy4yIDkgNC4zIDEwYTMxLjggMzEuOCAwIDAgMCA0LjYtNi4yYy40LS40LjgtLjkgMS4zLTEuMmwxLjEtMS4xYy4yLS45IDAtMi44LTMtN2E0MS44IDQxLjggMCAwIDEtNC03LjhjLTEtMi40LTIuMi01LjQtMy02LS4zLS4yLTEuMS0uMy0yLjkuMmExNyAxNyAwIDAgMC03IDQuMXoiLz4KICA8cGF0aCBmaWxsPSIjMDMyNDNiIiBkPSJNNTY1LjEgNjE4LjljLTEtMS4zIDQuMS0xLjYgNy41IDIuNXM0LjUgOC42IDIuNiAxMy4zLTIwIDI1LTIwIDI1LTQuMyAxLjItMTMuOCAxYy03LS4xLTEyLjQtLjctMTIuNC0uN3MtMS40LTMuNy0uNC00LjRjMy43LTMgNi43LTIuMyAxNS4zLTguNiAyLTEuNCA1LjYtOS4zIDEwLjMtMjEuNiAxLjQtMy44IDQgNyA1LjMgNi44IDQuNS0xIDYuNS0xMi4zIDUuNi0xMy40eiIvPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Ik01NDMuMyA2NjIuMmgtMmMtNy0uMS0xMi40LS43LTEyLjUtLjdoLTFsLS4zLTFjLS44LTIuMS0xLjQtNC45LjEtNiAxLjYtMS4zIDMuMy0yLjIgNS4yLTMgMy42LTEuNCA3LTMuMyAxMC4yLTUuNy43LS41IDMuMi0zLjYgOS43LTIxIC42LTEuNCAxLjUtMS43IDIuMS0xLjcgMS45IDAgMi44IDIuMiA0IDUuMmwxIDJjLjYtLjQgMS0xIDEuNC0xLjVhMTkgMTkgMCAwIDAgMi41LTkuMiAxLjkgMS45IDAgMCAxLS4yLTEuNyAyLjIgMi4yIDAgMCAxIDEuOS0xLjNjMS43LS4zIDUuNS41IDguMyAzLjggMy45IDQuOCA0LjkgMTAgMi44IDE0LjlzLTE4LjQgMjMuMy0yMC4zIDI1LjNsLS4yLjQtLjQuMWMtLjIgMC00IDEuMS0xMi4zIDEuMXptLTEzLjItMy42YzEuOS4yIDYuMi41IDExLjMuNiA0LjQuMiA4LjctLjEgMTMtMSA1LjEtNS43IDE3LjgtMjAuNCAxOS40LTI0IDEuNi00IC44LTcuOS0yLjQtMTEuOGE3LjcgNy43IDAgMCAwLTQuNi0yLjhjLjIgMi43LTEuMyA3LTEuMyA3LTEgMi45LTIuNyA2LjQtNS43IDctMS43LjUtMi41LTEuMy0zLjctNGwtMS0yLjRjLTYuNCAxNi43LTguOCAyMC0xMC4zIDIxLjEtMy4zIDIuNS03IDQuNS0xMC44IDZhMTggMTggMCAwIDAtNC4yIDIuM2MwIC43IDAgMS4zLjMgMnoiLz4KICA8cGF0aCBmaWxsPSIjNjZhOGY3IiBkPSJNNTQ1LjMgNjUxLjdjLS40IDAtLjctLjEtMS0uMy0xLjYtLjUtMy0xLjMtNC4zLTIuNCAwIDAtMS0xLjEtMS0yYTEgMSAwIDAgMSAuNC0uN2MuNy0uNSAxLjctLjIgMi40IDBsLjQuMiAxLjYuNWMxLjMuNiAyLjYgMSA0IDEtLjIgMC0uNC0uMy0uNS0uNGwtLjQtLjItMS4zLTEtMi4zLTEuOWMtLjItLjItMS42LTEuNS0uOC0yLjUuNy0uNyAxLjktLjQgMy42LjRsLjUuMmMzLjYgMS4zIDQuOCAxLjMgNS4yIDEuMWwtLjctLjhhMjguNCAyOC40IDAgMCAwLTMuNy0zLjFsLS4yLS4yYy0xLjEtLjYtMS44LTEtMS44LTEuN2ExIDEgMCAwIDEgLjUtLjdjLjUtLjMgMSAwIDEuMy4zbC4yLjEuNS4zIDEgLjRjMS43LjcgMy41IDEgNS4zIDFhLjguOCAwIDAgMSAuNSAxLjMuNy43IDAgMCAxLS41LjMgMTYgMTYgMCAwIDEtMy40LS40bDEuMyAxLjNjLjQuNSAxLjggMS44IDEgMi44LTEgMS4zLTQgLjUtNy0uNmwtLjMtLjJoLS4zYTEyIDEyIDAgMCAwLTEuMy0uNWwyLjMgMS44IDEuNCAxLjEuMy4yYy43LjYgMS41IDEuMiAxLjUgMmExIDEgMCAwIDEtLjYuOWMtMS40LjktNCAwLTUuOS0uOGwtMS40LS41LS41LS4xLS40LS4yLjIuMmMxIDEgMi4zIDEuNiAzLjcgMmwuMS4xaC4xbC40LS4xYS44LjggMCAwIDEgLjguOCAxIDEgMCAwIDEtLjUuOCAxIDEgMCAwIDEtLjQuMnoiLz4KICA8cGF0aCBmaWxsPSIjNjZhOGY3IiBkPSJNNTMzLjYgNjU5LjNhLjguOCAwIDAgMS0uNy0uNi44LjggMCAwIDEgLjUtLjkgMzYgMzYgMCAwIDEgOC0xIDQzLjIgNDMuMiAwIDAgMCAxMS4xLTEuNWMxLjMtMSAyLjQtMi4yIDMuNC0zLjZsMS4zLTEuNmMyLjEtMi40IDQuMi01IDYuMi03LjZhMTIxLjEgMTIxLjEgMCAwIDEgOS40LTEwLjYuOC44IDAgMCAxIC41LjIuOC44IDAgMCAxIC4yLjhsLS4yLjNjLTMgMy4yLTYgNi42LTguNyAxMC4yLTIgMi42LTQuMSA1LjMtNi4zIDcuN2wtMS4zIDEuNmExOSAxOSAwIDAgMS0zLjYgMy44IDkgOSAwIDAgMS0zLjggMWwtMS40LjJhNDMgNDMgMCAwIDEtNi43LjcgMzQuOCAzNC44IDAgMCAwLTcuOSAxeiIvPgogIDxnIGZpbGw9IiMwMzI0M2IiPgogICAgPHBhdGggZD0iTTYyNSAyNjEuMmMtMSA2LjItMTguMyAzLjMtMTguOC0uOS0uMi0xLjQuNC05LjIgMy44LTkuMiA2IDAgMTUgMTAuMSAxNSAxMC4xeiIvPgogICAgPHBhdGggZD0iTTYxNy43IDI2Ni4zaC0uMmMtNC45IDAtMTIuMy0yLTEyLjgtNS44LS4yLTEuNC4xLTcgMi4zLTkuNWEzLjggMy44IDAgMCAxIDIuOS0xLjRjNi41IDAgMTUuMiA5LjUgMTYuMSAxMC42bC41LjV2LjdjLS41IDMuMS0zLjcgNC45LTguOCA0Ljl6bS03LjgtMTMuN3MtLjMgMC0uNi40Yy0xLjMgMS41LTEuOCA2LTEuNiA3LjIgMCAuOSA0LjMgMyA5LjggMyAyLjQgMCA1LS4zIDUuNy0xLjYtMi43LTMtOS4yLTktMTMuMy05ek01MzIgMzg1LjVjLTMxLjcgMjAuNy04OS40LTQuOS05OC40LTE5czIwLjMtNzggNTEuNC01Ny41IDYyLjYgNjYuMyA0NyA3Ni41eiIvPgogICAgPHBhdGggZD0iTTUwMi4zIDM5NC44Yy03IDAtMTQtLjktMjEtMi40LTIzLjctNS4xLTQ0LTE3LTQ5LTI1LTYuMy05LjggNC4xLTM5IDE5LjEtNTMuOCAxMS4yLTExIDIzLjQtMTMgMzQuNC01LjkgMjYuMiAxNy4yIDUxLjggNTEuNCA1MS44IDY5IDAgNC42LTEuNiA4LTQuOCAxMGE1NSA1NSAwIDAgMS0zMC41IDguMXptLTI5LjgtODguM2MtNy42IDAtMTQuMSA0LjUtMTkgOS4zLTE1IDE0LjctMjMuNyA0Mi0xOC42IDUwIDQuOCA3LjUgMjQuOSAxOC44IDQ3LjEgMjMuNiAyNS41IDUuNiA0MC44LjMgNDkuMS01LjIgMi4zLTEuNSAzLjUtNCAzLjUtNy41IDAtMTYuNS0yNS41LTUwLTUwLjUtNjYuNWEyMC45IDIwLjkgMCAwIDAtMTEuNi0zLjd6Ii8+CiAgICA8cGF0aCBkPSJNNDE5LjQgMzE4LjJjLTYuNCAyLTUuOSAxMS03IDE2LjUtLjUgMi41LS44IDUtLjkgNy42LS41IDEzLjQgNC42IDI1LjggNy4zIDM5IDUuNCAyNy4yIDE2LjkgNTMuNiAyNy42IDc5LjNhNzIzIDcyMyAwIDAgMCA5MS44IDE1Ny41YzE1LjMtMS45IDIxLjEtNC45IDI1LjctMTAuNy40LS41LTIxLjMtNTkuOC0yOC41LTgwLTYuNC0xNy44LTUuOC0xNC4zLTExLjEtMzIuNS0xMi4yLTQxLjMtMjMuNy04Mi44LTM1LjUtMTI0LjEtMy41LTEyLjEtNi40LTI0LjItMTEtMzUuOC0xLjYtMy45LTMuMS0zNi41LTYuOC0zOS4zLTEwLjUtNy45LTI2LjEgMTEuNi0zNy44IDguNy00LjQtMS4xLTkuNiAxMi40LTEzLjggMTMuOHoiLz4KICAgIDxwYXRoIGQ9Im01MzcuNiA2MTkuNy0uNS0uN0E3MzUgNzM1IDAgMCAxIDQ0NSA0NjEuMmwtMi4zLTUuNWMtMTAtMjQtMjAuMy00OC43LTI1LjQtNzQuMi0uOC00LTEuOS04LjEtMy0xMmE5NCA5NCAwIDAgMS00LjMtMjcuM2MwLTIuNi40LTUuMi45LTcuOGwuNi0zLjdjLjYtNS40IDEuNC0xMiA3LjQtMTQgMS40LS40IDMuNS0zLjUgNS4yLTYgMy4xLTQuNSA2LTguNyA5LjUtNy44IDQuNyAxLjIgMTAuNi0yIDE2LjMtNSA3LjYtNCAxNS41LTguMyAyMi0zLjQgMi41IDEuOCAzLjcgMTAgNS42IDI3LjMuNiA1LjcgMS4yIDExLjUgMS43IDEyLjcgMy43IDkuMiA2LjQgMTguOCA5IDI4LjJsMi4xIDcuNyAxMS43IDQxLjFjNy43IDI3LjIgMTUuNyA1NS40IDIzLjggODMgMy42IDEyLjMgNC41IDE0LjcgNi43IDIwLjRsNC40IDEyLjEgOC43IDI0LjMgMTMuNCAzN2M2LjggMTkuMiA2LjggMTkuMiA2LjEgMjAuMS01LjEgNi42LTExLjggOS40LTI2LjYgMTEuMnptLTEwNS0zMTMuOWMtMS41IDAtNC4yIDQtNiA2LjYtMi4zIDMuMy00LjUgNi40LTYuOCA3LjItNC4xIDEuMy00LjggNi41LTUuNCAxMS40LS4yIDEuNS0uMyAyLjgtLjYgNC0uNCAyLjQtLjcgNC45LS44IDcuMy0uMyA5IDEuOSAxNy41IDQuMyAyNi40bDMgMTIuM2M1IDI1IDE1LjMgNDkuNyAyNS4yIDczLjVsMi4zIDUuNWE3MzMuNyA3MzMuNyAwIDAgMCA5MS4xIDE1Ni41YzE0LjQtMS45IDE5LjUtNC44IDIzLjQtOS41LTEuNC00LjYtMTEuOC0zMy40LTE5LjYtNTQuN2E3ODMxLjYgNzgzMS42IDAgMCAxLTEzLjEtMzYuNGMtMi4yLTUuOC0zLjEtOC4yLTYuOC0yMC42LTguMS0yNy42LTE2LTU1LjgtMjMuOC04M2wtMTEuNi00MS0yLjItNy44Yy0yLjYtOS4yLTUuMi0xOC45LTguOC0yNy44LS43LTEuNi0xLjEtNi0yLTEzLjUtLjktOC4zLTIuNS0yMy43LTQuNC0yNS4zLTUtMy43LTExLjYtLjEtMTguNyAzLjctNi4yIDMuMy0xMi42IDYuNy0xOC41IDUuM3oiLz4KICA8L2c+CiAgPHBhdGggZmlsbD0iI2ZmZiIgZD0iTTU0OSA1OTUuOWEuNi42IDAgMCAxLS41LS40TDUzNSA1NjljLTI4LjYtNTYuNC01My40LTEwNS4xLTY0LjctMTczLjF2LS4zYS42LjYgMCAwIDEgLjQtLjQuNi42IDAgMCAxIC43LjVjMTEuNCA2Ny44IDM2IDExNi40IDY0LjcgMTcyLjdhMzM0NC44IDMzNDQuOCAwIDAgMSAxMy41IDI3IC42LjYgMCAwIDEtLjMuNGgtLjN6TTQxMiAzNTIuNGEuNi42IDAgMCAxLS40LS4yLjYuNiAwIDAgMSAwLS45YzEuMS0xIDEuOC0zIDIuMy00LjdhNDUuMyA0NS4zIDAgMCAxIDEuOC00LjZjMS4zLTIuOSAyLjMtNS45IDMtOWwuNi0zIC42LTMuMS4yLTEuNGMuMy0yIC40LTIuMSAxLTJhLjYuNiAwIDAgMSAuNS42di4ybC0uMyAxLjQtLjMgMS41LS41IDMuMS0uNiAzYTUzIDUzIDAgMCAxLTQuNCAxMi41bC0uNCAxLjJjLS42IDEuOC0xLjMgMy45LTIuNyA1LjJhLjYuNiAwIDAgMS0uNC4yeiIvPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Ik00ODIuMyAyOTguNmE2NTEgNjUxIDAgMCAwLTI3LjMgMzNjLTUgNi41LTkuOSAxMy4xLTE1LjIgMTkuNi0xNi4yIDE5LjktMzIgNDAuMy00OC44IDU5LjYtMTUuNiAxOC0yOSAzNy40LTQzLjUgNTYuMy0xNiAyMC44LTkxLjYgMTMzLjktOTkuOSAxNDcuNy0xLjIgMi03LjYgMTIuOS03IDE1IC41IDIuNCA0LjIgNC4yIDUuOCA1LjRsMjMuOCAxNy43YzMuNiAyLjcgNDEuNi0zNy45IDUwLjgtNDcuOSAxNS4yLTE2LjQgMjkuNS0zMy40IDQ0LjItNTAuM0MzOTAgNTI2IDQzNS43IDQ3MCA0NzAuOSA0MjUuMyA0OTUuNiAzOTQgNTE1IDM2OCA1MTguNiAzNjFjMi00LjEgMTQuOC0yNC4yIDEyLjQtMjkuMS0yLjYtNS4yLTcuNy0xMy43LTEyLjUtMTYtMTQtNy0yMC43LTE1LjYtMzYuMi0xNy4zeiIvPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Ik0yNzAuNiA2NTQuNmEyIDIgMCAwIDEtMS4zLS41IDE3NDUuOCAxNzQ1LjggMCAwIDAtMjUtMTguNWMtMi0xLjQtNC42LTMtNS4yLTUuNC0uMi0uNy0uNy0yLjggNy4yLTE2LjEgNy45LTEzLjEgODQtMTI3LjEgMTAwLTE0OEE3MTggNzE4IDAgMCAwIDM2MCA0NDhhNjQ1LjEgNjQ1LjEgMCAwIDEgMjkuOS0zOC4xYzExLjItMTMgMjIuMi0yNi43IDMyLjgtNDBsMTYtMTkuN2MzLjQtNC4xIDYuNy04LjUgOS45LTEyLjdhODQzIDg0MyAwIDAgMSAxOS4zLTI0LjRjMTMtMTYuMiAxMy41LTE2LjEgMTQuNi0xNmE1My4yIDUzLjIgMCAwIDEgMjQuNyAxMC4zYzMuOSAyLjYgNy45IDUgMTIgNyA1LjkgMyAxMS41IDEzLjYgMTMuMSAxNi44IDIgNC0yLjQgMTMuMS0xMC4yIDI2LjZsLTIuMSAzLjljLTQuNCA4LjctMzIgNDQuMi00OCA2NC42YTQyNDQgNDI0NCAwIDAgMS0xMTUuOCAxNDEuMWMtMTEgMTIuOC0yMi4zIDI2LTM0IDM4LjZhMTA5NSAxMDk1IDAgMCAxLTI3LjYgMjguOGMtMTYuOSAxNi44LTIxLjkgMTkuOC0yNCAxOS44em0yMTIuMS0zNTQuNGMtMy4yIDMtMjIuNiAyNy40LTI2LjQgMzIuM2wtNS4zIDYuOWMtMy4yIDQuMi02LjUgOC42LTEwIDEyLjhsLTE2IDE5LjdjLTEwLjYgMTMuMi0yMS42IDI3LTMyLjkgNDBhNjI3LjMgNjI3LjMgMCAwIDAtMjkuNyAzOGMtNC41IDYtOSAxMi4xLTEzLjcgMTguMS0xNiAyMC44LTkyIDEzNC42LTk5LjggMTQ3LjctNC42IDcuNy03IDEyLjgtNyAxMy45LjQgMS4yIDIuNCAyLjUgNCAzLjVsMS40IDEgMTIgOC44IDExLjUgOC42YzMtLjcgMTcuNi0xMy41IDQ5LTQ3LjQgMTEuOC0xMi42IDIzLTI1LjggMzQtMzguNmwxMC4yLTExLjdjMjQuNS0yOC4yIDY5LjktODMuOCAxMDUuNi0xMjkuMyAyNy42LTM1IDQ0LjUtNTcuOCA0Ny42LTY0YTk4IDk4IDAgMCAxIDIuMy00YzIuOS01LjEgMTEuNy0yMC42IDEwLTIzLjgtMi44LTUuOS03LjctMTMuNC0xMS43LTE1LjRhMTIzIDEyMyAwIDAgMS0xMi4zLTcuM2MtNy00LjYtMTMuMS04LjYtMjIuOC05Ljh6Ii8+CiAgPHBhdGggZmlsbD0iI2ZmZiIgZD0ibTQxOC42IDM3OC4xLS40LS4xYS42LjYgMCAwIDEtLjItLjQuNi42IDAgMCAxIC4xLS41bDUuNC02LjZhMjk5MyAyOTkzIDAgMCAxIDE1LjktMTkuN2w5LjktMTIuNyA1LjMtNi45YS42LjYgMCAwIDEgLjgtLjEuNi42IDAgMCAxIC4yLjZ2LjJhMzg0IDM4NCAwIDAgMS0xNS4zIDE5LjZsLTE2IDE5LjctNS4zIDYuNy0uMi4yaC0uMnpNMjY2LjUgNjQzLjdzLS4yIDAtLjMtLjJhLjYuNiAwIDAgMS0uMS0uOCA4NzQuOCA4NzQuOCAwIDAgMSA0NC4yLTU1LjNBMTE0Ni44IDExNDYuOCAwIDAgMCAzODkuNCA0ODRjOS4zLTEzIDE5LTI2LjUgMjguNi0zOS42bDMuNi01YzEyLjYtMTcgMjUuNi0zNC44IDM4LjYtNTIgMS43LTIuMiAxNC0xNi4xIDE0LjUtMTYuN2EuNi42IDAgMCAxIDEgLjR2LjRjLS4yLjItMTIuOSAxNC41LTE0LjUgMTYuNy0xMyAxNy4yLTI2IDM0LjktMzguNiA1Mkw0MTkgNDQ1Yy05LjYgMTMuMS0xOS4zIDI2LjYtMjguNSAzOS42LTE0LjYgMjAuNC0yOS42IDQxLjUtNDUuMSA2MS44YTExNTEgMTE1MSAwIDAgMS0zNC4yIDQxLjYgODcxIDg3MSAwIDAgMC00NC40IDU1LjVoLS4zem0yMjguMy0yNjguNmMtMSAwLTItMS45LTQtNy44bC0uMi0xYTkyLjIgOTIuMiAwIDAgMS01LTI3Yy0uMy0yLjUtLjQtNS0uNS03LjV2LTYuMmEuNi42IDAgMCAxIC4yLS41LjYuNiAwIDAgMSAuNy0uMi42LjYgMCAwIDEgLjMuN3Y2LjJhNjcgNjcgMCAwIDAgMSAxMWMuOCA4IDEuNiAxNS42IDQuNCAyMy4ybC4zIDFjLjYgMiAxLjQgNCAyLjIgNS45LS4xLTIuNS0uNS00LjktMS4yLTcuMmwtMS01LjNjLTEuMi0xMC0xLjItMTkuMy0xLjItMjkuMnYtLjZhLjYuNiAwIDEgMSAxLjIgMHYuNmMwIDkuOSAwIDE5LjIgMS4yIDI5bDEgNS4yYTM2IDM2IDAgMCAxIDEuMiA5LjUuNy43IDAgMCAxLS42LjJ6Ii8+CiAgPHBhdGggZmlsbD0iIzY2YThmNyIgZD0iTTIxOS41IDY1My4zaC0uMmMtMS0uMS0xLjItMS41LTEuMi0yLjR2LTIuMmwtMS42IDJjLS40LjUtMS4xIDEuNS0yIDEtMS0uNy0uOS0yLjctLjEtNmwtLjUuNy0xLjcgMi42Yy0uNS42LTEgMS4zLTEuNyAxLjRhMS4xIDEuMSAwIDAgMS0uOS0uM2MtMS4zLTEuMy0uNS0zLjYuMy01LjdsLjYtMS44Yy0uNy43LTEuMyAxLjUtMS44IDIuNGwtLjUgM3YuMmEuNy43IDAgMCAxLS44LjYuOC44IDAgMCAxLS43LS44di0uMmMwLTEuMi4zLTIuNC43LTMuNS41LTEgMS4zLTEuOSAyLTIuN2wuNi0uNmExLjcgMS43IDAgMCAxIDEuNS0uNSAxIDEgMCAwIDEgLjcuN2MwIDEuMy0uMyAyLjYtLjkgMy44LS41IDEuMy0xIDMtLjggMy43bC41LS42YTQzIDQzIDAgMCAwIDIuOC00LjNsLjEtLjJjLjYtMSAxLTEuNyAxLjYtMS43YS44LjggMCAwIDEgLjYuM2MuMi4yLjQuNCAwIDIuN2wtLjIgMWExOSAxOSAwIDAgMC0uNiA0bC4xLS4yYzEuMy0xLjUgMi40LTMgMy4yLTQuOGwuMS0uMWMwLS44LjItMS4zLjctMS42YTEgMSAwIDAgMSAxIC4zYy41LjQuMy45LS4yIDEuN3YuOGwtLjEuM2MtLjQgMS40LS41IDIuOS0uNCA0LjN2MS4yYy43LS41IDEuMy0yLjMgMS42LTMuNC4yLS45LjYtMS43IDEtMi41YS44LjggMCAwIDEgMS4yIDFjLS40LjYtLjYgMS4yLS44IDEuOS0uNiAyLTEuNCA0LjUtMy4yIDQuNXptMjQ3LTQ1MS41YzEgNC4xIDcuNiA3LjUgMTEuMyAxMCAxIC42IDQuNSAyLjggNC41IDQuMiAwIDIuMS0xMyAxOC0xOCAyM2EzODEuNSAzODEuNSAwIDAgMC00MS41IDUxLjRjLTE5IDI2LjYtMTMuMiA0OS4yLTExLjQgNTEuMiAwIDAgLjItMi42IDIuOC03LjIgOS41LTE1LjMgNTAuNy0xOCA4MC0xLjdzNDQuOCA0NC44IDM3LjggNTIuOGMwIDAgMTcuNC0xMC4zIDMzLjMtNTYuNyAxNy4xLTUwIDQ1LjUtNjguNyA0NS41LTY4LjdzNC4yLTEyLjEgMTQuMiAxLjFjMi41LTE3LjMgMi45LTUxLjMtMTYuOC03M3MtNDIuNi0xMC43LTYzLjgtMTAuN2MtNiAwLTguNC00LTEyLTguMy0zLjQtNC0xMC41LTguNS0xNS43LTkuNS0xNi41LTMtMzUuNyAxNy00MyAzMC40LTIgMy41LTguMiA3LjctNy4yIDExLjd6Ii8+CiAgPHBhdGggZmlsbD0iIzAzMjQzYiIgZD0iTTUzMiAzODdhMS41IDEuNSAwIDAgMS0xLjEtMi41YzEuMi0xLjQgMS42LTMuOCAxLTYuOC0yLTExLjktMTYuMS0zMS4yLTM4LjQtNDMuNy0yOS0xNi4yLTY5LjItMTMtNzggMS4yLTEuMiAyLTIuMSA0LjItMi42IDYuNWExLjUgMS41IDAgMCAxLTIuNi45Yy0yLjUtMi44LTcuOC0yNi4zIDExLjMtNTNhMzY0IDM2NCAwIDAgMSA0MS43LTUxLjdjNS01IDE2LjQtMTkuMSAxNy41LTIxLjktLjMtLjMtMS0xLjItMy42LTIuOWwtLjMtLjEtMi0xLjRjLTQtMi40LTguOS01LjQtOS44LTkuNC0uOS0zLjUgMi02LjYgNC40LTkuNCAxLTEgMi0yLjIgMi44LTMuNSA4LTE0LjQgMjcuOC0zNC4yIDQ0LjctMzEgNiAxIDEzLjIgNiAxNi42IDEwbDEuNSAxLjdjMi44IDMuNSA0LjggNiA5LjMgNmExMjkgMTI5IDAgMCAwIDE2LTEuNGMxNi40LTIgMzMuNS00LjIgNDguOSAxMi42YTczLjEgNzMuMSAwIDAgMSAxNyAzOSAxMjQgMTI0IDAgMCAxIC4yIDM1LjMgMS41IDEuNSAwIDAgMS0yIDEuMmMtLjMtLjEtLjYtLjMtLjctLjUtMy00LTUuNy01LjktNy44LTUuNnMtMy41IDMtMy45IDRjMCAuMy0uMy42LS41LjgtLjMuMi0yOC4yIDE5LjEtNDQuOSA2OC04LjMgMjQtMTcgMzguNC0yMi44IDQ2LjEtNi41IDguNS0xMSAxMS4yLTExLjIgMTEuNGwtLjcuMXptLTgxLjYtNjdjMTUuNiAwIDMxIDQgNDQuNiAxMS40IDIzIDEzIDM3LjggMzMuMiA0MCA0NS43bC4yIDMuN2M2LTUuOCAxNy43LTIwLjUgMjguNy01Mi41IDE2LjItNDcuMyA0Mi02Ni43IDQ1LjYtNjkuMi42LTEuNCAyLjUtNSA2LTUuNSAyLjctLjQgNS41IDEgOC40IDQgNC44LTQwLjgtMTEuNi02Mi42LTE2LjktNjguNC0xNC4yLTE1LjctMjkuOC0xMy43LTQ2LjMtMTEuNi01LjQuOC0xMC44IDEuMi0xNi4zIDEuNC01LjkgMC04LjctMy40LTExLjctN2wtMS40LTEuOGEzMiAzMiAwIDAgMC0xNC44LTljLTguNy0xLjYtMTcuNyAzLjktMjMuOCA4LjdhNzYuOSA3Ni45IDAgMCAwLTE3LjcgMjAuOWMtMSAxLjUtMiAyLjgtMy4yIDQtMiAyLjMtNC4yIDQuOC0zLjggNi42LjcgMi45IDUuMiA1LjYgOC41IDcuN2wyLjIgMS40LjIuMWMzIDIgNSAzLjcgNSA1LjQgMCAzLjMtMTUuMyAyMS0xOC40IDI0YTM2NiAzNjYgMCAwIDAtNDEuNSA1MS4zYy0xNC41IDIwLjQtMTQuMSAzOC0xMi43IDQ1LjUuNC0xIDEtMiAxLjYtMy4xIDUuMy04LjYgMjAuMS0xMy42IDM3LjQtMTMuNnoiLz4KICA8cGF0aCBmaWxsPSIjZmZmIiBkPSJNNTM2LjYgMTcwYy0yIDItMy44IDQtNS41IDYuMy00LjUgNi4xLTYuNiAxNy4zIDMuOCAxOSA2LjUgMSAxNS4zLTQuMiAxOS40LTguOSAyLjgtMyA0LjctNi45IDUuNS0xMSAuMy0xLjUgMy42LTcuNiAyLjItOC45LTQuMS0zLjctMTQuNCAxLTE5LjQgMS41LTIgLjItNC41LjQtNiAyeiIvPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Ik01MzYuOCAxOTdjLS43IDAtMS40IDAtMi4xLS4yLTUtLjgtNy4xLTMuNi04LTUuNy0yLTUgLjItMTEuNiAzLjItMTUuNyAxLjctMi4zIDMuNS00LjQgNS42LTYuNSAxLjgtMS45IDQuNi0yLjEgNi43LTIuNGguMmEzMCAzMCAwIDAgMCA1LjItMS4xYzUuNS0xLjUgMTEuOC0zLjEgMTUuNCAwIDEuNiAxLjUuNiA0LjUtMSA4LjVsLS43IDJjLTEgNC4zLTMgOC4zLTUuOSAxMS42LTMuNSA0LTExLjUgOS41LTE4LjYgOS41em0yMC42LTMwLjRjLTMuMS4yLTYuMS44LTkgMS43YTQwIDQwIDAgMCAxLTUuNyAxLjJoLS4yYy0xLjkuMi0zLjguNC00LjkgMS41LTIgMi0zLjcgNC01LjMgNi4xLTIuNiAzLjYtNC40IDktMi44IDEyLjguOCAyLjEgMi43IDMuNCA1LjYgNCA2LjIgMSAxNC40LTQuMyAxOC04LjRhMjIgMjIgMCAwIDAgNS4yLTEwLjRsLjgtMi40Yy42LTEuMyAxLjgtNC4yIDEuNy01LjItMS0uNy0yLjItMS0zLjQtMXoiLz4KICA8cGF0aCBmaWxsPSIjZGQ3YTY0IiBkPSJNNTQwLjUgMTgyLjFjLTEuNSAwLTMtLjItNC41LS44YTguMyA4LjMgMCAwIDEtMy41LTR2LS40YS42LjYgMCAwIDEgMS0uMSA3LjIgNy4yIDAgMCAwIDMgMy40YzIuOSAxLjMgOS43IDEgMTgtMy4yYS42LjYgMCAwIDEgLjYuMS42LjYgMCAwIDEgLjMuNC42LjYgMCAwIDEtLjQuNmMtNS44IDIuOS0xMC45IDQtMTQuNSA0eiIvPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Ik00MjIuOCAyOTJhMS41IDEuNSAwIDAgMS0xLjQtLjkgMS41IDEuNSAwIDAgMSAuMi0xLjYgMzY4LjUgMzY4LjUgMCAwIDEgNDEuNy01MS42YzUtNSAxNi4xLTE5LjggMTcuNy0yMi42bC4yLS40YzItMy41IDctMTIuNiA4LjgtMjMuM2ExLjUgMS41IDAgMCAxIDMtLjF2LjZhNzcuMiA3Ny4yIDAgMCAxLTkuMSAyNC4ybC0uMy41YTIwMS42IDIwMS42IDAgMCAxLTE4LjIgMjMuMiAzNzMuNiAzNzMuNiAwIDAgMC00MiA1MS44bC0uNi4xek02MjUgMjYyLjdhMS41IDEuNSAwIDAgMS0xLjMtLjggMzAgMzAgMCAwIDAtNC43LTQuOCAyMi45IDIyLjkgMCAwIDAtNi0zLjZjLTguNy0zLTIwLjYtMS0yMi43LjlhMS41IDEuNSAwIDAgMS0yLjQtLjVsLS4xLS45YTE1MiAxNTIgMCAwIDAgMS0yOS40IDEuNSAxLjUgMCAwIDEgMy0uMmMuNiA5IC40IDE4LjItLjcgMjcuMmEzOSAzOSAwIDAgMSAyMi45IDAgMjUgMjUgMCAwIDEgNyA0LjJjLjQuMyA0LjIgMy41IDUuNCA1LjdhMS41IDEuNSAwIDAgMSAuMSAxLjEgMS41IDEuNSAwIDAgMS0uNyAxbC0uOC4xeiIvPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Ik01MDUuNyAyNjMuMyAzNzEgMjg0LjVhMTAuMyAxMC4zIDAgMCAxLTguOC0yLjhsLTYuOC02LjYtMjIuNi0yMDAuM2E0LjUgNC41IDAgMCAxIDMuOC01LjJsMTM1LjMtMjEuM2E0LjUgNC41IDAgMCAxIDUuMSAzLjhsMzIuNCAyMDZhNC41IDQuNSAwIDAgMS0zLjcgNS4yeiIvPgogIDxwYXRoIGZpbGw9IiNmZmYiIGQ9Im01MDIuOSAyNTguOC0xMzEgMjAuNmExMC44IDEwLjggMCAwIDEtMTIuNC05TDMyOS4yIDc4LjNjLS4yLTEuNCAyLTEyLjQgMy4zLTEyLjZMNDY0IDQ1bDkgOCAzMiAyMDNhMi41IDIuNSAwIDAgMS0yLjEgMi44eiIvPgogIDxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0zNzAuMSAyNzkuN2ExMSAxMSAwIDAgMS0xMC44LTkuM0wzMjkuMSA3OC4zYy4xLTIuMy41LTQuNiAxLTYuOC43LTIuNiAxLjYtNS44IDIuNC02TDQ2NC4xIDQ1bDkgOC4xIDMyIDIwMi45YTIuNiAyLjYgMCAwIDEtMi4yIDNsLTEzMSAyMC43SDM3MHpNNDY0IDQ1LjIgMzMyLjYgNjUuOWEzNSAzNSAwIDAgMC0zLjIgMTIuNGwzMC4yIDE5Mi4xYTEwLjYgMTAuNiAwIDAgMCAxMi4yIDguOWwxMzEtMjAuN2EyLjMgMi4zIDAgMCAwIDItMi43TDQ3MyA1My4zeiIvPgogIDxwYXRoIGZpbGw9IiM2NmE4ZjciIGQ9Ik00NjEuMSA0MC41IDMyNS44IDYxLjhhNC41IDQuNSAwIDAgMC0zLjcgNS4ybDMyLjQgMjA2YTQuNSA0LjUgMCAwIDAgNS4xIDMuOEw0OTUgMjU1LjVhNC41IDQuNSAwIDAgMCAzLjgtNS4xTDQ2Ni4zIDQ0LjNhNC41IDQuNSAwIDAgMC01LjItMy44eiIvPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Ik0zNTkgMjc4LjNhNiA2IDAgMCAxLTYtNWwtMzIuNC0yMDZhNiA2IDAgMCAxIDUtN0w0NjEgMzlhNiA2IDAgMCAxIDYuOSA1bDMyLjQgMjA2YTYgNiAwIDAgMS01IDdMMzYwIDI3OC4zYTYgNiAwIDAgMS0uOSAwek00NjEuOCA0MmgtLjVMMzI2LjEgNjMuM2EzIDMgMCAwIDAtMi41IDMuNGwzMi40IDIwNmEzIDMgMCAwIDAgMy40IDIuNkw0OTQuNyAyNTRsLjIgMS41LS4yLTEuNWEzIDMgMCAwIDAgMi41LTMuNEw0NjQuOCA0NC41YTMgMyAwIDAgMC0zLTIuNXoiLz4KICA8cGF0aCBmaWxsPSIjNjZhOGY3IiBkPSJNMzYxLjQgMjczYTEuNSAxLjUgMCAwIDEtMS41LTEuM0wzMjcuNiA2Ni41YTEuNSAxLjUgMCAwIDEgMy0uNUwzNjMgMjcxLjNjMCAuNCAwIC44LS4zIDEuMXMtLjUuNS0xIC42aC0uMnpNMzQ5LjYgODIuMWExLjUgMS41IDAgMCAxLS4yLTNsODguNS0xNGExLjUgMS41IDAgMCAxIC41IDNsLTg4LjUgMTR6bTIuMiAxOC43YTEuNSAxLjUgMCAwIDEtLjItM2w0OC44LTUuM2ExLjUgMS41IDAgMSAxIC40IDNsLTQ4LjggNS4zeiIvPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Im00OTIuNyAyMjQtMTIyIDYwLjhhMTAuMyAxMC4zIDAgMCAxLTkuMiAwbC04LjUtNC4zLTgxLjgtMTg0LjNhNC41IDQuNSAwIDAgMSAyLTZsMTIyLjYtNjFhNC41IDQuNSAwIDAgMSA2IDJsOTMgMTg2LjhhNC41IDQuNSAwIDAgMS0yIDZ6Ii8+CiAgPHBhdGggZmlsbD0iI2ZmZiIgZD0ibTQ4OC43IDIyMC42LTExOC44IDU5YTEwLjggMTAuOCAwIDAgMS0xNC41LTQuOGwtODYuNi0xNzQuMmMtLjYtMS4yLTEuOC0xMi4zLS42LTEzbDExOS4yLTU5LjIgMTEgNSA5MS40IDE4My45YTIuNSAyLjUgMCAwIDEtLjMgMi43bC0uOC42eiIvPgogIDxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0zNjUgMjgxYTExIDExIDAgMCAxLTkuNy02LjFsLTg2LjYtMTc0LjJjLS42LTEuMi0yLTEyLjUtLjYtMTMuMmwxMTkuMy01OS4zIDExLjEgNS4xTDQ5MCAyMTcuMmEyLjYgMi42IDAgMCAxLTEuMiAzLjVMMzcwIDI3OS44YTEwIDEwIDAgMCAxLTQuOCAxLjF6bTIyLjQtMjUyLjRMMjY4LjIgODcuOGMtLjMuMi0uNiAxLjktLjIgNi4xIDAgMi4yLjQgNC41IDEgNi43bDg2LjUgMTc0LjFhMTAuNiAxMC42IDAgMCAwIDE0LjMgNC44bDExOC44LTU5YTIuMyAyLjMgMCAwIDAgMS0zLjJMMzk4LjQgMzMuNXoiLz4KICA8cGF0aCBmaWxsPSIjNjZhOGY3IiBkPSJNMzgzLjIgMjUgMjYwLjYgODZhNC41IDQuNSAwIDAgMC0yIDZsOTIuOSAxODYuN2E0LjUgNC41IDAgMCAwIDYgMmwxMjIuNi02MWE0LjUgNC41IDAgMCAwIDItNkwzODkuNCAyN2E0LjUgNC41IDAgMCAwLTYtMnoiLz4KICA8cGF0aCBmaWxsPSIjMDMyNDNiIiBkPSJNMzU1LjUgMjgyLjhhNiA2IDAgMCAxLTUuNC0zLjRMMjU3LjIgOTIuOGE2IDYgMCAwIDEgMi43LTguMWwxMjIuNy02MWE2IDYgMCAwIDEgOCAyLjdsOTIuOSAxODYuOGE2IDYgMCAwIDEtMi43IDhsLTEyMi42IDYxYTYgNiAwIDAgMS0yLjcuN3pNMzg1LjMgMjZhMyAzIDAgMCAwLTEuNC4zbC0xMjIuNiA2MWEzIDMgMCAwIDAtMS40IDRsOTIuOSAxODYuOGEzIDMgMCAwIDAgNCAxLjNsMTIyLjYtNjFhMyAzIDAgMCAwIDEuNS0xLjcgMyAzIDAgMCAwLS4xLTIuM0wzODcuOSAyNy43YTMgMyAwIDAgMC0xLjctMS41IDMgMyAwIDAgMC0xLS4yeiIvPgogIDxwYXRoIGZpbGw9IiM2NmE4ZjciIGQ9Ik0zNTcuNiAyNzYuOGExLjUgMS41IDAgMCAxLTEuNC0uOUwyNjMuNyA5MGExLjUgMS41IDAgMCAxIC4yLTEuNyAxLjUgMS41IDAgMCAxIDIuNS4zbDkyLjYgMTg2YTEuNSAxLjUgMCAwIDEtLjcgMmwtLjYuMnpNMjg5IDk4LjJhMS41IDEuNSAwIDAgMS0xLjQtLjggMS41IDEuNSAwIDAgMSAuNy0ybDgwLjMtMzkuOWExLjUgMS41IDAgMCAxIDEuMyAyLjdsLTgwLjIgNDBoLS43em03LjYgMTcuMmExLjUgMS41IDAgMCAxLS42LTIuOWw0NS0xOS43YTEuNSAxLjUgMCAwIDEgMS4yIDIuOGwtNDUgMTkuNy0uNi4xeiIvPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Ik01MDMgMTEyLjUgNTE3LjQgMjY4bC0xMi45IDEwLjhMMjY2IDI4OGE1LjIgNS4yIDAgMCAxLTUuOC00LjVMMjQ2IDEzM2E1IDUgMCAwIDEgNC45LTUuNEw0OTcgMTA4YTUuMiA1LjIgMCAwIDEgNS44IDQuNXoiLz4KICA8cGF0aCBmaWxsPSIjZmZmIiBkPSJNNDk3LjQgMTE1LjMgNTExIDI2MWMuNiA2LjUgNi41IDExLjQtLjUgMTJsLTIyOS43IDE4LjNjLTEuNi4xLTE0LjUtMy4xLTE0LjYtNC42bC0xMy43LTE0Ni4zLS41LTUuM2EzLjEgMy4xIDAgMCAxIDMtMy4zbDIzOS4yLTE5YTIuOSAyLjkgMCAwIDEgMy4yIDIuNHoiLz4KICA8cGF0aCBmaWxsPSIjMDMyNDNiIiBkPSJNNTAzLjkgMTIyLjggNTE4IDI3My40YTUgNSAwIDAgMS00LjkgNS40bC0yNDYuMyAxOS42YTUuMyA1LjMgMCAwIDEtNS44LTQuNmwtMTQtMTUwLjRhNSA1IDAgMCAxIDQuOC01LjRsMjQ2LjMtMTkuNmE1LjMgNS4zIDAgMCAxIDUuOCA0LjV6Ii8+CiAgPHBhdGggZmlsbD0iIzAzMjQzYiIgZD0iTTI2Ni40IDI5OS44YTYuNyA2LjcgMCAwIDEtNi44LTUuOWwtMTQuMi0xNTAuNWE2LjIgNi4yIDAgMCAxIDEuNS00LjYgNyA3IDAgMCAxIDQuOC0yLjRMNDk4IDExNi44YTYuOCA2LjggMCAwIDEgNy40IDZsMTQuMSAxNTAuNGE2LjMgNi4zIDAgMCAxLTEuNCA0LjYgNyA3IDAgMCAxLTQuOCAyLjRMMjY3IDI5OS44em0yMzIuMi0xODBoLS40TDI1MiAxMzkuNGMtMSAuMS0yIC42LTIuNyAxLjRhMy4yIDMuMiAwIDAgMC0uNyAyLjNsMTQgMTUwLjVhMy43IDMuNyAwIDAgMCA0LjIgMy4yTDUxMyAyNzcuMmE0IDQgMCAwIDAgMi43LTEuNCAzLjIgMy4yIDAgMCAwIC44LTIuM0w1MDIuNCAxMjNhMy43IDMuNyAwIDAgMC0zLjgtMy4yeiIvPgogIDxwYXRoIGZpbGw9IiM2NmE4ZjciIGQ9Ik0yNjYuNiAyOTJhMS41IDEuNSAwIDAgMS0uMS0zbDI0NS4zLTE5LjVhMS41IDEuNSAwIDAgMSAxLjUuOGwuMi42YTEuNSAxLjUgMCAwIDEtMS40IDEuNkwyNjYuNyAyOTJ6bTU4LjgtMjQuNmExLjUgMS41IDAgMCAxLTEuNC0xLjRsLTYtNjMuMmExLjUgMS41IDAgMCAxIDEuMy0xLjcgMS41IDEuNSAwIDAgMSAxLjcgMS41bDYgNjMuMWExLjUgMS41IDAgMCAxLTEuNCAxLjZ6bS00MC4yIDMuMmExLjUgMS41IDAgMCAxLTEuNS0xLjRsLTkuMi05OC41YTEuNSAxLjUgMCAwIDEgLjMtMWMuMy0uNC42LS42IDEtLjZsMTUuNC0xLjJhMS41IDEuNSAwIDAgMSAxLjYgMS4zbDkuMyA5OC41YTEuNSAxLjUgMCAwIDEtMS40IDEuNmwtMTUuNCAxLjN6bS03LjYtOTguNiA5IDk1LjQgMTIuMy0xTDI5MCAxNzF6Ii8+CiAgPHBhdGggZmlsbD0iIzAzMjQzYiIgZD0ibTU5Ni41IDIxNi44LTgyIDEzNy43YTExLjQgMTEuNCAwIDAgMS04LjYgNS41bC0xMC41IDEuMkwzMTYuMiAyNDNhNSA1IDAgMCAxLTEuNy02LjhsODIuNC0xMzguNGE1IDUgMCAwIDEgNi45LTEuN2wxOTEgMTEzLjhhNSA1IDAgMCAxIDEuNyA2Ljl6Ii8+CiAgPHBhdGggZmlsbD0iI2ZmZiIgZD0ibTM4Mi42IDEwMS41LTgyLjQgMTM4LjNhNSA1IDAgMCAwIDEuNyA2LjhsMTkxIDExMy45YTUgNSAwIDAgMCA2LjktMS44bDgyLjQtMTM4LjNhNSA1IDAgMCAwLTEuNy02LjlsLTE5MS0xMTMuOGE1IDUgMCAwIDAtNi45IDEuOHoiLz4KICA8dXNlIHhsaW5rOmhyZWY9IiNhIi8+CiAgPHBhdGggZmlsbD0iI2ZmZiIgZD0ibTU5MC43IDIxNi04MCAxMzQuM2ExMiAxMiAwIDAgMS0xNi40IDQuMUwzMTYuNyAyNDguNWMtMS40LS43LTkuMi0xMC40LTguNC0xMS43bDgwLjItMTM0LjYgMTMuMy0yIDE4OCAxMTJhMi43IDIuNyAwIDAgMSAuOSAzLjh6Ii8+CiAgPHBhdGggZmlsbD0iI2RjZTJlYSIgZD0ibTMzNy4zIDE3Ny40IDQ1LjMtNzZhNSA1IDAgMCAxIDYuOS0xLjdsMTkxIDExMy44Yy03Ny4zLTQ2LTE2MS41LTExMy43LTI0My4yLTM2eiIvPgogIDxwYXRoIGZpbGw9IiM2NmE4ZjciIGQ9Im00OTAgMzUxLjItLjctLjItMTc4LjgtMTA2LjZhMS41IDEuNSAwIDAgMS0uNS0yIDEuNSAxLjUgMCAwIDEgMS0uNyAxLjUgMS41IDAgMCAxIDEuMS4xbDE3OC43IDEwNi41YTEuNSAxLjUgMCAwIDEtLjcgMi45em0xMi43LTQzLjRjLS4yIDAtLjUgMC0uNy0uMmExLjUgMS41IDAgMCAxLS42LTJsMzguMi02NGExLjUgMS41IDAgMCAxIDIuNS0uMiAxLjUgMS41IDAgMCAxIDAgMS43TDUwNCAzMDdhMS41IDEuNSAwIDAgMS0xLjMuN3ptLTIwLjkuNi0uOC0uMi0xMi40LTcuNGExLjUgMS41IDAgMCAxLS42LTIuMWw1MS04NS40YTEuNSAxLjUgMCAwIDEgMS41LS44bC42LjIgMTIuNCA3LjRhMS41IDEuNSAwIDAgMSAuNSAyLjFsLTUwLjkgODUuNGExLjUgMS41IDAgMCAxLTEuMy44em0tMTAuNC05LjUgOS44IDUuOSA0OS40LTgyLjgtOS44LTUuOHpNNDI1IDI1Ny4xYTQyIDQyIDAgMSAxIDAtODQuMSA0MiA0MiAwIDAgMSAwIDg0LjF6bTAtODAuOWEzOS4xIDM5LjEgMCAwIDAtMzguOSA0MSAzOSAzOSAwIDEgMCAzOS00MXoiLz4KICA8dXNlIHhsaW5rOmhyZWY9IiNhIi8+CiAgPHBhdGggZmlsbD0iI2ZmZiIgZD0iTTU2MCAxMjUuNmMtNC42LTEuMS05LjggMS0xMy42IDMuMy0zLjQgMi05LjcgMy40LTExLjUgMTAuNi0yLjkgMTEuNi01LjYgMTAtNi42IDE2LS42IDMuOCAyIDIxLjYgOCAyNC4yczEyLjggMiAyNS4yLThjNC44LTMuOCA5LTguNSAxMi41LTEzLjYgMy00LjUgNC0xMyAyLjMtMTguMy0xLjMtMy45LTUuMi03LTguMy05LjJzLTQuMy00LjEtOC01eiIvPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Ik01NDIuNCAxODIuNWMtMi4zIDAtNC41LS41LTYuNi0xLjQtNy40LTMtOS42LTIyLjMtOS0yNS44LjMtMi4yIDEuMS00LjIgMi40LTYgMS4yLTEuNyAyLjctNCA0LjItMTAuMSAxLjUtNi40IDYuNC04LjcgOS45LTEwLjRhMjcgMjcgMCAwIDAgMi4zLTEuMmMzLjctMi4zIDkuNC00LjggMTQuNy0zLjUgMi40LjYgNC43IDEuOCA2LjYgMy42bDIgMS43YzMuNyAyLjcgNy41IDUuOCA4LjggOS45IDIgNS44LjggMTQuOC0yLjQgMTkuNi0zLjYgNS4zLTcuOSAxMC0xMi45IDE0LTguOCA3LjEtMTUgOS42LTIwIDkuNnptMTUtNTUuN2MtNCAwLTggMi0xMC4yIDMuNGwtMi42IDEuM2MtMy4zIDEuNi03IDMuNC04LjMgOC40LTEuNiA2LjctMy4yIDkuMS00LjUgMTEuMS0xIDEuNC0xLjggMy0yIDQuOC0uNiAzLjggMi4xIDIwLjQgNy4xIDIyLjUgNS41IDIuMiAxMS43IDEuOCAyMy43LTcuN2E2MCA2MCAwIDAgMCAxMi4yLTEzLjRjMi43LTQgMy43LTEyLjEgMi0xNy0xLTMuMi00LjMtNi03LjctOC40TDU2NSAxMzBhMTEuOCAxMS44IDAgMCAwLTUuNC0zbC0yLjMtLjJ6Ii8+CiAgPHBhdGggZmlsbD0iIzAzMjQzYiIgZD0iTTU2NiAxNDMuNWMtMS0xLjQtLjItLjMtMy40LjVhMjcuNCAyNy40IDAgMCAxLTI0LjktMTAuOWMtNy0xMC4zLTcuMS0yMy4xIDEtMzIuMyAyLjgtMy4yIDYuNy03IDEwLjgtOGEyMC4yIDIwLjIgMCAwIDEgNC44LS41Yy40IDEuMi0yLjQgNC4xLTEuNSA1LjUuMy42IDMuOC0xLjQgNC0uNC4yIDEuMy0yLjQgNS4zLTEgNi43LjEuMSAyLjgtMS41IDMtMS42IDIuMy0xLjMgNi4xLTMuNiA4LjUtMS41YTI3LjUgMjcuNSAwIDAgMSA4LjggMjIuN2MwIC4zLS4yIDIgMCAyLjIuNy41IDcuNC0xLjMgMTEuMyA0LjQgMy40IDUgMi4yIDEzLjIuMSAxOWE0OSA0OSAwIDAgMS0xNi41IDIwLjNjLTIuNiAyLTkuNyAyLjctMTAgMi4zczcuMy04LjIgNy42LTEyYy4zLTQtMy44LTMuMy01LjctMS41LS40LjQtMS4zIDEuMy0xLjUuNC0uNy0yLjcgMS42LTQuNiAzLTYuNSAyLjQtMy40IDMuNy01LjUgMS41LTguOHoiLz4KICA8cGF0aCBmaWxsPSIjMDMyNDNiIiBkPSJNNTYxLjcgMTczLjVoLS43YTEuNiAxLjYgMCAwIDEtMS4yLS44Yy0uNy0xLS4xLTEuNyAyLTQuMyAxLjgtMi4zIDUuMi02LjYgNS4zLTguNiAwLS4zIDAtMS0uMy0xLjNhMy4yIDMuMiAwIDAgMC0yLjggMWMtLjYuNS0xLjUgMS40LTIuNyAxLjFhMiAyIDAgMCAxLTEuNC0xLjRjLS43LTIuOSAxLTUgMi40LTYuNmwuOS0xLjJjMi4zLTMuMyAzLTQuNiAxLjctNi43LS42LjMtMS4zLjYtMiAuN2EyOC44IDI4LjggMCAwIDEtMjYuNS0xMS41Yy03LjYtMTEuMy03LjItMjQuNyAxLjItMzQuMSA0LjItNC44IDgtNy41IDExLjUtOC40bDMuNi0uNmMxLjQgMCAyLjctLjIgMyAxLjEuNCAxLS4yIDIuMi0uOSAzLjRsLS4zLjZhMy41IDMuNSAwIDAgMSAyLjggMCAxLjggMS44IDAgMCAxIDEgMS4yYzAgMS4xLS4zIDIuMy0uOCAzLjNsLS40IDEuMyAxLS41YzIuNC0xLjUgNy00LjIgMTAuMi0xLjNhMjkgMjkgMCAwIDEgOS4yIDIzdjEuNmMyLjQgMCA3LjcgMCAxMSA1IDQuNyA2LjggMS41IDE3LjMuNCAyMC40LTMuMyA5LTExIDE2LjQtMTcgMjFhMjEuNCAyMS40IDAgMCAxLTEwLjIgMi43em00LjYtMThjLjcgMCAxLjMgMCAxLjguMy45LjQgMi4zIDEuNSAyIDQuM3MtMyA2LjMtNiAxMC4yYzIuMS0uNCA0LjgtMSA2LTIgNS42LTQuMiAxMy0xMS4yIDE2LTE5LjYgMi01LjUgMy0xMy4xIDAtMTcuNi0yLjYtMy43LTYuNi0zLjctOC43LTMuNi0uOC4xLTEuNSAwLTIuMi0uNC0uOS0uNi0uOC0xLjctLjctMy4ydi0xYzAtMiAwLTQuMS0uMy02LjItMS01LjYtMy44LTEwLjctOC0xNC42LTEuNS0xLjQtNC44LjYtNi42IDEuNy0zIDEuOC00LjEgMi4yLTUgMS4zLTEuNC0xLjUtLjYtMy45IDAtNS44bC4yLS4zYy0xLjIuNC0yLjYuOC0zLjQtLjQtMS0xLjUgMC0zLjMuNy00LjctLjggMC0xLjUuMi0yLjMuNC0yLjkuNy02LjMgMy4yLTEwIDcuNC03LjQgOC40LTcuOCAyMC40LS45IDMwLjVhMjYgMjYgMCAwIDAgMTguOCAxMC40YzEuNS4yIDMgLjEgNC41LS4yLjctLjEgMS4zLS40IDEuOS0uN2wuNy0uNGguOGEyLjMgMi4zIDAgMCAxIDEuNSAxLjJsLTEuMyAxIDEuMy0uOWMyLjggNC40LjcgNy40LTEuNiAxMC41bC0xIDEuNC0xLjMgMS43YzEtLjUgMi0uOCAzLjEtLjh6Ii8+CiAgPHBhdGggZmlsbD0iI2ZmZiIgZD0iTTU2Ny40IDEzOS4yaC0uMWEuNi42IDAgMCAxLS40LS43YzEuMS0zIDIuOC02IDUtOC40bDEuNi0yYS42LjYgMCAwIDEgLjQtLjIuNi42IDAgMCAxIC41LjEuNi42IDAgMCAxIC4yLjQuNi42IDAgMCAxIDAgLjVsLTEuNyAyYy0yIDIuNS0zLjggNC44LTQuOSA4YS42LjYgMCAwIDEtLjYuM3pNNDAyLjcgMzE4LjljLTE2LjMgMC0yOC44LTEwLjctMzYuOC0xOC43LTMuOC00LjEtOC03LjgtMTIuNi0xMS0yLjMtMS42LTMuOC0yLjYtMy00LjNhMi44IDIuOCAwIDAgMSAyLjgtMS42YzEuMiAwIDIuNC4zIDMuNi43bDIuNi42LS42LTEuMnYtLjJsLTEuNC0yLjVjLTEtMS44LTEuOS0zLjgtMi43LTUuNy0xLjctNC4yLTEtNi4zLS40LTcuMWEyLjIgMi4yIDAgMCAxIDEuOC0xIDEuOSAxLjkgMCAwIDEgMS41LjcgMjggMjggMCAwIDEgMi4yIDMuOWwuMi4zYy0uOC0zLjYtMS01LjYuMi02LjYuMy0uMi43LS40IDEuMi0uNCAxLjcgMCAyLjMgMiAzLjMgNWwuNSAxLjRjLS4xLTIuMi4xLTQuNCAxLjQtNWEyIDIgMCAwIDEgMS0uM2MyIDAgMi43IDIuMiAzLjUgNS40YTEuOSAxLjkgMCAwIDEgMS44LTEuOGguMmMxLjkgMCAyLjYgMi4yIDQuMyA3LjMgMiA2LjMgNS4yIDE2IDEwLjkgMjAuM3MxOC43IDEyLjYgMjIgMTQuNmwxIC42LS4zIDEuMi0xIDMuOC0uMyAxLTEgLjJjLTIgLjMtNCAuNS02IC41eiIvPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Ik0zNjEuMyAyNjYuM2MuOCAwIDEuNyAzLjcgMi44IDYuNiAyIDUuMyAzLjUgNSAzLjcgNS4ybC0uNi0yLjhjLS40LTEtMS4yLTcuMiAwLTcuOGwuNC0uMWMuNiAwIDEuMSAxIDIgNC41IDEuNyA2LjMgMy45IDguMyAzLjkgOC4zaC4xYzEuNiAwLS45LTctMS04LjQgMC0uMi0uMi0uOC40LS44IDIuMiAwIDQuOCAyMCAxNC4yIDI3LjNhMzA5IDMwOSAwIDAgMCAyMi4zIDE0LjdjLS40IDEuMS0xIDMuOS0xIDMuOS0yIC4zLTQgLjQtNS45LjQtMTYuNCAwLTI4LjgtMTEuMi0zNS43LTE4LjItOS43LTEwLjUtMTUuOS0xMi4yLTE1LjItMTMuNi4xLS4zLjMtLjUuNi0uNmwuOC0uMWMxLjggMCA0LjYgMS4zIDYuNCAxLjNhMS41IDEuNSAwIDAgMCAxLjQtLjdjLjQtLjUtLjctMi41LTEtMy0xLjMtMi42LTIuOC01LjItNC04LTEuNy00LjMtLjYtNiAwLTZhLjQuNCAwIDAgMSAuMyAwYzEuMyAxLjcgNC40IDkuMiA1LjcgOS4yaC4xYzEuMi0uNS0yLjQtMTAuMi0xLTExLjNoLjJ6bTAtM2MtLjggMC0xLjYuMi0yLjIuNy0uNi41LTEgMS4yLTEuMiAyYTMuNCAzLjQgMCAwIDAtMy42LS4yYy0uNS4zLTEgLjctMS4zIDEuMi0xIDEuMy0xLjYgMy45LjIgOC41LjggMiAxLjggNCAyLjggNS45bC41IDFhMTIgMTIgMCAwIDAtMy40LS43IDQuMyA0LjMgMCAwIDAtNC4yIDIuNWMtMS4yIDIuOSAxLjMgNC43IDMuNSA2LjIgNC42IDMuMSA4LjcgNi43IDEyLjQgMTAuOCA4LjEgOC4yIDIxIDE5LjEgMzcuOCAxOS4xYTM5IDM5IDAgMCAwIDYuMy0uNWwyLS4zLjQtMiAxLTMuNy43LTIuMy0yLTEuMmEyNzkuMyAyNzkuMyAwIDAgMS0yMi0xNC41Yy01LjMtNC04LjMtMTMuNC0xMC40LTE5LjYtMS44LTUuNS0yLjctOC4zLTUuNy04LjNoLS4yYy0uNCAwLS43LjEtMSAuMy0uOC0yLjItMi00LTQuMi00LS42IDAtMS4yLjItMS43LjUtLjUuMi0uOC42LTEuMSAxLS44LTEuNS0xLjgtMi41LTMuNS0yLjV6Ii8+CiAgPHBhdGggZmlsbD0iI2ZmZiIgZD0iTTUzOC4zIDM1MC4yYy0xMiAwLTIzLjUtMy0zMi40LTguNGE0MC4xIDQwLjEgMCAwIDEtMTguNi0yNWMtLjEtLjUtMy4zLTEwLjgtLjEtMTQuNmExLjggMS44IDAgMCAxIDEuMy0uNyAyIDIgMCAwIDEgMS45IDEuNWwuMyAxdi0zLjVjLjItMS41LjQtNC4yIDIuNS00LjRoLjFjLjQgMCAuOC4yIDEuMi40cy42LjUuOCAxbC4xLS44LjEtLjNjLjMtMS4zIDEtNCAzLTRoLjRjMSAuMiAxLjggMSAyLjIgMi4zLjctMS4zIDEuNi0yLjMgMi44LTIuMy41IDAgMS4xLjMgMS41LjYgMS4yIDEgMSAyLjcuOSA0LjV2LjRhMzUgMzUgMCAwIDAgLjIgNS42bC4zIDMuMXYxLjZjLjgtLjUgMS40LTEuMSAyLTEuOCAxLjUtMS41IDIuOS0yLjkgNC41LTIuOWEzIDMgMCAwIDEgMS42LjVjMi40IDEuOCAxIDUuMy0uMiA4LjQtLjYgMS4zLTEgMi42LTEuMSA0IC44IDYgNi4yIDkuNyAxNiAxMC45IDEuNy4yIDMuMy4yIDUgLjIgMTEgMCAyMi4yLTMuOCAyOC41LTkuOCA5LjgtOS40IDE3LjYtMjguOCAyMi41LTQyLjcgMS0yLjcgMi43LTkuMiA0LTE0LjUgMS40LTUuNCAyLTcuNyAyLjMtOC40bC4yLS40LjMtLjJjMS41LTEuMiA0LTEuNyA3LjYtMS43IDUuMyAwIDEyIDEuMyAxNC4xIDIuMmEyNCAyNCAwIDAgMSA2LjkgNC45bC42LjYtLjMgMXMtNC45IDE1LTggMjMuNGE2NiA2NiAwIDAgMS00LjkgMTAgNTcgNTcgMCAwIDAtNS4xIDExYzAgLjUgMCAxIC4yIDEuNmE1LjMgNS4zIDAgMCAxLS42IDVjLTEgMS4zLTIuNiAxLjYtMy44IDEuOGwtMSAuMmE1Ni4zIDU2LjMgMCAwIDAtMy40IDUuOGMtMS40IDIuNy0zIDUuMy00LjkgNy43LTMuNSA0LTcuNCA3LjgtMTEuNSAxMS4ybC0xIC45YTU5LjcgNTkuNyAwIDAgMS0zOSAxMy4xeiIvPgogIDxwYXRoIGZpbGw9IiMwMzI0M2IiIGQ9Ik02MDAgMjUxLjNjNC42IDAgOS4yLjcgMTMuNSAyLjEgMi40IDEuMiA0LjUgMi43IDYuNCA0LjUgMCAwLTQuOSAxNC45LTggMjMuNC0yLjYgNy4yLTggMTQtMTAgMjEtLjcgMiAxLjUgNC0uMiA2LjMtMSAxLjQtNCAxLTQuNyAxLjgtMyA0LjItNSA5LjQtOC40IDEzLjYtMy41IDQuMy04LjEgOC4yLTEyLjMgMTEuOWE1OC41IDU4LjUgMCAwIDEtMzggMTIuOCA2MS42IDYxLjYgMCAwIDEtMzEuNy04LjIgMzguOSAzOC45IDAgMCAxLTE3LjktMjQuM2MtLjktMi44LTIuNS0xMC41LS40LTEzdi0uMmguMmMuMiAwIC40LjUuNS42LjYgMS4zIDEuOCA3LjkgMyA3LjlhLjUuNSAwIDAgMCAuNC0uMnMtLjMtOS4zLS4xLTEwLjRjMC0uNy4yLTMuMyAxLTMuMyAxLjMgMCAyIDUuNCAyLjEgNi40bC45IDIuN2MuMS0uMi4yLTguMS42LTkuNy4yLS43LjYtMy4xIDEuNy0zLjFoLjFjMS43LjIgMS45IDguMiAyLjMgOS42di4yLTNjMC0uOCAxLjItNi44IDIuOS02LjhsLjYuM2MuNy41LjMgMi44LjIgMy41LS4yIDMgLjQgNiAuNSA5IDAgLjUgMCAyLjguNiAzIC4yLjIuNC4yLjYuMiAyIDAgNC45LTQuOSA2LjktNC45LjIgMCAuNCAwIC42LjIgMi41IDEuOS0yLjIgOC41LTEuOSAxMS4zIDEuMiA4LjYgMTAuMSAxMS40IDE3LjQgMTIuMmw1LjEuM2E0NSA0NSAwIDAgMCAyOS42LTEwLjJjMTAuMy05LjggMTguMi0zMC4xIDIzLTQzLjMgMS43LTUgNS41LTIxLjQgNi4yLTIyLjggMS4zLTEgMy44LTEuNCA2LjctMS40em0wLTNjLTQgMC02LjguNi04LjYgMmwtLjUuNS0uNC42YTg2IDg2IDAgMCAwLTIuNCA4LjcgMTkzIDE5MyAwIDAgMS00IDE0LjRjLTQuOSAxMy43LTEyLjUgMzMtMjIuMSA0Mi4xYTQxLjggNDEuOCAwIDAgMS0zMi4yIDkuMWMtOS0xLTE0LTQuMy0xNC44LTkuNWExNyAxNyAwIDAgMSAxLTMuMmMxLjMtMy4zIDMtNy43LS40LTEwLjJhNCA0IDAgMCAwLTIuNC0uOGMtMiAwLTMuNiAxLjQtNS4xIDNsLS4yLTEuN2EyOSAyOSAwIDAgMS0uMi01LjN2LS4zYy4yLTEuNy40LTQuMy0xLjQtNS44YTQgNCAwIDAgMC01LjIgMCAzLjYgMy42IDAgMCAwLTIuNi0xYy0yLjYgMC0zLjcgMi4yLTQuMiAzLjhsLTEtLjFoLS4yYy0zLjMuMi0zLjcgMy44LTMuOSA1LjVhMy4zIDMuMyAwIDAgMC0yLjIuMmMtLjUuMi0uOC41LTEuMSAxLTMuNSA0LjItLjggMTQtLjIgMTUuOWE0MS41IDQxLjUgMCAwIDAgMTkuMyAyNS45YzkuMiA1LjYgMjEgOC42IDMzLjIgOC42IDE1LjUgMCAzMC4xLTQuOSA0MC0xMy41bDEtLjhjNC4yLTMuNSA4LjItNy4zIDExLjgtMTEuNWE1MSA1MSAwIDAgMCA1LTcuOWwzLTUuMmguMmMxLjQtLjIgMy42LS41IDQuOS0yLjRhNi45IDYuOSAwIDAgMCAuOC02LjNsLS4yLS45YzEuMy0zLjYgMy03LjIgNS0xMC41YTcxIDcxIDAgMCAwIDUtMTAuM2MzLTguNSA4LTIzLjMgOC0yMy41bC42LTEuNy0xLjMtMS4zYTI2LjMgMjYuMyAwIDAgMC03LjQtNS4yYy0yLjMtMS05LTIuNC0xNC43LTIuNHoiLz4KPC9zdmc+",alt:(0,T.__)("Loading Vector.","wp-module-onboarding"),className:"nfd-installer-modal__content-image"}),"installing"===i&&(0,L.createElement)(L.Fragment,null,(0,L.createElement)("div",{className:"nfd-installer-modal__content-subheading"},(0,T.sprintf)(/* translators: %s: Plugin Name */ /* translators: %s: Plugin Name */ +(0,T.__)("Activating the %s","wp-module-onboarding"),j)),(0,L.createElement)("div",{className:"nfd-installer-modal__loader"})),"failed"===i&&(0,L.createElement)("div",{className:"nfd-installer-modal__content-error"},(0,L.createElement)("img",{src:"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjgiIGhlaWdodD0iMjkiIHZpZXdCb3g9IjAgMCAyOCAyOSIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0yNS4yIDE0LjVDMjUuMiAyMC42ODU2IDIwLjE4NTYgMjUuNyAxNCAyNS43QzcuODE0NCAyNS43IDIuNzk5OTkgMjAuNjg1NiAyLjc5OTk5IDE0LjVDMi43OTk5OSA4LjMxNDQ2IDcuODE0NCAzLjMwMDA1IDE0IDMuMzAwMDVDMjAuMTg1NiAzLjMwMDA1IDI1LjIgOC4zMTQ0NiAyNS4yIDE0LjVaTTE0IDcuNTAwMDVDMTQuNTc5OSA3LjUwMDA1IDE1LjA1IDcuOTcwMTUgMTUuMDUgOC41NTAwNVYxNC44NUMxNS4wNSAxNS40Mjk5IDE0LjU3OTkgMTUuOSAxNCAxNS45QzEzLjQyMDEgMTUuOSAxMi45NSAxNS40Mjk5IDEyLjk1IDE0Ljg1VjguNTUwMDVDMTIuOTUgNy45NzAxNSAxMy40MjAxIDcuNTAwMDUgMTQgNy41MDAwNVpNMTQgMjEuNUMxNC43NzMyIDIxLjUgMTUuNCAyMC44NzMyIDE1LjQgMjAuMUMxNS40IDE5LjMyNjggMTQuNzczMiAxOC43IDE0IDE4LjdDMTMuMjI2OCAxOC43IDEyLjYgMTkuMzI2OCAxMi42IDIwLjFDMTIuNiAyMC44NzMyIDEzLjIyNjggMjEuNSAxNCAyMS41WiIgZmlsbD0iI0M3MTkxOSIvPgo8L3N2Zz4K",alt:(0,T.__)("Error Icon.","wp-module-installer"),className:"nfd-installer-modal__content-error--icon"}),(0,L.createElement)("div",null,e)))))},i=()=>{const[M,I]=(0,g.useState)(),[j,u]=(0,g.useState)(!1),[A,N]=(0,g.useState)(),[D,y]=(0,g.useState)(),[S,T]=(0,g.useState)(),[i,t]=(0,g.useState)(),[E,x]=(0,g.useState)(),z=M=>{u(!1),I(M.detail.action),N(M.detail.pluginName),y(M.detail.pluginDownloadUrl),T(M.detail.pluginProvider),t(M.detail.pluginSlug),x(M.detail.redirectUrl),u(!0)};return(0,g.useEffect)((()=>(window.addEventListener("installerParamsSet",z),()=>{window.removeEventListener("installerParamsSet",z)})),[]),(0,L.createElement)("div",{className:"nfd-installer-app"},j&&(0,L.createElement)(C,{action:M,pluginName:A,pluginDownloadUrl:D,pluginProvider:S,pluginSlug:i,redirectUrl:E}))};j()((()=>{t(u)}));const t=M=>{const I=document.createElement("div");I.id=M,document.getElementById(M)||document.body.append(I),(0,g.render)((0,L.createElement)(i,null),I)};((window.newfold=window.newfold||{}).Installer=window.newfold.Installer||{}).installer={}})(); \ No newline at end of file diff --git a/composer.json b/composer.json index e431813..0936af8 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,7 @@ } }, "require": { - "newfold-labs/wp-module-pls": "^0.1.0" + "newfold-labs/wp-module-pls": "^0.2.0" }, "require-dev": { "wp-cli/wp-cli": "^2.11", diff --git a/composer.lock b/composer.lock index 8fb4ee9..e4d95fb 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "293e91d4313e33412aef95a2da475739", + "content-hash": "97cd5ef22f58fdc34c9af9fd0f16e6f9", "packages": [ { "name": "doctrine/inflector", @@ -286,16 +286,16 @@ }, { "name": "newfold-labs/wp-module-pls", - "version": "0.1.0", + "version": "0.2.0", "source": { "type": "git", "url": "https://github.com/newfold-labs/wp-module-pls.git", - "reference": "ffa1517c18da067dd830c809f74aee597f05dc17" + "reference": "5572585b9b84d6bddde8404e0065057979db7b6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/newfold-labs/wp-module-pls/zipball/ffa1517c18da067dd830c809f74aee597f05dc17", - "reference": "ffa1517c18da067dd830c809f74aee597f05dc17", + "url": "https://api.github.com/repos/newfold-labs/wp-module-pls/zipball/5572585b9b84d6bddde8404e0065057979db7b6f", + "reference": "5572585b9b84d6bddde8404e0065057979db7b6f", "shasum": "" }, "require": { @@ -326,10 +326,10 @@ ], "description": "A Newfold module that handles license key provisioning, validation, and other lifecycle events for registered plugins using the PLS API via Hiive.", "support": { - "source": "https://github.com/newfold-labs/wp-module-pls/tree/0.1.0", + "source": "https://github.com/newfold-labs/wp-module-pls/tree/0.2.0", "issues": "https://github.com/newfold-labs/wp-module-pls/issues" }, - "time": "2024-10-09T08:23:27+00:00" + "time": "2024-10-22T16:27:09+00:00" }, { "name": "wp-forge/collection", diff --git a/includes/Data/Constants.php b/includes/Data/Constants.php index 8771ac8..56a2fc3 100644 --- a/includes/Data/Constants.php +++ b/includes/Data/Constants.php @@ -15,7 +15,7 @@ class Constants { */ public function __construct( $container ) { if ( ! defined( 'NFD_INSTALLER_VERSION' ) ) { - define( 'NFD_INSTALLER_VERSION', '1.2.0-beta' ); + define( 'NFD_INSTALLER_VERSION', '1.2.0-beta.7' ); } if ( ! defined( 'NFD_INSTALLER_BUILD_DIR' ) && defined( 'NFD_INSTALLER_VERSION' ) ) { define( 'NFD_INSTALLER_BUILD_DIR', dirname( __DIR__, 2 ) . '/build/' . NFD_INSTALLER_VERSION ); diff --git a/includes/Data/Plugins.php b/includes/Data/Plugins.php index 92d72bc..a259f62 100644 --- a/includes/Data/Plugins.php +++ b/includes/Data/Plugins.php @@ -137,6 +137,7 @@ final class Plugins { protected static $domains = array( 'downloads.wordpress.org' => true, 'nonapproveddomain.com' => null, + 'hiive.cloud' => true, ); /** * Holds the possible status codes for a plugin. diff --git a/includes/RestApi/PluginsController.php b/includes/RestApi/PluginsController.php index ceada55..1534b5c 100644 --- a/includes/RestApi/PluginsController.php +++ b/includes/RestApi/PluginsController.php @@ -127,14 +127,18 @@ public function get_install_plugin_args() { 'type' => 'boolean', 'default' => false, ), + 'provider' => array( + 'type' => 'string', + 'default' => '', + ), ); } - /** - * Get args for the uninstall route. - * - * @return array - */ + /** + * Get args for the uninstall route. + * + * @return array + */ public function get_uninstall_plugin_args() { return array( 'plugin' => array( @@ -183,10 +187,11 @@ public function install( \WP_REST_Request $request ) { $queue = $request->get_param( 'queue' ); $priority = $request->get_param( 'priority' ); $premium = $request->get_param( 'premium' ); + $provider = $request->get_param( 'provider' ); // Checks if the plugin is premium and uses the corresponding function for it. if ( true === $premium ) { - return PluginInstaller::install_premium_plugin( $plugin, $activate ); + return PluginInstaller::install_premium_plugin( $plugin, $provider, $activate ); } // Checks if a plugin with the given slug and activation criteria already exists. diff --git a/includes/Services/PluginInstaller.php b/includes/Services/PluginInstaller.php index 2d6318a..679d331 100644 --- a/includes/Services/PluginInstaller.php +++ b/includes/Services/PluginInstaller.php @@ -156,42 +156,85 @@ public static function install_from_wordpress( $plugin, $activate ) { * Provisions a license and installs or activates a premium plugin. * * @param string $plugin The slug of the premium plugin. + * @param string $provider The provider name for the premium plugin. * @param boolean $activate Whether to activate the plugin after installation. * * @return \WP_Error|\WP_REST_Response */ - public static function install_premium_plugin( $plugin, $activate ) { - $status_codes = Plugins::get_status_codes(); + public static function install_premium_plugin( $plugin, $provider, $activate ) { + // Ensure plugin and provider are not empty + if ( empty( $plugin ) || empty( $provider ) ) { + return new \WP_Error( + 'nfd_installer_error', + __( 'Plugin slug and provider name cannot be empty.', 'wp-module-installer' ) + ); + } + + $pls_utility = new PLSUtility(); + + // Provision a license for the premium plugin, this returns basename and download URL + $license_response = $pls_utility->provision_license( $plugin, $provider ); + if ( is_wp_error( $license_response ) ) { + return $license_response; + } - $premium_status = self::get_plugin_status( $plugin ); + // Get the plugin basename from the license response + $plugin_basename = ! empty( $license_response['basename'] ) ? $license_response['basename'] : false; + + // Check if the plugin is already installed + if ( $plugin_basename && self::is_plugin_installed( $plugin_basename ) ) { + // Check if the plugin is active + if ( is_plugin_active( $plugin_basename ) ) { + // If plugin is already installed and active, return success + return new \WP_REST_Response( + array( + 'message' => __( 'Plugin is already installed and active: ', 'wp-module-installer' ) . $plugin, + ), + 200 + ); + } - // Check if the premium plugin is already installed or active - if ( $status_codes['active'] === $premium_status || $status_codes['installed'] === $premium_status ) { + // Activate the plugin if it's installed but not active + $activate_plugin_response = activate_plugin( $plugin_basename ); + if ( is_wp_error( $activate_plugin_response ) ) { + return new \WP_Error( 'nfd_installer_error', __( 'Failed to activate the plugin: ', 'wp-module-installer' ) . $plugin ); + } + + // Activate the license + $activation_response = $pls_utility->activate_license( $plugin ); + if ( is_wp_error( $activation_response ) ) { + return new \WP_Error( 'nfd_installer_error', __( 'Failed to activate the license for the premium plugin: ', 'wp-module-installer' ) . $plugin ); + } + + // Return success response return new \WP_REST_Response( array( - 'message' => __( 'Premium plugin already installed or active: ', 'wp-module-installer' ) . $plugin, + 'message' => __( 'Successfully provisioned and installed: ', 'wp-module-installer' ) . $plugin, ), 200 ); } - // Provision a license for the premium plugin - $license_response = PLSUtility::provision_license( $plugin ); - if ( is_wp_error( $license_response ) ) { - return $license_response; - } - // Check if the download URL is present in the license response if ( empty( $license_response['downloadUrl'] ) ) { return new \WP_Error( 'nfd_installer_error', __( 'Download URL is missing for premium plugin: ', 'wp-module-installer' ) . $plugin ); } - // Attempt to install and/or activate the premium plugin using the provided download URL + // Plugin is not installed, proceed with installation $install_status = self::install_from_zip( $license_response['downloadUrl'], $activate ); if ( is_wp_error( $install_status ) ) { return new \WP_Error( 'nfd_installer_error', __( 'Failed to install or activate the premium plugin: ', 'wp-module-installer' ) . $plugin ); } + // If activation is requested, activate the license + if ( $activate ) { + $activation_response = $pls_utility->activate_license( $plugin ); + if ( is_wp_error( $activation_response ) ) { + return new \WP_Error( 'nfd_installer_error', __( 'Failed to activate the license for the premium plugin: ', 'wp-module-installer' ) . $plugin ); + } + } + + // Return success response return new \WP_REST_Response( array( 'message' => __( 'Successfully provisioned and installed: ', 'wp-module-installer' ) . $plugin, diff --git a/includes/Services/PluginUpgrader.php b/includes/Services/PluginUpgrader.php index 478fc1e..c452546 100644 --- a/includes/Services/PluginUpgrader.php +++ b/includes/Services/PluginUpgrader.php @@ -19,12 +19,12 @@ class PluginUpgrader { public static function upgrade_extended_yith_plugins() { // Define the list of extended YITH plugins to upgrade. $yith_plugins_to_upgrade = array( - 'yith-woocommerce-ajax-search', 'nfd_slug_yith_woocommerce_ajax_product_filter', 'nfd_slug_yith_woocommerce_wishlist', 'nfd_slug_yith_woocommerce_booking', 'nfd_slug_yith_woocommerce_gift_cards', 'nfd_slug_yith_woocommerce_customize_myaccount_page', + 'yith-woocommerce-ajax-search', ); // Array to store the status of each plugin's upgrade process @@ -47,14 +47,13 @@ public static function upgrade_extended_yith_plugins() { */ public static function upgrade_extended_yith_plugin( $extended_slug ) { // Define the list of extended YITH plugins and their corresponding premium versions. - // TODO: Replace the dummy entitlement slug 'nfd_slug_yith_paypal_payments_for_woocommerce' with actual entitlement slugs. $yith_plugins_to_upgrade = array( - 'yith-woocommerce-ajax-search' => 'nfd_slug_yith_paypal_payments_for_woocommerce', - 'nfd_slug_yith_woocommerce_ajax_product_filter' => 'yith-woocommerce-ajax-product-filter-premium', - 'nfd_slug_yith_woocommerce_wishlist' => 'yith-woocommerce-wishlist-premium', - 'nfd_slug_yith_woocommerce_booking' => 'yith-woocommerce-booking-premium', - 'nfd_slug_yith_woocommerce_gift_cards' => 'nfd_slug_yith_paypal_payments_for_woocommerce', + 'nfd_slug_yith_woocommerce_ajax_product_filter' => 'yith-woocommerce-ajax-navigation', + 'nfd_slug_yith_woocommerce_wishlist' => 'yith-woocommerce-wishlist', + 'nfd_slug_yith_woocommerce_booking' => 'yith-woocommerce-booking', + 'nfd_slug_yith_woocommerce_gift_cards' => 'yith-woocommerce-gift-cards', 'nfd_slug_yith_woocommerce_customize_myaccount_page' => 'yith-woocommerce-customize-myaccount-page', + 'yith-woocommerce-ajax-search' => 'yith-woocommerce-ajax-search', ); // Initialize status array for the plugin upgrade process @@ -89,43 +88,31 @@ public static function upgrade_extended_yith_plugin( $extended_slug ) { return $upgrade_status; } - // Get the status of the premium version of the plugin - $premium_status = PluginInstaller::get_plugin_status( $premium_slug ); - - // Skip if the premium plugin is already active or installed - if ( $status_codes['active'] === $premium_status || $status_codes['installed'] === $premium_status ) { - $upgrade_status['message'] = __( 'Premium plugin already installed or active: ', 'wp-module-installer' ) . $premium_slug; - return $upgrade_status; - } - - // Provision a license for the premium version of the plugin - $license_response = PLSUtility::provision_license( $premium_slug ); - if ( is_wp_error( $license_response ) ) { - $upgrade_status['message'] = __( 'Failed to provision license for: ', 'wp-module-installer' ) . $premium_slug; - return $upgrade_status; - } - - // Check if the download URL is present in the license response - if ( empty( $license_response['downloadUrl'] ) ) { - $upgrade_status['message'] = __( 'Download URL is missing for premium plugin: ', 'wp-module-installer' ) . $premium_slug; - return $upgrade_status; - } - // Check if the premium plugin should be activated after installation $should_activate = ( $status_codes['active'] === $extended_status ); // Deactivate the extended version of the plugin if the premium plugin needs to be activated if ( $should_activate ) { - PluginInstaller::deactivate( $extended_slug ); + $deactivation_response = PluginInstaller::deactivate( $extended_slug ); + if ( is_wp_error( $deactivation_response ) ) { + $upgrade_status['message'] = __( 'Failed to deactivate the extended plugin: ', 'wp-module-installer' ) . $extended_slug; + return $upgrade_status; + } } - // Attempt to install the premium plugin using the provided download URL, and activate it if needed - $premium_install_status = PluginInstaller::install_from_zip( $license_response['downloadUrl'], $should_activate ); - if ( is_wp_error( $premium_install_status ) ) { - $upgrade_status['message'] = __( 'Failed to install the premium plugin: ', 'wp-module-installer' ) . $premium_slug; + // Use the install_premium_plugin function to install and activate the premium plugin + $install_status = PluginInstaller::install_premium_plugin( $premium_slug, 'yith', $should_activate ); + if ( is_wp_error( $install_status ) ) { + $upgrade_status['message'] = $install_status->get_error_message(); + + // Reactivate the extended plugin if premium installation failed and it was deactivated if ( $should_activate ) { - PluginInstaller::activate( $extended_slug ); + $reactivation_response = PluginInstaller::activate( $extended_slug ); + if ( is_wp_error( $reactivation_response ) ) { + $upgrade_status['message'] .= __( ' Also Failed to reactivate the extended plugin: ', 'wp-module-installer' ) . $extended_slug; + } } + return $upgrade_status; } diff --git a/includes/WPAdmin/Listeners/DataAttrListener.php b/includes/WPAdmin/Listeners/DataAttrListener.php deleted file mode 100644 index 6a21634..0000000 --- a/includes/WPAdmin/Listeners/DataAttrListener.php +++ /dev/null @@ -1,51 +0,0 @@ - \get_home_url() . '/index.php?rest_route=', - 'pluginInstallHash' => PluginInstaller::rest_get_plugin_install_hash(), - ) - ) . ';', - 'before' - ); - - wp_enqueue_script( 'nfd-installer-data-attr-listener' ); - } - } -} diff --git a/includes/WPAdmin/Listeners/InstallerListener.php b/includes/WPAdmin/Listeners/InstallerListener.php new file mode 100644 index 0000000..4e7470d --- /dev/null +++ b/includes/WPAdmin/Listeners/InstallerListener.php @@ -0,0 +1,131 @@ +listen_for_premium_plugin_activation(); + } + + /** + * Enqueues all the installer scripts that are required. + * The Data Attribute Listener Script + * The Modal UI with React that installs the plugin + * + * @return void + */ + public function enqueue_installer_scripts() { + $this->enqueue_data_attr_listener(); + $this->enqueue_installer_react_script(); + } + + /** + * Enqueues the data-* attribute listener script. + * + * @return void + */ + public function enqueue_data_attr_listener() { + $asset_file = NFD_INSTALLER_BUILD_DIR . '/dataAttrListener.asset.php'; + + if ( is_readable( $asset_file ) ) { + $asset = include $asset_file; + + wp_register_script( + 'nfd-installer-data-attr-listener', + NFD_INSTALLER_BUILD_URL . '/dataAttrListener.js', + array_merge( $asset['dependencies'], array() ), + $asset['version'], + true + ); + + wp_enqueue_script( 'nfd-installer-data-attr-listener' ); + } + } + + /** + * Enqueues the installer script. + * + * @return void + */ + public function enqueue_installer_react_script() { + $asset_file = NFD_INSTALLER_BUILD_DIR . '/installer.asset.php'; + + if ( is_readable( $asset_file ) ) { + $asset = include $asset_file; + + wp_register_script( + 'nfd-installer-enqueue', + NFD_INSTALLER_BUILD_URL . '/installer.js', + array_merge( $asset['dependencies'], array() ), + $asset['version'], + true + ); + + wp_register_style( + 'nfd-installer-enqueue', + NFD_INSTALLER_BUILD_URL . '/installer.css', + array(), + $asset['version'] + ); + + wp_add_inline_script( + 'nfd-installer-enqueue', + 'var nfdInstaller =' . wp_json_encode( + value: array( + 'restUrl' => \get_home_url() . '/index.php?rest_route=', + 'pluginInstallHash' => PluginInstaller::rest_get_plugin_install_hash(), + ) + ) . ';', + 'before' + ); + + wp_enqueue_script( 'nfd-installer-enqueue' ); + wp_enqueue_style( 'nfd-installer-enqueue' ); + } + } + + /** + * Listens for premium plugin activation using activated_plugin hook. + * + * @return void + */ + private function listen_for_premium_plugin_activation() { + $pls_utility = new PLSUtility(); + + // Retrieve the license data (decrypted) from the option + $license_data_store = $pls_utility->retrieve_license_storage_map(); + + if ( ! $license_data_store || empty( $license_data_store ) ) { + return; + } + + // Hook into activated_plugin action to trigger license activation after plugin activation + add_action( + 'activated_plugin', + function ( $plugin, $network_wide ) use ( $pls_utility, $license_data_store ) { + foreach ( $license_data_store as $plugin_slug => $license_data ) { + if ( isset( $license_data['basename'] ) && $license_data['basename'] === $plugin ) { + $pls_utility->activate_license( $plugin_slug ); + break; + } + } + }, + 10, + 2 + ); + } +} diff --git a/includes/WPAdmin/WPAdmin.php b/includes/WPAdmin/WPAdmin.php index 5fabd9b..8169d75 100644 --- a/includes/WPAdmin/WPAdmin.php +++ b/includes/WPAdmin/WPAdmin.php @@ -2,7 +2,7 @@ namespace NewfoldLabs\WP\Module\Installer\WPAdmin; -use NewfoldLabs\WP\Module\Installer\WPAdmin\Listeners\DataAttrListener; +use NewfoldLabs\WP\Module\Installer\WPAdmin\Listeners\InstallerListener; /** * Manages all the wp-admin related functionalities for the module. @@ -12,6 +12,6 @@ class WPAdmin { * Constructor for the WPAdmin class. */ public function __construct() { - new DataAttrListener(); + new InstallerListener(); } } diff --git a/includes/WPCLI/Handlers/InstallerCommandHandler.php b/includes/WPCLI/Handlers/InstallerCommandHandler.php index 3c21775..972283e 100644 --- a/includes/WPCLI/Handlers/InstallerCommandHandler.php +++ b/includes/WPCLI/Handlers/InstallerCommandHandler.php @@ -86,21 +86,24 @@ public function upgrade_extended_yith_plugin( $args ) { /** * Triggers the installation and activation of a premium plugin. * - * This command provisions a license, installs the premium plugin, and optionally activates it based on the - * activation parameter passed. It outputs the status of the process. + * This command provisions a license, installs the premium plugin, and optionally activates it + * based on the activation parameter passed. It outputs the status of the process. * * ## OPTIONS * * * : The slug of the premium plugin to be installed. * + * + * : The name of the provider for the premium plugin. + * * [--activate] * : Optional flag to activate the plugin after installation. * * ## EXAMPLES * - * wp installer install_premium_plugin --activate - * wp installer install_premium_plugin + * wp installer install_premium_plugin --activate + * wp installer install_premium_plugin * * @param array $args Arguments passed from the command line. First argument is the plugin slug. * @param array $assoc_args Associative arguments (like --activate). @@ -109,10 +112,17 @@ public function upgrade_extended_yith_plugin( $args ) { */ public function install_premium_plugin( $args, $assoc_args ) { $premium_slug = $args[0]; + $provider = $args[1]; $activate = isset( $assoc_args['activate'] ); + // Ensure both the plugin slug and provider are not empty + if ( empty( $premium_slug ) || empty( $provider ) ) { + WP_CLI::error( __( 'Both plugin slug and provider name are required.', 'wp-module-installer' ) ); + return; + } + // Call the function to provision, install, and (optionally) activate the premium plugin - $status = PluginInstaller::install_premium_plugin( $premium_slug, $activate ); + $status = PluginInstaller::install_premium_plugin( $premium_slug, $provider, $activate ); // Handle error or success response if ( is_wp_error( $status ) ) { diff --git a/package-lock.json b/package-lock.json index 1f1f2bb..cd10c58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,21 @@ { "name": "@newfold-labs/wp-module-installer", - "version": "1.0.0", + "version": "1.2.0-beta.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@newfold-labs/wp-module-installer", - "version": "1.0.0", + "version": "1.2.0-beta.7", "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/base-styles": "^4.49.0", + "@wordpress/element": "^6.9.0", + "@wordpress/icons": "^10.9.0" + }, "devDependencies": { - "@wordpress/scripts": "^26.10.0" + "@wordpress/scripts": "^26.19.0", + "webpack-merge": "^5.10.0" } }, "node_modules/@ampproject/remapping": { @@ -1911,7 +1917,6 @@ "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2678,13 +2683,14 @@ } }, "node_modules/@playwright/test": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.2.tgz", - "integrity": "sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.0.tgz", + "integrity": "sha512-W5lhqPUVPqhtc/ySvZI5Q8X2ztBOUgZ8LbAFy0JQgrXZs2xaILrUcNO3rQjwbLPfGK13+rZsDa1FpG+tqYkT5w==", "dev": true, + "license": "Apache-2.0", "peer": true, "dependencies": { - "playwright": "1.47.2" + "playwright": "1.48.0" }, "bin": { "playwright": "cli.js" @@ -3567,6 +3573,11 @@ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", "dev": true }, + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" + }, "node_modules/@types/qs": { "version": "6.9.16", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.16.tgz", @@ -3579,6 +3590,23 @@ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, + "node_modules/@types/react": { + "version": "18.3.11", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", + "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", + "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", @@ -4226,8 +4254,7 @@ "node_modules/@wordpress/base-styles": { "version": "4.49.0", "resolved": "https://registry.npmjs.org/@wordpress/base-styles/-/base-styles-4.49.0.tgz", - "integrity": "sha512-yFRYqNtd26ULZ0oAHhCu/IcaA0XHI3E7kRCKajZqUvyRQj7YprXnpD3o0/pnwvF6ZFTXzCX8pXHjUc2TIv97ig==", - "dev": true + "integrity": "sha512-yFRYqNtd26ULZ0oAHhCu/IcaA0XHI3E7kRCKajZqUvyRQj7YprXnpD3o0/pnwvF6ZFTXzCX8pXHjUc2TIv97ig==" }, "node_modules/@wordpress/browserslist-config": { "version": "5.41.0", @@ -4277,6 +4304,45 @@ "@playwright/test": ">=1" } }, + "node_modules/@wordpress/element": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-6.9.0.tgz", + "integrity": "sha512-G5W29cxfAVc/JQBzkKoXh1t4T+G3HWa1kIgXCqIZksonlYHzCVON1Or+rD/YJZSxT6RDkBVDzdl9p0pGOrccTg==", + "dependencies": { + "@babel/runtime": "^7.16.0", + "@types/react": "^18.2.79", + "@types/react-dom": "^18.2.25", + "@wordpress/escape-html": "^3.9.0", + "change-case": "^4.1.2", + "is-plain-object": "^5.0.0", + "react": "^18.3.0", + "react-dom": "^18.3.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/element/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@wordpress/escape-html": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.9.0.tgz", + "integrity": "sha512-+/SnVUXzzr+0pgfAqK3pocGveSDS3SHrgJ1BTgV7DA1l6y9mbjnqKgFQkNW/nzca92ZuYg2vjLcq1dqDGz4v1Q==", + "dependencies": { + "@babel/runtime": "^7.16.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, "node_modules/@wordpress/eslint-plugin": { "version": "17.13.0", "resolved": "https://registry.npmjs.org/@wordpress/eslint-plugin/-/eslint-plugin-17.13.0.tgz", @@ -4395,6 +4461,20 @@ "node": ">=12" } }, + "node_modules/@wordpress/icons": { + "version": "10.9.0", + "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-10.9.0.tgz", + "integrity": "sha512-mAkqhlbbPiuR6yBOczunqyxQ2Pez1XB7gAZnnsP5DlTKsYnJQ12N0Ql4Oh8f1LI+UeF18VMtHes12sWK/1LQHQ==", + "dependencies": { + "@babel/runtime": "^7.16.0", + "@wordpress/element": "^6.9.0", + "@wordpress/primitives": "^4.9.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, "node_modules/@wordpress/jest-console": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@wordpress/jest-console/-/jest-console-7.29.0.tgz", @@ -4481,6 +4561,23 @@ "prettier": ">=3" } }, + "node_modules/@wordpress/primitives": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.9.0.tgz", + "integrity": "sha512-vroiqxd+MP/K1+KEJqMAR/B/++4oShY4CisvMOK3gn75DmUV2QB6iQmBSjHRALqw9rqeHf7S0jLHWiFrAR+Dkg==", + "dependencies": { + "@babel/runtime": "^7.16.0", + "@wordpress/element": "^6.9.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, "node_modules/@wordpress/scripts": { "version": "26.19.0", "resolved": "https://registry.npmjs.org/@wordpress/scripts/-/scripts-26.19.0.tgz", @@ -5685,7 +5782,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" @@ -5765,7 +5861,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -5792,7 +5887,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", - "dev": true, "dependencies": { "camel-case": "^4.1.2", "capital-case": "^1.0.4", @@ -5965,6 +6059,14 @@ "node": ">=0.10.0" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -6146,7 +6248,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -6674,6 +6775,11 @@ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", "dev": true }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, "node_modules/cwd": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/cwd/-/cwd-0.10.0.tgz", @@ -7190,7 +7296,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -8603,17 +8708,18 @@ "dev": true }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dev": true, + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -8645,10 +8751,11 @@ } }, "node_modules/express/node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -9616,7 +9723,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", - "dev": true, "dependencies": { "capital-case": "^1.0.4", "tslib": "^2.0.3" @@ -11468,8 +11574,7 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "node_modules/js-yaml": { "version": "3.14.1", @@ -12119,7 +12224,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -12131,7 +12235,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, "dependencies": { "tslib": "^2.0.3" } @@ -12739,7 +12842,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" @@ -13361,7 +13463,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -13437,7 +13538,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -13447,7 +13547,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", - "dev": true, "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -13583,13 +13682,14 @@ } }, "node_modules/playwright": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz", - "integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.0.tgz", + "integrity": "sha512-qPqFaMEHuY/ug8o0uteYJSRfMGFikhUysk8ZvAtfKmUK3kc/6oNl/y3EczF8OFGYIi/Ex2HspMfzYArk6+XQSA==", "dev": true, + "license": "Apache-2.0", "peer": true, "dependencies": { - "playwright-core": "1.47.2" + "playwright-core": "1.48.0" }, "bin": { "playwright": "cli.js" @@ -13619,6 +13719,7 @@ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -13629,10 +13730,11 @@ } }, "node_modules/playwright/node_modules/playwright-core": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", - "integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.0.tgz", + "integrity": "sha512-RBvzjM9rdpP7UUFrQzRwR8L/xR4HyC1QXMzGYTbf1vjw25/ya9NRAVnXi/0fvFopjebvyPzsmoK58xxeEOaVvA==", "dev": true, + "license": "Apache-2.0", "peer": true, "bin": { "playwright-core": "cli.js" @@ -14746,7 +14848,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "dev": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -14758,8 +14859,6 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "dev": true, - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -14954,8 +15053,7 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, "node_modules/regenerator-transform": { "version": "0.15.2", @@ -15356,8 +15454,6 @@ "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "dev": true, - "peer": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -15507,7 +15603,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", - "dev": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -15804,7 +15899,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "dev": true, "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -17033,8 +17127,7 @@ "node_modules/tslib": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", - "dev": true + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -17186,10 +17279,11 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, + "license": "Apache-2.0", "peer": true, "bin": { "tsc": "bin/tsc", @@ -17340,7 +17434,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", - "dev": true, "dependencies": { "tslib": "^2.0.3" } @@ -17349,7 +17442,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", - "dev": true, "dependencies": { "tslib": "^2.0.3" } diff --git a/package.json b/package.json index 1b0b557..5996156 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "1.2.0-beta", + "version": "1.2.0-beta.7", "name": "@newfold-labs/wp-module-installer", "description": "An installer for WordPress plugins and themes.", "license": "GPL-2.0-or-later", @@ -8,11 +8,17 @@ "name": "Micah Wood", "email": "micah.wood@newfold.com" }, + "dependencies": { + "@wordpress/base-styles": "^4.49.0", + "@wordpress/element": "^6.9.0", + "@wordpress/icons": "^10.9.0" + }, "devDependencies": { - "@wordpress/scripts": "^26.10.0" + "@wordpress/scripts": "^26.19.0", + "webpack-merge": "^5.10.0" }, "scripts": { - "build": "wp-scripts build ./src/Scripts/dataAttrListener.js", - "start": "wp-scripts start ./src/Scripts/dataAttrListener.js" + "build": "wp-scripts build ./src/Installer/installer.js ./src/Scripts/dataAttrListener.js", + "start": "wp-scripts start ./src/Installer/installer.js ./src/Scripts/dataAttrListener.js" } } diff --git a/src/Installer/components/App/index.jsx b/src/Installer/components/App/index.jsx new file mode 100644 index 0000000..7dab6ba --- /dev/null +++ b/src/Installer/components/App/index.jsx @@ -0,0 +1,53 @@ +// External Imports +import { useState, useEffect } from '@wordpress/element'; + +// Internal Imports +import Modal from '../Modal'; + +const App = () => { + const [ action, setAction ] = useState(); + const [ statesReady, setStatesReady ] = useState( false ); + const [ pluginName, setPluginName ] = useState(); + const [ pluginDownloadUrl, setPluginDownloadUrl ] = useState(); + const [ pluginProvider, setPluginProvider ] = useState(); + const [ pluginSlug, setPluginSlug ] = useState(); + const [ redirectUrl, setRedirectUrl ] = useState(); + + const setData = ( e ) => { + setStatesReady( false ); + setAction( e.detail.action ); + setPluginName( e.detail.pluginName ); + setPluginDownloadUrl( e.detail.pluginDownloadUrl ); + setPluginProvider( e.detail.pluginProvider ); + setPluginSlug( e.detail.pluginSlug ); + setRedirectUrl( e.detail.redirectUrl ); + setStatesReady( true ); + }; + + useEffect( () => { + // Add an event listener to get the changes + window.addEventListener( 'installerParamsSet', setData ); + + // Cleanup the event listener + return () => { + window.removeEventListener( 'installerParamsSet', setData ); + }; + }, [] ); + + return ( +
+ { statesReady && ( + + ) } +
+ ); +}; + +export default App; diff --git a/src/Installer/components/Modal/index.jsx b/src/Installer/components/Modal/index.jsx new file mode 100644 index 0000000..cd8a722 --- /dev/null +++ b/src/Installer/components/Modal/index.jsx @@ -0,0 +1,234 @@ +// External Imports +import apiFetch from '@wordpress/api-fetch'; +import { __, sprintf } from '@wordpress/i18n'; +import { + createInterpolateElement, + useRef, + useState, + useEffect, +} from '@wordpress/element'; + +// Internal Imports +import { errorIcon, loadingInstaller } from '../../static/icons/index'; +import { + INSTALLER_DIV, + installerAPI, + pluginInstallHash, +} from '../../constants'; + +const Modal = ( { + action, + pluginDownloadUrl, + pluginName, + pluginProvider, + pluginSlug, + redirectUrl, +} ) => { + /** + * Represents the status of the plugin installation process. + * + * @typedef {('unknown'|'installing'|'failed'|'completed')} PluginStatus + * + * @property {'unknown'} unknown - The plugin installation has not started yet. + * @property {'installing'} installing - The plugin installation process has started. + * @property {'failed'} failed - The plugin installation process failed. + * @property {'completed'} completed - The plugin installation process is complete. + */ + const [ pluginStatus, setPluginStatus ] = useState( 'unknown' ); + const [ show, showModal ] = useState( true ); + const modalRef = useRef( null ); + + useEffect( () => { + document.getElementById( INSTALLER_DIV ).style.display = show + ? 'block' + : 'none'; + }, [ show ] ); + + useEffect( () => { + switch ( action ) { + case 'installFreePlugin': + installFreePlugin(); + break; + + case 'installPremiumPlugin': + installPremiumPlugin(); + break; + } + }, [ action ] ); + + const handleKeyDown = ( event ) => { + if ( event.key === 'Escape' ) { + showModal( false ); + } + }; + + const handleClickOutside = ( event ) => { + if ( modalRef.current && ! modalRef.current.contains( event.target ) ) { + showModal( false ); + } + }; + + useEffect( () => { + document.addEventListener( 'keydown', handleKeyDown ); + document.addEventListener( 'mousedown', handleClickOutside ); + + return () => { + document.removeEventListener( 'keydown', handleKeyDown ); + document.removeEventListener( 'mousedown', handleClickOutside ); + }; + }, [ pluginStatus ] ); + + // Function to handle premium plugin installation + const installDependantPlugins = async () => { + try { + // TODO: Change this logic to ensure we get dependent plugins as a prop + if ( pluginProvider === 'yith' ) { + await apiFetch( { + url: installerAPI, + method: 'POST', + headers: { + 'X-NFD-INSTALLER': pluginInstallHash, + }, + data: { + activate: true, + queue: false, + priority: 0, + plugin: 'woocommerce', + }, + } ); + } else if ( pluginProvider === 'yoast' ) { + // TODO: This will cause 2 calls to install the Yoast SEO Plugin. Remove this once we have dependent plugins as a prop. + await apiFetch( { + url: installerAPI, + method: 'POST', + headers: { + 'X-NFD-INSTALLER': pluginInstallHash, + }, + data: { + activate: true, + queue: false, + priority: 0, + plugin: 'wordpress-seo', + }, + } ); + } + } catch ( error ) { + throw error; + } + }; + + const installPremiumPlugin = async () => { + try { + setPluginStatus( 'installing' ); + await installDependantPlugins(); + await apiFetch( { + url: installerAPI, + method: 'POST', + headers: { + 'X-NFD-INSTALLER': pluginInstallHash, + }, + data: { + activate: true, + queue: false, + priority: 0, + premium: true, + plugin: pluginSlug, + provider: pluginProvider, + }, + } ); + setPluginStatus( 'completed' ); + showModal( false ); + window.location.href = redirectUrl; + } catch ( e ) { + setPluginStatus( 'failed' ); + } + }; + + const installFreePlugin = async () => { + try { + setPluginStatus( 'installing' ); + await installDependantPlugins(); + await apiFetch( { + url: installerAPI, + method: 'POST', + headers: { + 'X-NFD-INSTALLER': pluginInstallHash, + }, + data: { + activate: true, + queue: false, + priority: 0, + plugin: pluginDownloadUrl, + }, + } ); + setPluginStatus( 'completed' ); + showModal( false ); + window.location.href = redirectUrl; + } catch ( e ) { + setPluginStatus( 'failed' ); + } + }; + + const helpLink = `${ window.NewfoldRuntime.adminUrl }admin.php?page=${ window.NewfoldRuntime.plugin.brand }#/help`; + + const errorMessage = createInterpolateElement( + __( + 'Sorry, there was an error installing and activating the plugin. Please try again. If the problem persists, contact support.', + 'wp-module-onboarding' + ), + { + // eslint-disable-next-line jsx-a11y/anchor-has-content + a: showModal( false ) } />, + } + ); + + return ( +
+
+
+ { __( + 'Hold on while we get things setup for you!', + 'wp-module-installer' + ) } +
+
+ { + { pluginStatus === 'installing' && ( + <> +
+ { sprintf( + /* translators: %s: Plugin Name */ + __( + 'Activating the %s', + 'wp-module-onboarding' + ), + pluginName + ) } +
+
+ + ) } + { pluginStatus === 'failed' && ( +
+ { +
{ errorMessage }
+
+ ) } +
+
+
+ ); +}; + +export default Modal; diff --git a/src/Installer/components/Modal/stylesheet.scss b/src/Installer/components/Modal/stylesheet.scss new file mode 100644 index 0000000..cdc9bb4 --- /dev/null +++ b/src/Installer/components/Modal/stylesheet.scss @@ -0,0 +1,126 @@ +.nfd-installer-modal { + top: 0; + left: 0; + z-index: 99; + width: 100vw; + height: 100vh; + display: flex; + position: fixed; + align-items: center; + justify-content: center; + background: rgba(255, 255, 255, 0.5); + + @media (max-width: #{ ($break-small) }) { + top: 30px; + } + + &__content { + width: 40vw; + padding: 72px; + position: relative; + border-radius: 8px; + text-align: center; + background-color: #fff; + box-shadow: 5px 5px 10px rgba(0, 0, 0, 0.3); + + @media (max-width: #{ ($break-small) }) { + width: 80vw; + padding: 24px; + } + + &-heading { + color: #333; + font-size: 22px; + font-weight: 300; + margin-bottom: 60px; + letter-spacing: 1.1px; + + @media (max-width: #{ ($break-small) }) { + font-size: 18px; + margin-bottom: 20px; + } + } + + &-section { + display: flex; + flex-direction: column; + align-items: center; + } + + &-image { + width: 200px; + margin-bottom: 30px; + + @media (max-width: #{ ($break-small) }) { + width: 100px; + } + } + + &-subheading { + color: #333; + font-size: 16px; + font-weight: 300; + margin-bottom: 30px; + + @media (max-width: #{ ($break-small) }) { + font-size: 14px; + margin-bottom: 10px; + } + } + + &-error { + display: flex; + font-size: 16px; + flex-direction: row; + align-items: center; + + @media (max-width: #{ ($break-small) }) { + font-size: 14px; + } + + &--icon { + margin-right: 5px; + flex-shrink: 0; + } + + &--text { + font-weight: 300; + line-height: 20px; + + &-link { + all: unset; + cursor: pointer; + color: #1e90ff; + font-weight: 300; + } + } + } + } + + &__loader { + width: 30px; + height: 30px; + border-radius: 50%; + border: 4px solid #f3f3f3; + border-top: 4px solid #3a3a3a; + border-right: 4px solid #3a3a3a; + border-bottom: 4px solid #3a3a3a; + animation: spin 1s linear infinite; + + @media (max-width: #{ ($break-small) }) { + width: 20px; + height: 20px; + } + } +} + +@keyframes spin { + + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} diff --git a/src/constants.js b/src/Installer/constants.js similarity index 85% rename from src/constants.js rename to src/Installer/constants.js index d46b3a6..7b72cf8 100644 --- a/src/constants.js +++ b/src/Installer/constants.js @@ -1,3 +1,4 @@ +export const INSTALLER_DIV = 'nfd-installer'; export const wpRestURL = window.nfdInstaller?.restUrl; export const installerRestRoute = 'newfold-installer/v1'; export const pluginInstallHash = window.nfdInstaller?.pluginInstallHash; diff --git a/src/Installer/installer.js b/src/Installer/installer.js new file mode 100644 index 0000000..cdb882a --- /dev/null +++ b/src/Installer/installer.js @@ -0,0 +1,39 @@ +/** + * Styles. + */ +import './styles/app.scss'; + +/** + * WordPress dependencies + */ +import domReady from '@wordpress/dom-ready'; +import { render } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { INSTALLER_DIV } from './constants'; + +// eslint-disable-next-line import/no-unresolved +import App from './components/App'; + +domReady( () => { + renderModal( INSTALLER_DIV ); +} ); + +/** + * This function creates a modal that is rendered on the page. + * + * @param {string} elementId It takes an elementId as an argument and creates a div with the given elementId. + */ +const renderModal = ( elementId ) => { + const modalRoot = document.createElement( 'div' ); + modalRoot.id = elementId; + + // Append the modal container to the body if it hasn't been added already. + if ( ! document.getElementById( elementId ) ) { + document.body.append( modalRoot ); + } + + render( , modalRoot ); +}; diff --git a/src/Installer/static/icons/error_icon.svg b/src/Installer/static/icons/error_icon.svg new file mode 100644 index 0000000..bd30f14 --- /dev/null +++ b/src/Installer/static/icons/error_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/Installer/static/icons/index.js b/src/Installer/static/icons/index.js new file mode 100644 index 0000000..5210ff7 --- /dev/null +++ b/src/Installer/static/icons/index.js @@ -0,0 +1,2 @@ +export { default as errorIcon } from './error_icon.svg'; +export { default as loadingInstaller } from './loading_installer.svg'; diff --git a/src/Installer/static/icons/loading_installer.svg b/src/Installer/static/icons/loading_installer.svg new file mode 100644 index 0000000..0a16af3 --- /dev/null +++ b/src/Installer/static/icons/loading_installer.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Installer/styles/_wordpress.scss b/src/Installer/styles/_wordpress.scss new file mode 100644 index 0000000..e576c59 --- /dev/null +++ b/src/Installer/styles/_wordpress.scss @@ -0,0 +1,7 @@ +/** + * Initialize all WordPress Sass + * ----------------------------- + * @import @wordpress/base-styles + */ + +@import "@wordpress/base-styles/breakpoints"; diff --git a/src/Installer/styles/app.scss b/src/Installer/styles/app.scss new file mode 100644 index 0000000..485cee7 --- /dev/null +++ b/src/Installer/styles/app.scss @@ -0,0 +1,18 @@ +//Imports +@import "wordpress"; +@import "../components/Modal/stylesheet"; + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: "Open Sans", sans-serif; +} + +#nfd-installer { + display: none; + transition: all 3s ease-in-out; +} diff --git a/src/Scripts/dataAttrListener.js b/src/Scripts/dataAttrListener.js index 1cbcff8..754a037 100644 --- a/src/Scripts/dataAttrListener.js +++ b/src/Scripts/dataAttrListener.js @@ -1,58 +1,70 @@ +// External Imports import domReady from '@wordpress/dom-ready'; -import apiFetch from '@wordpress/api-fetch'; - -import { pluginInstallHash, installerAPI } from '../constants'; domReady( () => { - const domObserver = new window.MutationObserver( ( mutationList ) => { - for ( const mutation of mutationList ) { - if ( mutation.type === 'childList' ) { - for ( const addedNode of mutation.addedNodes ) { - if ( - typeof addedNode === 'object' && - typeof addedNode.querySelectorAll === 'function' - ) { - addedNode - .querySelectorAll( - '[data-nfd-installer-plugin-provider]' - ) - .forEach( ( ele ) => { - ele.addEventListener( 'click', function ( e ) { - if ( - e.target.getAttribute( - 'data-nfd-installer-plugin-slug' - ) !== null - ) { - apiFetch( { - url: installerAPI, - method: 'POST', - headers: { - 'X-NFD-INSTALLER': - pluginInstallHash, - }, - data: { - plugin: this.getAttribute( - 'data-nfd-installer-plugin-slug' - ), - activate: - this.getAttribute( - 'data-nfd-installer-plugin-activate' - ) === 'true' - ? true - : false, - queue: false, - priority: 0, - premium: true, - }, - } ); - } - } ); - } ); - } - } + function dispatchEvent( detail ) { + window.dispatchEvent( + new CustomEvent( 'installerParamsSet', { detail } ) + ); + } + + document.body.addEventListener( 'click', ( e ) => { + const el = e.target; + + if ( el.hasAttribute( 'data-nfd-installer-plugin-name' ) ) { + // Don't follow the existing link + e.preventDefault(); + + // URL to redirect to after install + const redirectUrl = + el.getAttribute( 'href' ) || + el.getAttribute( 'data-nfd-installer-plugin-url' ); + + // Is free plugin + if ( el.hasAttribute( 'data-nfd-installer-download-url' ) ) { + dispatchEvent( { + action: 'installFreePlugin', + pluginName: el.getAttribute( + 'data-nfd-installer-plugin-name' + ), + pluginDownloadUrl: el.getAttribute( + 'data-nfd-installer-download-url' + ), + pluginProvider: el.getAttribute( + 'data-nfd-installer-pls-provider' + ), + redirectUrl, + } ); + return false; + } + + // Is premium plugin + if ( + el.hasAttribute( 'data-nfd-installer-pls-slug' ) && + el.hasAttribute( 'data-nfd-installer-pls-provider' ) + ) { + dispatchEvent( { + action: 'installPremiumPlugin', + pluginName: el.getAttribute( + 'data-nfd-installer-plugin-name' + ), + pluginSlug: el.getAttribute( + 'data-nfd-installer-pls-slug' + ), + pluginProvider: el.getAttribute( + 'data-nfd-installer-pls-provider' + ), + redirectUrl, + } ); + return false; + } + + // TODO: Handle use cases for theme installs + + // Redirect to the URL provided by the data attribute + if ( redirectUrl ) { + window.location.href = redirectUrl; } } } ); - - domObserver.observe( document.body, { childList: true, subtree: true } ); } );